Tips10. マテビューの効果的な使い方。

マテビューとは?!

 正しくは「マテリアライズド・ビュー(MATERIALIZED VIEW)」と呼びます。
 通常のビュー(VIEW)というオブジェクトは、実体を持たず、SQLの定義があるだけですが、マテリアライズド・ビューは、作成すると定義文中のSQLが実行され、結果がテーブルに保持されます。

 それではこのマテリアライズド・ビュー、どのように使用するのが効果的でしょうか。

※以下、マテリアライズド・ビューはMVと略させて頂きます。

ミニバッチ、中間テーブルとしての活用

 最も単純に思いつくのが簡単なバッチプログラムの代替としての使用です。
 テーブルemp, deptを結合して、テーブルXに保存するといった、単一のSQLで記述し切れて、データの内容も安定しているので複雑な例外処理を考慮する必要もあまりない場合です。
 MVであれば、

CREATE MATERIALIZED VIEW emp_mv
AS
SELECT e.empno
      ,e.deptno
      ,d.dname
FROM   emp e
       INNER JOIN dept d
       ON (e.deptno = d.deptno);

という記述を行い、

DBMS_MVIEW.REFRESH('emp_mv', 'c');

とSQLを発行すると、データが最新の状態に更新されます。
上記はあまりにも簡単なSQLですが、1つのSQLで記述しきれる内容でしたらバッチプログラムを作るまでもなく、MVで代替することができます。

実際の利用例としては他にも、複数のマスタテーブルを結合した複合マスタテーブルを作成、バッチプログラムで性能要求を満たすための中間テーブルの代替といったことが考えられます。

ただし、このときパラメータを指定した抽出条件は指定できませんので、注意が必要です。
トランザクションテーブルに対して同様の処理を行う場合は、パラメータを計算式で導出できるようにしておく必要があります。

そのパラメータが今日を表す日付の場合は、「Tips2. SYSDATEは使わない!」で記述したように、その日付を取得する関数を作成し、抽出条件として適用するといった方法を考える必要があります。

高速マテビュー(差分MV)の使用には注意!

 MVを使うようになると、気になってくるのがリフレッシュにかかる時間です。
 もっと高速にリフレッシュできないのか。
 そう思ってマニュアルを見ると、すぐ目に入ってくるのが「高速リフレッシュ」の文字です。
 DBMS_MVIEW.REFRESHの実行時に「F」を指定すると高速リフレッシュできるらしい、と思いがちですが、これが深いワナの入り口です。

 高速リフレッシュは、リフレッシュ処理そのものを高速化する魔法のような仕組みではありません。

 この仕組みは、リフレッシュ時ではなく、MVに含まれるテーブルに対してINSERT/UPDATE/DELETEが発行された時点で、トリガーが動き、マテリアライズド・ビュー・ログと呼ばれる差分テーブルのようなものに変更内容を登録することで、予めリフレッシュ状態を作成しておくというものです。

このような仕組みを実現するためには様々な制約が発生します。

まずは前述の通り、MVログというオブジェクトを定義・作成する必要があります。
これはテーブルに付随するオブジェクトになります。
そのためには、MVを構成する元テーブルを全て把握する必要があります。
多重ネストしたSQLから構成されるMVの場合、そのビューの構成元となる全てのテーブルを把握し、全てのテーブルに対してMVログを作成する必要があります。

他にも

  • CONNECT BYを使用していないこと
  • MINUS、もしくは、UNION、UNION ALLを使用していないこと
  • DISTINCT句を使用していないこと
  • 集計関数(SUM, AVG, COUNT等)を使用していないこと

といった制約があります(上記の場合でも高速リフレッシュ可能な場合もあります)。

MVの制約に関する詳細は、
Oracle® Databaseアドバンスト・レプリケーション 11g リリース2 B72089-02 3 マテリアライズド・ビューの概要とアーキテクチャ
複合マテリアライズド・ビュー
https://docs.oracle.com/cd/E16338_01/server.112/b72089/repmview.htm#BABEEHGJ

