【Oracle】マテリアライズドビューの作成・管理・リフレッシュ完全ガイド|FAST/COMPLETE・クエリリライト・DBMS_MVIEWまで解説

【Oracle】マテリアライズドビューの作成・管理・リフレッシュ完全ガイド|FAST/COMPLETE・クエリリライト・DBMS_MVIEWまで解説 Oracle

集計やJOINを含むSQLが遅い、レポート画面の応答が改善できないといった場面で威力を発揮するのがマテリアライズドビュー(Materialized View)です。通常のビューがSELECTのたびにクエリを実行するのに対し、マテリアライズドビューは結果を物理的に保存するため、参照コストを大幅に削減できます。本記事では作成・リフレッシュ・クエリリライト・管理SQLまで体系的に解説します。

この記事でわかること

  • マテリアライズドビューと通常ビューの違い
  • CREATE MATERIALIZED VIEW の基本構文とオプション
  • COMPLETE / FAST / FORCE / NEVER の各リフレッシュモード
  • FAST リフレッシュに必要なマテリアライズドビューログの作成
  • 自動リフレッシュのスケジュール設定(ON COMMIT / START WITH … NEXT)
  • DBMS_MVIEW.REFRESH による手動リフレッシュ
  • クエリリライト(QUERY REWRITE)の仕組みと有効化
  • DBA_MVIEWS / USER_MVIEWS での情報確認
スポンサーリンク

マテリアライズドビューとは

マテリアライズドビュー(以降MV)は、あらかじめ定義したSELECT文の結果をデータベースに物理的に保存したオブジェクトです。通常のビューとの主な違いは次のとおりです。

比較項目 通常ビュー(VIEW) マテリアライズドビュー(MV)
データの保存 保存しない(毎回実行) 物理的に保存する
参照速度 ベーステーブルの複雑さに依存 高速(集計済みデータを返す)
データの鮮度 常に最新 リフレッシュするまで古い可能性あり
ストレージ使用 なし 行数・列数に比例して消費
主な用途 論理的なデータ抽象化 集計キャッシュ・レポート高速化

MVはDWH(データウェアハウス)やレポートシステムで特に効果的です。集計・JOIN・サブクエリを含む重いSQLをMVとして定義しておき、アプリケーションからはMVを参照するだけで高速な結果が得られます。

基本構文:CREATE MATERIALIZED VIEW

CREATE MATERIALIZED VIEW ── 最もシンプルな例
CREATE MATERIALIZED VIEW mv_dept_sales AS
SELECT
    dept_id,
    COUNT(*)       AS order_count,
    SUM(amount)    AS total_sales
FROM   orders
GROUP BY dept_id;

これだけでマテリアライズドビューが作成され、SELECTの結果がディスクに保存されます。以降は SELECT * FROM mv_dept_sales で集計済みの高速な結果を取得できます。

オプションを含む完全な構文は次のとおりです。

CREATE MATERIALIZED VIEW ── 全オプション構文
CREATE MATERIALIZED VIEW マテリアライズドビュー名
    [BUILD {IMMEDIATE | DEFERRED}]          -- 作成時のデータ投入タイミング
    [REFRESH [FAST | COMPLETE | FORCE | NEVER]
             [ON {DEMAND | COMMIT}]
             [START WITH 日時]
             [NEXT 日時]]                   -- リフレッシュ設定
    [{ENABLE | DISABLE} QUERY REWRITE]      -- クエリリライトの有効/無効
AS
SELECT文;

BUILD オプション:作成時のデータ投入タイミング

オプション 説明
BUILD IMMEDIATE(デフォルト) 作成と同時にSELECTを実行してデータを格納する
BUILD DEFERRED MVの定義だけ作成してデータは格納しない。最初のリフレッシュ時にデータが入る

初回の集計に時間がかかる場合は BUILD DEFERRED を使い、業務時間外のジョブで初回リフレッシュするのが安全です。

リフレッシュモード

