集計や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 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 マテリアライズドビュー名
[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では時間がかかります。
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を作成します。
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リフレッシュはすべてのSELECT文に使えるわけではありません。集計(GROUP BY / SUM / COUNT)を含む場合は追加の条件があり、JOINの組み合わせによっては利用不可のケースもあります。FAST リフレッシュが使えるかどうかは後述の
DBMS_MVIEW.EXPLAIN_MVIEW で確認できます。
リフレッシュのタイミング設定
ON DEMAND(手動リフレッシュ)
デフォルトの設定です。明示的に DBMS_MVIEW.REFRESH を呼び出したときだけリフレッシュします。
ON COMMIT(コミット時に自動リフレッシュ)
ベーステーブルへのDMLがCOMMITされるたびに自動でリフレッシュします。データの鮮度は最高ですが、COMMITのたびにリフレッシュ処理が走るためオンライントランザクションへの影響を考慮する必要があります。FASTリフレッシュ可能な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 WITH と NEXT でスケジュールを設定します。
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 プロシージャで手動リフレッシュします。
-- 単一の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
-- スキーマ内の全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を活用して高速化できます。
-- 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の一覧と状態確認
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 ではない場合など) |
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ログの確認
SELECT
log_table, -- ログテーブル名(MLOG$_テーブル名)
master, -- ベーステーブル名
log_creator, -- 作成者
primary_key, -- プライマリキーを記録するか
rowids, -- ROWIDを記録するか
sequence -- SEQUENCEを記録するか
FROM user_mview_logs;
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への参照がさらに速くなります。
-- 部署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】インデックス情報を取得する方法も参照してください。
マテリアライズドビューの変更・削除
-- リフレッシュモードを変更(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】ジョブの確認方法完全ガイドも参照してください。