を参照ください。

とにかく、非常に複雑な制約がありますので、安易な使用は避けるべきです。
単純SQLで更新頻度の高いテーブルが1つに特定できているような、シンプルなケースに限定して使用するようにしておいた方が良いでしょう。
特に、開発初期の時点では制約に該当しなくても、仕様変更が行われるうち、または保守・運用を経ていくうちに、制約に該当してしまうかもしれません。

まずは、高速リフレッシュを使用しない設計を心掛けた方が良いでしょう。
それでも尚、どうしても高速リフレッシュが必要な場合は、十分な検証を行った上で高速リフレッシュを使用しましょう。

様々なモードの整理

 さて、ここまでいくつかのMVのモードが登場しました。
 混乱しがちですので、整理しましょう。

リフレッシュ方法

NAME名称動作
COMPLETE完全リフレッシュクエリを再実行して全件再更新。デフォルト
FAST高速リフレッシュMVログを参照し変更箇所のみ更新
FORCE強制リフレッシュ高速リフレッシュ可能な場合は高速、
そうでなければ完全リフレッシュを使用

例)

-- 完全リフレッシュ
CREATE MATERIALIZED VIEW emp_mv
REFRESH COMPLETE AS SELECT empno FROM emp;
  
-- 高速リフレッシュは事前にMVログの作成が必要
CREATE MATERIALIZED VIEW LOG ON emp;
CREATE MATERIALIZED VIEW LOG ON dept;
-- 高速リフレッシュでMVを作成
CREATE MATERIALIZED VIEW emp_mv
REFRESH FAST AS SELECT empno FROM emp;
  
-- FORCEモードで作成
CREATE MATERIALIZED VIEW emp_mv
REFRESH FORCE AS SELECT empno FROM emp;

リフレッシュタイミング

名称タイミング
ON DEMANDDBMS_MVIEW.REFRESHコマンドを発行
もしくはスケジュールによるリフレッシュ。デフォルト
ON COMMITMVの構成元テーブルに対するコミット時、自動リフレッシュ
START WITH/NEXTリフレッシュのタイミングを時間の指定で行います。
START WITH句で最初のリフレッシュ時刻、NEXT句でリフレッシュの間隔を指定します。

例)

CREATE MATERIALIZED VIEW emp_mv_demand
REFRESH ON DEMAND AS SELECT empno FROM emp;
  
CREATE MATERIALIZED VIEW emp_mv_commit
REFRESH ON COMMIT AS SELECT empno FROM emp;
  
CREATE MATERIALIZED VIEW emp_mv_time
REFRESH START WITH SYSDATE NEXT TRUNC(SYSDATE)+7
AS SELECT empno FROM emp;

リフレッシュオプション「atomic_refresh」

 dbms_mview.refreshによるMVのリフレッシュ時、"atomic_refresh"パラメータがFALSEの場合、MVテーブルがTRUNCATEで削除されます。
 削除処理は高速ですが、リフレッシュが1トランザクション中で行われないため、一瞬データが0件になる瞬間が発生します。
 オンライン処理等で不定期に参照されるMVに対して、このオプションは使用しない方が良いでしょう。
 夜間バッチのような、アクセスタイミングが予測でき、性能が要求される場面でのみ使用するのが望ましいです。

 パラメータがTRUEの場合(こちらがデフォルト)は、DELETE~INSERTが1トランザクションで行われるため、データの状態は0件になることなく瞬時に切り替わります。

参考サイト

Oracle® Database SQL言語リファレンス 11gリリース2 (11.2)
B56299-08
「CREATE MATERIALIZED VIEW」

Oracle® Databaseアドバンスト・レプリケーション 11g リリース2 (11.2)
B72089-02
3 マテリアライズド・ビューの概要とアーキテクチャ
「マテリアライズド・ビューの高速リフレッシュ機能に関する判断」

Oracle® Database PL/SQLパッケージおよびタイプ・リファレンス 11g リリース2(11.2)
B56262-06
DBMS_MVIEW