リフレッシュとは、ベーステーブルの変更をMVに反映する処理です。モードによって動作と前提条件が異なります。

モード 動作 速度 前提条件
COMPLETE MVを全削除して再構築(全件リフレッシュ) 遅い なし
FAST 前回から差分のみを反映 速い マテリアライズドビューログが必要
FORCE 可能ならFAST、できなければCOMPLETE(デフォルト) 中間 なし(FASが使える場合はログ必要)
NEVER 自動・手動ともにリフレッシュしない なし

COMPLETE リフレッシュ

最もシンプルで、どんなSELECT文のMVにも使えます。ただし全データを削除して再構築するため、大規模なMVでは時間がかかります。

COMPLETE リフレッシュのMV作成例
CREATE MATERIALIZED VIEW mv_monthly_sales
    BUILD IMMEDIATE
    REFRESH COMPLETE ON DEMAND
AS
SELECT
    TO_CHAR(order_date, 'YYYYMM') AS ym,
    product_id,
    SUM(quantity)                  AS total_qty,
    SUM(amount)                    AS total_amount
FROM   orders
GROUP BY TO_CHAR(order_date, 'YYYYMM'), product_id;

FAST リフレッシュとマテリアライズドビューログ

FAST リフレッシュを使うには、ベーステーブルにマテリアライズドビューログを作成する必要があります。これはベーステーブルの変更(INSERT/UPDATE/DELETE)を記録するログテーブルです。

マテリアライズドビューログの作成
-- ベーステーブルにMVログを作成
CREATE MATERIALIZED VIEW LOG ON orders
    WITH PRIMARY KEY, ROWID, SEQUENCE
    INCLUDING NEW VALUES;

-- MVログが作成されたことを確認
SELECT log_table, primary_key, rowids, sequence
FROM   user_mview_logs
WHERE  master = 'ORDERS';

MVログを作成したあと、FAST リフレッシュのMVを作成します。

FAST リフレッシュのMV作成例
CREATE MATERIALIZED VIEW mv_orders_fast
    BUILD IMMEDIATE
    REFRESH FAST ON DEMAND
AS
SELECT
    order_id,
    customer_id,
    product_id,
    quantity,
    amount,
    order_date
FROM   orders;
FAST リフレッシュの制約
FASTリフレッシュはすべてのSELECT文に使えるわけではありません。集計(GROUP BY / SUM / COUNT)を含む場合は追加の条件があり、JOINの組み合わせによっては利用不可のケースもあります。FAST リフレッシュが使えるかどうかは後述の DBMS_MVIEW.EXPLAIN_MVIEW で確認できます。

リフレッシュのタイミング設定

ON DEMAND(手動リフレッシュ)

デフォルトの設定です。明示的に DBMS_MVIEW.REFRESH を呼び出したときだけリフレッシュします。

ON COMMIT(コミット時に自動リフレッシュ)

ベーステーブルへのDMLがCOMMITされるたびに自動でリフレッシュします。データの鮮度は最高ですが、COMMITのたびにリフレッシュ処理が走るためオンライントランザクションへの影響を考慮する必要があります。FASTリフレッシュ可能なMVに限定して使うのが一般的です。

ON COMMIT を使ったMV
CREATE MATERIALIZED VIEW mv_order_summary
    BUILD IMMEDIATE
    REFRESH FAST ON COMMIT
AS
SELECT
    customer_id,
    COUNT(*)    AS order_cnt,
    SUM(amount) AS total_amt
FROM   orders
GROUP BY customer_id;

スケジュールによる定期リフレッシュ(START WITH / NEXT)

業務時間外に定期的にリフレッシュしたい場合は START WITHNEXT でスケジュールを設定します。

定期リフレッシュ ── 毎日深夜2時に実行
CREATE MATERIALIZED VIEW mv_daily_report
    BUILD IMMEDIATE
    REFRESH COMPLETE
    START WITH TRUNC(SYSDATE + 1) + 2/24   -- 翌日の 02:00
    NEXT  TRUNC(SYSDATE + 1) + 2/24        -- 以降も毎日 02:00
