マテリアライズドビューを使用してクエリを高速化する場合、ベーステーブルに対するクエリ実行時に、システムは自動的に既存のマテリアライズドビューを使用したクエリにリライトします。この方法の原理は、クエリステートメントとマテリアライズドビューの定義を照合し、一致するマテリアライズドビューが見つかった場合、自動的にクエリをマテリアライズドビューを使用したクエリにリライトすることで、クエリのパフォーマンスと効率を大幅に向上させることができます。
マテリアライズドビューによるクエリのリライトの制限
マテリアライズドビューは以下の要件を満たしている必要があります:
マテリアライズドビューを作成する際に、
ENABLE QUERY REWRITE句を指定する必要があります。注意
OceanBaseデータベースのOracleモードでは、リライトをサポートするマテリアライズドビューを作成する際には、更新方法(
refresh_clause)を指定する必要があります。マテリアライズドビューには
SELECT、JOIN、WHERE、GROUP BY句のみを含める必要があります。つまり、SPJGクエリである必要があります。条件を満たさないマテリアライズドビューはエラーにはなりませんが、リライトには使用されません。
現在のクエリは以下の要件を満たしている必要があります:
- クエリは
SELECTクエリであり、集合クエリや階層クエリではなく、ウィンドウ関数も含まれていません。 FROM句には、マテリアライズドビューに出現するすべてのテーブルが含まれており、マテリアライズドビューには存在しないテーブルも含めることができ、結合順序が互換性があります。WHERE条件は、マテリアライズドビューのサブセットであり、現在のクエリに集約がある場合は完全に一致している必要があります。例えば、マテリアライズドビューのWHEREがc1 > 10、現在のクエリのWHEREがc1 > 10 AND c2 >20の場合、マテリアライズドビューの条件 {c1 > 10} は現在のクエリの条件 {c1>10, c2>20} のサブセットです。- クエリに集約がない場合、マテリアライズドビューにも集約がない必要があります。
- クエリに集約がある場合、マテリアライズドビューに集約がないか、またはマテリアライズドビューにクエリのすべての
GROUP BY列が含まれている必要があります。集約を含むマテリアライズドビューを使用してリライトする場合、FROMとWHEREが完全に一致している必要があります。 - 現在のクエリで扱われている
SELECT項目、WHERE、HAVING、GROUP BYなどの条件に含まれる列は、すべてマテリアライズドビューのSELECTステートメントの列に含まれている必要があります。
- クエリは
ルール/コストチェック
クエリのリライト時、OceanBaseデータベースの現在のバージョンではコストチェックは行われませんが、ルールチェックでは、リライト後のWHERE条件で少なくとも1つのマテリアライズドビュー上のインデックスを使用できる必要があります。また、マテリアライズドビューが10個を超える場合、マテリアライズドビューによるクエリのリライトは最初の10個のマテリアライズドビューのみを使用して試みられます。
マテリアライズドビューのリライト
マテリアライズドビューによるクエリのリライトを制御するシステム変数は以下のとおりです:
query_rewrite_enabled:マテリアライズドビューのリライト機能を有効にするかどうかを設定します。この変数の詳細については、query_rewrite_enabledを参照してください。例:
SET query_rewrite_enabled = 'force';query_rewrite_integrity:マテリアライズドビューのリライトにおけるデータ一貫性チェックのレベルを指定します。この変数の詳細については、query_rewrite_integrityを参照してください。例:
SET query_rewrite_integrity = 'stale_tolerated';
以下の例でマテリアライズドビューのリライトを示します:
テーブル
test_tbl1を作成します。CREATE TABLE test_tbl1 (col1 INT, col2 INT, col3 INT);テーブル
test_tbl2を作成します。CREATE TABLE test_tbl2 (col1 INT, col2 INT, col3 INT);mv_test_tbl1_tbl2という名前のマテリアライズドビューを作成し、現在のマテリアライズドビューの自動リライトを有効にします。CREATE MATERIALIZED VIEW mv_test_tbl1_tbl2 NEVER REFRESH ENABLE QUERY REWRITE AS SELECT t1.col1 col1, t1.col2 t1col2, t1.col3 t1col3, t2.col2 t2col2, t2.col3 t2col3 FROM test_tbl1 t1, test_tbl2 t2 WHERE t1.col1 = t2.col1;クエリ
SELECT count(*), test_tbl1.col1 col1 FROM test_tbl1, test_tbl2 WHERE test_tbl1.col1 = test_tbl2.col1 AND test_tbl2.col2 > 10 GROUP BY test_tbl1.col1;を実行すると、マテリアライズドビューによるクエリのリライトが発生します。SELECT count(*), test_tbl1.col1 col1 FROM test_tbl1, test_tbl2 WHERE test_tbl1.col1 = test_tbl2.col1 AND test_tbl2.col2 > 10 GROUP BY test_tbl1.col1; MV REWRITE ==> SELECT count(*), mv_test_tbl1_tbl2.col1 col1 FROM mv_test_tbl1_tbl2 WHERE mv_test_tbl1_tbl2.t2col2 > 10 GROUP BY mv_test_tbl1_tbl2.col1;
マテリアライズドビューによるクエリのリライト制御
マテリアライズドビューによるクエリのリライト制御には、MV_REWRITE と NO_MV_REWRITE の2つのヒントがあり、これら2つのヒントの優先順位はシステム変数 query_rewrite_enabled よりも高いです。
MV_REWRITE
MV_REWRITE の構文は次のとおりです:
/*+ MV_REWRITE (@ queryblock [mv_name_list]) */
mv_name_list:
mv_name [, mv_name ...]
MV_REWRITE ヒントを単独で使用する場合、マテリアライズドビューによるクエリのリライトルールやコストチェックをスキップし、利用可能なリライトを直接適用します。ヒントの後に1つ以上のマテリアライズドビューを指定する場合、ルール/コストチェックをスキップするだけでなく、マテリアライズドビューによるクエリのリライトは指定されたマテリアライズドビューのみを使用して試みられ、指定されていないすべてのマテリアライズドビューは無視されます。
MV_REWRITE ヒントを使用してマテリアライズドビューを指定しても、ENABLE QUERY REWRITE (現在のマテリアライズドビューの自動リライトを有効にする) 句がないマテリアライズドビューの使用を強制することはできません。また、システム変数 query_rewrite_integrity が enforced に設定されている場合、非リアルタイムマテリアライズドビューの使用を強制することもできません。
NO_MV_REWRITE
NO_MV_REWRITE の構文は次のとおりです:
/*+ NO_MV_REWRITE (@ queryblock) */
マテリアライズドビューによるクエリのリライトを禁止します。query blockを指定できます。
マテリアライズドビューのクエリリライト制御Hintの使用例
ベーステーブル
tbl2を作成します。CREATE TABLE tbl2 (col1 INT, col2 INT);ベーステーブル
tbl2に2行のデータを挿入します。INSERT INTO tbl2 VALUES (1,2),(3,4);実行結果は次のとおりです:
Query OK, 2 rows affected Records: 2 Duplicates: 0 Warnings: 0マテリアライズドビュー
mv1_tbl2を作成し、現在のマテリアライズドビューの自動リライトを有効にします。CREATE MATERIALIZED VIEW mv1_tbl2 NEVER REFRESH ENABLE QUERY REWRITE AS SELECT * FROM tbl2;マテリアライズドビュー
mv2_tbl2を作成し、現在のマテリアライズドビューの自動リライトを有効にします。CREATE MATERIALIZED VIEW mv2_tbl2 NEVER REFRESH ENABLE QUERY REWRITE AS SELECT * FROM tbl2 WHERE tbl2.col1 > 1;システム変数
query_rewrite_integrityをstale_toleratedに設定します。説明
MV_REWRITEおよびNO_MV_REWRITEHintの優先順位はシステム変数query_rewrite_enabledよりも高いため、query_rewrite_enabledを設定する必要はありません。query_rewrite_integrityをstale_toleratedに設定しておく必要があります。これにより、非リアルタイムマテリアライズドビューを使用したリライトを実行できます。SET query_rewrite_integrity = 'stale_tolerated';MV_REWRITEHintを使用してマテリアライズドビューによるリライトを試み、リライトコスト/ルールチェックをスキップします。以下の2つのクエリは、どちらもマテリアライズドビューmv1_tbl2を使用してリライトされます。/*+mv_rewrite*/は、リライト条件に合致するマテリアライズドビューを使用してリライトを試みます。リライト要件に合致するマテリアライズドビューが見つかると、その後のマテリアライズドビューは検討されず、リライトコスト/ルールチェックもスキップされます。EXPLAIN SELECT /*+mv_rewrite*/ count(*), col1 FROM tbl2 WHERE tbl2.col1 > 1 GROUP BY col1;実行結果は次のとおりです:
+----------------------------------------------------------------------------------------------+ | Query Plan | +----------------------------------------------------------------------------------------------+ | ===================================================== | | |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| | | ----------------------------------------------------- | | |0 |HASH GROUP BY | |1 |3 | | | |1 |└─TABLE FULL SCAN|MV1_TBL2|1 |3 | | | ===================================================== | | Outputs & filters: | | ------------------------------------- | | 0 - output([T_FUN_COUNT(*)], [MV1_TBL2.COL1]), filter(nil), rowset=16 | | group([MV1_TBL2.COL1]), agg_func([T_FUN_COUNT(*)]) | | 1 - output([MV1_TBL2.COL1]), filter([MV1_TBL2.COL1 > cast(1, NUMBER(-1, -85))]), rowset=16 | | access([MV1_TBL2.COL1]), partitions(p0) | | is_index_back=false, is_global_index=false, filter_before_indexback[false], | | range_key([MV1_TBL2.__pk_increment]), range(MIN ; MAX)always true | +----------------------------------------------------------------------------------------------+ 14 rows in set/*+mv_rewrite(mv1_tbl2)*/は、mv2_tbl2を使用してリライトを試み、リライトコスト/ルールチェックをスキップします。EXPLAIN SELECT /*+mv_rewrite(mv2_tbl2)*/ count(*), col1 FROM tbl2 WHERE tbl2.col1 > 1 GROUP BY col1;実行結果は次のとおりです:
+-------------------------------------------------------------------------+ | Query Plan | +-------------------------------------------------------------------------+ | ===================================================== | | |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| | | ----------------------------------------------------- | | |0 |HASH GROUP BY | |1 |3 | | | |1 |└─TABLE FULL SCAN|MV2_TBL2|1 |3 | | | ===================================================== | | Outputs & filters: | | ------------------------------------- | | 0 - output([T_FUN_COUNT(*)], [MV2_TBL2.COL1]), filter(nil), rowset=16 | | group([MV2_TBL2.COL1]), agg_func([T_FUN_COUNT(*)]) | | 1 - output([MV2_TBL2.COL1]), filter(nil), rowset=16 | | access([MV2_TBL2.COL1]), partitions(p0) | | is_index_back=false, is_global_index=false, | | range_key([MV2_TBL2.__pk_increment]), range(MIN ; MAX)always true | +-------------------------------------------------------------------------+ 14 rows in set
クエリは
mv2_tbl2の使用を指定していますが、クエリステートメントのWHERE条件が要件を満たさないため、mv2_tbl2はクエリのリライトに使用できません。そのため、このクエリはマテリアライズドビューによるクエリリライトを行いません。EXPLAIN SELECT /*+mv_rewrite(mv2_tbl2)*/ count(*), col1 FROM tbl2 WHERE tbl2.col1 < 1 GROUP BY col1;実行結果は次のとおりです:
+--------------------------------------------------------------------------------------+ | Query Plan | +--------------------------------------------------------------------------------------+ | ================================================= | | |ID|OPERATOR |NAME|EST.ROWS|EST.TIME(us)| | | ------------------------------------------------- | | |0 |HASH GROUP BY | |1 |3 | | | |1 |└─TABLE FULL SCAN|TBL2|1 |3 | | | ================================================= | | Outputs & filters: | | ------------------------------------- | | 0 - output([T_FUN_COUNT(*)], [TBL2.COL1]), filter(nil), rowset=16 | | group([TBL2.COL1]), agg_func([T_FUN_COUNT(*)]) | | 1 - output([TBL2.COL1]), filter([TBL2.COL1 < cast(1, NUMBER(-1, -85))]), rowset=16 | | access([TBL2.COL1]), partitions(p0) | | is_index_back=false, is_global_index=false, filter_before_indexback[false], | | range_key([TBL2.__pk_increment]), range(MIN ; MAX)always true | +--------------------------------------------------------------------------------------+ 14 rows in set/*+ no_mv_rewrite*/Hintを使用すると、マテリアライズドビューによるクエリリライトは行われません。EXPLAIN SELECT /*+no_mv_rewrite*/ count(*), col1 FROM tbl2 WHERE tbl2.col1 > 1 GROUP BY col1;実行結果は次のとおりです:
+--------------------------------------------------------------------------------------+ | Query Plan | +--------------------------------------------------------------------------------------+ | ================================================= | | |ID|OPERATOR |NAME|EST.ROWS|EST.TIME(us)| | | ------------------------------------------------- | | |0 |HASH GROUP BY | |1 |3 | | | |1 |└─TABLE FULL SCAN|TBL2|1 |3 | | | ================================================= | | Outputs & filters: | | ------------------------------------- | | 0 - output([T_FUN_COUNT(*)], [TBL2.COL1]), filter(nil), rowset=16 | | group([TBL2.COL1]), agg_func([T_FUN_COUNT(*)]) | | 1 - output([TBL2.COL1]), filter([TBL2.COL1 > cast(1, NUMBER(-1, -85))]), rowset=16 | | access([TBL2.COL1]), partitions(p0) | | is_index_back=false, is_global_index=false, filter_before_indexback[false], | | range_key([TBL2.__pk_increment]), range(MIN ; MAX)always true | +--------------------------------------------------------------------------------------+ 14 rows in set
マテリアライズドビューによるクエリのリライトの適用シナリオ
OceanBaseデータベースのマテリアライズドビューによるリライトは、非集約マテリアライズドビューのFROM接続互換性をサポートし、マテリアライズドビューに存在しないテーブルを含むクエリのリライトを可能にします。また、集約を伴うマテリアライズドビューのリライトや、集約上巻き込みのリライトもサポートしています。
説明
以下の例でクエリステートメントが作成するマテリアライズドビューは、フル更新マテリアライズドビューです。増分更新マテリアライズドビューのクエリステートメントも対応する条件を満たせば、同様のシナリオでのクエリリライトをサポートします。
接続順序の互換性
マテリアライズドビューとクエリの FROM 句における接続順序が異なる場合でも、両者の接続順序を変換した後で等価であれば、マテリアライズドビューを用いた書き換えが可能です。
マテリアライズドビューによる書き換えでは、競合検出器がクエリの接続順序とマテリアライズドビューの互換性をチェックします。
例:
マテリアライズドビュー内のクエリ文:
SELECT t1.c1 t1c1, t2.c1 t2c1, t3.c1 t3c1
FROM t1 INNER JOIN t2 ON t1.c1 = t2.c1
INNER JOIN t3 ON t1.c1 = t3.c1;
現在のクエリ文:
SELECT t1.c1, t2.c1, t3.c1
FROM t1 INNER JOIN t3 ON t1.c1 = t3.c1, t2
WHERE t1.c1 = t2.c1;
クエリの書き換え後の文:
SELECT mv.t1c1, mv.t2c1, mv.t3c1
FROM mv;
接続タイプの互換性
マテリアライズドビューの接続タイプがクエリと異なる場合でも、補償述語を追加することで互換性がある場合は、マテリアライズドビューを用いた書き換えが可能です。
接続タイプの互換性には以下の条件を満たす必要があります:
- マテリアライズドビューの接続には、より多くのNULL行が含まれている。例えば、クエリが
INNER JOINを必要とする場合、マテリアライズドビューがLEFT OUTER JOINまたはFULL OUTER JOINである。 - 現在の接続ノードには、NULL行をフィルタリングするために使用できる非NULL列が存在する。
- 現在のノード上のフィルタリング述語を
WHERE句に引き上げることができる。
例:
マテリアライズドビュー内のクエリ文:
SELECT t1.c1 t1c1, t2.pk t2pk
FROM t1 LEFT JOIN t2 ON t1.c1 = t2.c1;
現在のクエリ文:
SELECT t1.c1
FROM t1, t2
WHERE t1.c1 = t2.c1;
クエリの書き換え後の文:
SELECT mv.t1c1
FROM mv
WHERE mv.t2pk IS NOT NULL;
クエリにマテリアライズドビューにないテーブルが含まれる場合
クエリにマテリアライズドビューに存在しないテーブルが含まれる場合、マテリアライズドビューによる書き換えは接続順序をチェックし、余分なテーブルがマテリアライズドビューの後に接続できるかどうかを確認します。可能であれば書き換えを行います。
例:
マテリアライズドビュー内のクエリ文:
SELECT t1.c1 t1c1, t1.pk t1pk
FROM t1 LEFT JOIN t2 ON t1.c1 = t2.c1;
現在のクエリ文:
SELECT t1.c1
FROM t1, t2, t3
WHERE t1.c1 = t2.c1
AND t3.c1 = t1.c1;
クエリの書き換え後の文:
SELECT mv.t1c1
FROM mv, t3
WHERE t3.c1 = mv.t1c1
AND mv.t1pk IS NOT NULL;
集計を含むマテリアライズドビューの書き換え
集計を含むマテリアライズドビューを使用して書き換える場合、クエリとマテリアライズドビューの FROM 句の順序が互換性があり、かつクエリにマテリアライズドビューにないテーブルが含まれていないこと、そしてクエリの WHERE 句もマテリアライズドビューと完全に一致している必要があります。
例:
マテリアライズドビュー内のクエリ文:
SELECT SUM(c1) sumc1, c2, c3
FROM t1
WHERE c1 > 10
GROUP BY c2, c3;
現在のクエリ文:
SELECT SUM(c1), c2, c3
FROM t1
WHERE c1 > 10
GROUP BY c2, c3;
クエリの書き換え後の文:
SELECT mv.sumc1, mv.c2, mv.c3
FROM mv;
集計のアップロールによるリライト
マテリアライズドビューの集計列とクエリの集計列が異なる場合、クエリの集計列がマテリアライズドビューの集計列の部分集合であれば、マテリアライズドビューを用いたリライトが可能です。この場合、一部の集計関数のみがサポートされ、その他の要件は集計関数を含むマテリアライズドビューを用いたリライトと同じです。
集計のアップロールでサポートされている集計関数:COUNT、SUM、BIT_AND、BIT_OR、BIT_XOR、MIN、MAX。
例:
マテリアライズドビュー内のクエリ文:
SELECT SUM(c1) sumc1, COUNT(c1) cntc1, c2, c3
FROM t1
WHERE c1 > 10
GROUP BY c2, c3;
現在のクエリ文:
SELECT SUM(c1), COUNT(c1), c2
FROM t1
WHERE c1 > 10
AND c3 = 10
GROUP BY c2;
クエリのリライト後の文:
SELECT SUM(mv.sumc1), COUNT_SUM(mv.cntc1), mv.c2
FROM mv
WHERE mv.c3 = 10
GROUP BY mv.c2;