複数のテーブルを結合するシナリオにおいて、オプティマイザーの重要なタスクの一つは各テーブル間の結合順序(Join Order)を決定することです。これは、異なる結合順序が中間結果セットのサイズに影響を与え、ひいては計画全体の実行コストにも影響を及ぼすためです。
実行計画の探索空間と計画実行時のメモリ使用量を削減するため、OceanBaseデータベースのオプティマイザーは結合順序を生成する際に主に左深木の結合形式を考慮します。以下の図は、左深木、右深木、および複数分岐木の計画形状を示しています。

OceanBaseデータベースは、結合順序を生成する際にSystem-Rの動的計画法アルゴリズムを採用しており、考慮される要素には各テーブルの可能なアクセスパス、Interesting Order、結合アルゴリズム(Nested Loop Join、Block Based Nested Loop Join、またはSort Merge Joinなど)、および異なるテーブル間の結合選択率などが含まれます。
N個のテーブルの結合が与えられた場合、OceanBaseデータベースが結合順序を生成する方法は以下の通りです:
各ベーステーブルに対してアクセスパスを生成し、コストが最小のアクセスパスとInteresting Orderを持つすべてのパスを保持します。あるパスがInteresting Orderを持つ場合、その順序は後続の演算子で使用される可能性があります。
すべてのテーブル集合のサイズが
i (1 < i <= N)の計画を生成します。OceanBaseデータベースは一般的に左深木のみを考慮し、テーブル集合サイズがiの計画は、それ自身の計画と1つのベーステーブルの計画から構成されることがあります。この戦略に従って、OceanBaseデータベースはすべての結合アルゴリズムおよびInteresting Orderの継承などの要素を考慮し、すべてのテーブル集合サイズがiの計画を生成します。ここでも、コストが最小の計画とInteresting Orderを持つすべての計画のみが保持されます。
同時に、OceanBaseデータベースはヒントメカニズム /*+LEADING(table_name_list)*/ を提供して、複数テーブルの結合順序を制御します。
以下の例のように、最初に選択される結合順序は、t1 テーブルと t2 テーブルの JOIN 結合を行い、次に t3 テーブルと JOIN 結合を行うことです。ユーザーが最初に t2 テーブルと t3 テーブルの JOIN 結合を行い、その後 t1 テーブルと JOIN 結合を行うことを望む場合は、ヒント /*+LEADING(t2,t3,t1)*/ を使用して制御できます。ユーザーが最初に t1 テーブルと t3 テーブルの JOIN 結合を行い、その後 t2 テーブルと JOIN 結合を行うことを望む場合は、ヒント /*+LEADING(t1,t3,t2)*/ を使用して制御できます。
obclient> CREATE TABLE t1(c1 INT, c2 INT, PRIMARY KEY(c1));
Query OK, 0 rows affected
obclient> CREATE TABLE t2(c1 INT, c2 INT, PRIMARY KEY(c1));
Query OK, 0 rows affected
obclient> CREATE TABLE t3(c1 INT, c2 INT, PRIMARY KEY(c1));
Query OK, 0 rows affected
obclient> EXPLAIN SELECT * FROM t1,t2,t3 WHERE t1.c1 = t2.c2 AND t2.c1 = t3.c2;
+-----------------------------------------------------------------+
| Query Plan |
+-----------------------------------------------------------------+
| =======================================
|ID|OPERATOR |NAME|EST. ROWS|COST |
---------------------------------------
|0 |HASH JOIN | |98010 |926122|
|1 | TABLE SCAN |T3 |100000 |61860 |
|2 | HASH JOIN | |99000 |494503|
|3 | TABLE SCAN|T1 |100000 |61860 |
|4 | TABLE SCAN|T2 |100000 |61860 |
=======================================
Outputs & filters:
-------------------------------------
0 - output([T1.C1], [T1.C2], [T2.C1], [T2.C2], [T3.C1], [T3.C2]), filter(nil),
equal_conds([T2.C1 = T3.C2]), other_conds(nil)
1 - output([T3.C2], [T3.C1]), filter(nil),
access([T3.C2], [T3.C1]), partitions(p0)
2 - output([T1.C1], [T1.C2], [T2.C1], [T2.C2]), filter(nil),
equal_conds([T1.C1 = T2.C2]), other_conds(nil)
3 - output([T1.C1], [T1.C2]), filter(nil),
access([T1.C1], [T1.C2]), partitions(p0)
4 - output([T2.C2], [T2.C1]), filter(nil),
access([T2.C2], [T2.C1]), partitions(p0)
obclient> EXPLAIN SELECT /*+LEADING(t2,t3,t1)*/* FROM t1,t2,t3 WHERE t1.c1 = t2.c2
AND t2.c1 = t3.c2;
+-----------------------------------------------------------------+
| Query Plan |
+-----------------------------------------------------------------+
| ========================================
|ID|OPERATOR |NAME|EST. ROWS|COST |
----------------------------------------
|0 |HASH JOIN | |98010 |1096613|
|1 | HASH JOIN | |99000 |494503 |
|2 | TABLE SCAN|T2 |100000 |61860 |
|3 | TABLE SCAN|T3 |100000 |61860 |
|4 | TABLE SCAN |T1 |100000 |61860 |
========================================
Outputs & filters:
-------------------------------------
0 - output([T1.C1], [T1.C2], [T2.C1], [T2.C2], [T3.C1], [T3.C2]), filter(nil),
equal_conds([T1.C1 = T2.C2]), other_conds(nil)
1 - output([T2.C1], [T2.C2], [T3.C1], [T3.C2]), filter(nil),
equal_conds([T2.C1 = T3.C2]), other_conds(nil)
2 - output([T2.C2], [T2.C1]), filter(nil),
access([T2.C2], [T2.C1]), partitions(p0)
3 - output([T3.C2], [T3.C1]), filter(nil),
access([T3.C2], [T3.C1]), partitions(p0)
4 - output([T1.C1], [T1.C2]), filter(nil),
access([T1.C1], [T1.C2]), partitions(p0)
obclient> EXPLAIN SELECT /*+LEADING(t1,t3,t2)*/* FROM t1,t2,t3 WHERE t1.c1 = t2.c2
AND t2.c1 = t3.c2;
+-----------------------------------------------------------------+
| Query Plan |
+-----------------------------------------------------------------+
| =============================================================
|ID|OPERATOR |NAME|EST. ROWS |COST |
-------------------------------------------------------------
|0 |HASH JOIN | |98010 |53098071243|
|1 | NESTED-LOOP JOIN CARTESIAN| |10000000000|7964490204 |
|2 | TABLE SCAN |T1 |100000 |61860 |
|3 | MATERIAL | |100000 |236426 |
|4 | TABLE SCAN |T3 |100000 |61860 |
|5 | TABLE SCAN |T2 |100000 |61860 |
=============================================================
Outputs & filters:
-------------------------------------
0 - output([T1.C1], [T1.C2], [T2.C1], [T2.C2], [T3.C1], [T3.C2]), filter(nil),
equal_conds([T1.C1 = T2.C2], [T2.C1 = T3.C2]), other_conds(nil)
1 - output([T1.C1], [T1.C2], [T3.C1], [T3.C2]), filter(nil),
conds(nil), nl_params_(nil)
2 - output([T1.C1], [T1.C2]), filter(nil),
access([T1.C1], [T1.C2]), partitions(p0)
3 - output([T3.C1], [T3.C2]), filter(nil)
4 - output([T3.C2], [T3.C1]), filter(nil),
access([T3.C2], [T3.C1]), partitions(p0)
5 - output([T2.C2], [T2.C1]), filter(nil),
access([T2.C2], [T2.C1]), partitions(p0)