AS
SELECT
    dept_id,
    TO_CHAR(order_date, 'YYYYMMDD') AS order_day,
    SUM(amount) AS daily_sales
FROM   orders
GROUP BY dept_id, TO_CHAR(order_date, 'YYYYMMDD');
スケジュール指定の計算例

  • 翌日 02:00 → TRUNC(SYSDATE + 1) + 2/24
  • 1時間後 → SYSDATE + 1/24
  • 毎週月曜 00:00 → NEXT_DAY(TRUNC(SYSDATE), 'MONDAY')
  • 毎月1日 03:00 → TRUNC(LAST_DAY(SYSDATE) + 1) + 3/24

手動リフレッシュ:DBMS_MVIEW.REFRESH

ON DEMAND に設定したMVは DBMS_MVIEW.REFRESH プロシージャで手動リフレッシュします。

DBMS_MVIEW.REFRESH ── 単体リフレッシュ
-- 単一のMVをCOMPLETEリフレッシュ
EXEC DBMS_MVIEW.REFRESH('MV_DEPT_SALES', 'C');   -- 'C'=COMPLETE

-- FASTリフレッシュ
EXEC DBMS_MVIEW.REFRESH('MV_DEPT_SALES', 'F');   -- 'F'=FAST

-- FORCE(FAST が使えなければ COMPLETE)
EXEC DBMS_MVIEW.REFRESH('MV_DEPT_SALES', '?');   -- '?'=FORCE
DBMS_MVIEW.REFRESH_ALL_MVIEWS ── 全MVを一括リフレッシュ
-- スキーマ内の全MVをリフレッシュ(アウトオブプレース)
EXEC DBMS_MVIEW.REFRESH_ALL_MVIEWS(:errors);

-- 特定のMVリストをまとめてリフレッシュ
EXEC DBMS_MVIEW.REFRESH(
    LIST    => 'MV_DEPT_SALES,MV_MONTHLY_SALES',
    METHOD  => 'C',    -- COMPLETE
    ATOMIC_REFRESH => FALSE   -- 並列リフレッシュを許可
);

ATOMIC_REFRESH => FALSE を指定すると、複数のMVを並列でリフレッシュできます。TRUEの場合は全MVを1つのトランザクションでリフレッシュするためより安全ですが、時間がかかります。

クエリリライト(QUERY REWRITE)

クエリリライトは、ベーステーブルへのSELECT文をオプティマイザが自動的にMVへの参照に書き換える機能です。アプリケーションのSQLを変更しなくても、透過的にMVを活用して高速化できます。

クエリリライトを有効にしたMVの作成
-- QUERY REWRITE を有効化(ENABLE QUERY REWRITE句を指定)
CREATE MATERIALIZED VIEW mv_dept_monthly
    BUILD IMMEDIATE
    REFRESH COMPLETE ON DEMAND
    ENABLE QUERY REWRITE
AS
SELECT
    dept_id,
    TO_CHAR(order_date, 'YYYYMM') AS ym,
    SUM(amount) AS total_amount,
    COUNT(*)    AS order_cnt
FROM   orders
GROUP BY dept_id, TO_CHAR(order_date, 'YYYYMM');
クエリリライトを使うためのセッション設定
-- クエリリライトを有効にする(デフォルト有効だが確認・明示設定)
ALTER SESSION SET QUERY_REWRITE_ENABLED = TRUE;

-- クエリリライトの整合性チェックレベル
-- TRUSTED: ディメンションの制約を信頼して積極的にリライト
-- ENFORCED: 制約で保証された場合のみリライト(デフォルト)
ALTER SESSION SET QUERY_REWRITE_INTEGRITY = TRUSTED;

-- ベーステーブルへのSELECT(オプティマイザがMVを使うかもしれない)
SELECT dept_id, TO_CHAR(order_date, 'YYYYMM'), SUM(amount)
FROM   orders
GROUP BY dept_id, TO_CHAR(order_date, 'YYYYMM');

