マテリアライズドビューを使用してクエリを高速化する場合、ベーステーブルに対するクエリ実行時に、システムは自動的に既存のマテリアライズドビューを使用したクエリにリライトします。この方法の原理は、クエリステートメントとマテリアライズドビューの定義を照合し、一致するマテリアライズドビューが見つかった場合、自動的にクエリをマテリアライズドビューを使用したクエリにリライトすることで、クエリのパフォーマンスと効率を大幅に向上させることができます。
マテリアライズドビューによるクエリのリライトの制限
マテリアライズドビューは以下の要件を満たしている必要があります:
- マテリアライズドビューを作成する際に、
ENABLE QUERY REWRITE句を指定する必要があります。 - マテリアライズドビューには
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 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 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 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) */
マテリアライズドビューのクエリリライトを禁止します。クエリブロックを指定できます。
マテリアライズドビューのクエリリライト制御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 > 1]), 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(mv2_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 < 1]), 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 > 1]), 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;