マテリアライズドビューを使用してクエリを高速化することで、ベーステーブルに対するクエリ実行時に、システムは自動的に既存のマテリアライズドビューを利用したクエリに再書き込みます。この方法の原理は、クエリ文とマテリアライズドビューの定義を照合し、一致するマテリアライズドビューが見つかった場合には、クエリを自動的にマテリアライズドビューを使用したクエリに再書き込むことです。これにより、クエリのパフォーマンスと効率を大幅に向上させることができます。
マテリアライズドビューによるクエリの書き換えの制限
マテリアライズドビューは以下の要件を満たしている必要があります:
マテリアライズドビューを作成する際には、
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) */
マテリアライズドビュークエリの書き換えを禁止し、クエリブロックを指定できます。
マテリアライズドビューによるクエリの書き換えを制御する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;
接続タイプの互換性
マテリアライズドビューの接続タイプがクエリと異なる場合でも、補償述語を追加することで互換性が確保できれば、マテリアライズドビューを用いた書き換えが可能です。
接続タイプの互換性には以下の条件を満たす必要があります:
- マテリアライズドビューの接続には、より多くの補完行が含まれている。例えば、クエリに
INNER JOINが必要な場合、マテリアライズドビューはLEFT OUTER JOINまたはFULL OUTER JOINである。 - 現在の接続ノードには、補完行をフィルタリングするために使用できる非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;