クエリリライトが実際に使われているかどうかは実行計画で確認できます。詳しくは【Oracle】SQLの実行計画を確認する方法|EXPLAIN PLANとAUTOTRACEの違いを参照してください。

クエリリライトが使われない主な理由

  • QUERY_REWRITE_ENABLED が FALSE になっている
  • MV の SELECT 文がクエリと完全に一致しない(集計列の違いなど)
  • MV がリフレッシュされておらず古いデータになっている
  • QUERY_REWRITE 権限がない(非DBAユーザーの場合)

マテリアライズドビューの情報確認

作成済みMVの一覧と状態確認

USER_MVIEWS ── 自スキーマのMV一覧
SELECT
    mview_name,
    refresh_mode,         -- DEMAND / COMMIT
    refresh_method,       -- COMPLETE / FAST / FORCE
    last_refresh_date,    -- 最終リフレッシュ日時
    staleness,            -- FRESH / STALE / UNKNOWN
    query_rewrite_enabled -- Y / N
FROM   user_mviews
ORDER BY mview_name;

STALENESS 列に注目します。

STALENESSの値 意味
FRESH ベーステーブルと同期済み(最新)
STALE ベーステーブルが変更されたがMVはまだ古い
UNKNOWN 同期状態が不明(ON COMMIT ではない場合など)
DBA_MVIEWS ── 全スキーマのMV一覧(DBA権限が必要)
SELECT
    owner,
    mview_name,
    refresh_mode,
    refresh_method,
    last_refresh_date,
    staleness,
    query                 -- 定義クエリ(最大4000文字)
FROM   dba_mviews
WHERE  owner = 'MYSCHEMA'
ORDER BY mview_name;

MVログの確認

USER_MVIEW_LOGS ── MVログ一覧
SELECT
    log_table,            -- ログテーブル名(MLOG$_テーブル名)
    master,               -- ベーステーブル名
    log_creator,          -- 作成者
    primary_key,          -- プライマリキーを記録するか
    rowids,               -- ROWIDを記録するか
    sequence              -- SEQUENCEを記録するか
FROM   user_mview_logs;

FASTリフレッシュが可能か確認する

DBMS_MVIEW.EXPLAIN_MVIEW ── FASTリフレッシュ可否チェック
-- 結果格納テーブルを作成(初回のみ)
-- @?/rdbms/admin/utlxmv.sql でも作成可能
CREATE TABLE mv_capabilities_table (
    statement_id    VARCHAR2(30),
    mvowner         VARCHAR2(30),
    mvname          VARCHAR2(30),
    capability_name VARCHAR2(30),
    possible        CHAR(1),
    related_text    VARCHAR2(2000),
    related_num     NUMBER,
    msgno           NUMBER,
    msgtxt          VARCHAR2(2000),
    seq             NUMBER
);

-- 確認対象のMVを分析
EXEC DBMS_MVIEW.EXPLAIN_MVIEW('SELECT dept_id, SUM(amount) FROM orders GROUP BY dept_id', 'MV_TEST');

-- 結果確認(FASTリフレッシュが可能かどうか)
SELECT capability_name, possible, msgtxt
FROM   mv_capabilities_table
WHERE  capability_name LIKE '%REFRESH%'
ORDER BY seq;

インデックスの追加でMVをさらに高速化

MVはテーブルと同じようにインデックスを追加できます。検索条件に使われるカラムにインデックスを付けると、MVへの参照がさらに速くなります。

MV へのインデックス追加
-- 部署IDと年月の組み合わせで検索されることが多い場合
CREATE INDEX idx_mv_dept_monthly_01
    ON mv_dept_monthly (dept_id, ym);

-- インデックス情報の確認
SELECT index_name, column_name, column_position
FROM   user_ind_columns
WHERE  table_name = 'MV_DEPT_MONTHLY'
ORDER BY index_name, column_position;

インデックスの確認方法全般については【Oracle】インデックス情報を取得する方法も参照してください。

マテリアライズドビューの変更・削除

ALTER / DROP MATERIALIZED VIEW
-- リフレッシュモードを変更(COMPLETEに変更)
ALTER MATERIALIZED VIEW mv_dept_sales
    REFRESH COMPLETE;

-- スケジュールを変更(毎日03:00に変更)
ALTER MATERIALIZED VIEW mv_daily_report
    REFRESH NEXT TRUNC(SYSDATE + 1) + 3/24;

-- クエリリライトを無効化
ALTER MATERIALIZED VIEW mv_dept_monthly
    DISABLE QUERY REWRITE;

-- MVを削除(MVのデータも一緒に削除される)
DROP MATERIALIZED VIEW mv_dept_sales;

-- MVログを削除(必要なければ削除)
DROP MATERIALIZED VIEW LOG ON orders;

よくあるトラブルと対処

ORA-23413: テーブルにマテリアライズドビュー・ログがありません

FAST リフレッシュのMVを作成しようとしたが、ベーステーブルにMVログが存在しない場合に発生します。先にベーステーブルで CREATE MATERIALIZED VIEW LOG ON テーブル名 を実行してください。

ORA-12054: ON COMMITリフレッシュ属性は設定できません

集計を含むMVに ON COMMIT を設定しようとした場合など、FASTリフレッシュの条件を満たさないMVにON COMMITを指定すると発生します。DBMS_MVIEW.EXPLAIN_MVIEW でFAST ON COMMITが可能かどうかを先に確認してください。

リフレッシュに時間がかかる

COMPLETE リフレッシュは全件再構築するため大規模MVでは時間がかかります。対処法としては次が有効です。

  • FAST リフレッシュに切り替える(MVログの作成が必要)
  • ATOMIC_REFRESH => FALSE で並列リフレッシュを有効にする
  • リフレッシュを業務時間外のスケジュールにする
  • 不要なMVを整理してリフレッシュ対象を減らす

MVがSTALEのままクエリリライトが使われない

QUERY_REWRITE_INTEGRITY = ENFORCED(デフォルト)の場合、STALEなMVはクエリリライトに使用されません。即時リフレッシュできない場合は QUERY_REWRITE_INTEGRITY = STALE_TOLERATED に設定するとSTALEでもリライトに使いますが、データの整合性は保証されません。本番環境での使用は用途に応じて慎重に判断してください。

実務での使いどころ

用途 リフレッシュの推奨設定
バッチ後の集計レポート COMPLETE / ON DEMAND(バッチ後に手動リフレッシュ)
リアルタイム性が必要な集計 FAST / ON COMMIT(更新頻度が低いテーブル向け)
日次レポートの事前計算 COMPLETE / 定期スケジュール(毎日深夜)
読み取り専用DWH COMPLETE / NEVER(ETL後に手動で一度だけリフレッシュ)

まとめ

マテリアライズドビューは、重い集計・JOINクエリのパフォーマンスを大幅に改善できる強力な機能です。

  • 通常ビューとの最大の違いは結果を物理保存すること。参照速度は格段に速い
  • リフレッシュモードはCOMPLETE(全件再構築)・FAST(差分のみ)・FORCE(自動選択)の3種
  • FASTリフレッシュにはベーステーブルへのMVログ作成が必須
  • ON COMMIT はデータ鮮度を最大化するが、COMMITごとにオーバーヘッドが発生
  • スケジュール設定はSTART WITH / NEXT で柔軟に定義できる
  • クエリリライトでアプリを変えずに透過的な高速化が可能
  • インデックスを追加することでMVへの参照をさらに高速化できる

ビューの基本的な使い方については【Oracle】ビューの使い方、ジョブスケジューリングと組み合わせて定期リフレッシュを自動化する場合は【Oracle】ジョブの確認方法完全ガイドも参照してください。