複数テーブルに対する JOIN 操作は、複雑な分析クエリにおいて一般的なパフォーマンスボトルネックです。PolarDB for MySQL の JOIN 除去機能は、クエリ最適化フェーズで不要な JOIN 操作を特定して削除します。これにより、実行計画が簡素化され、I/O と計算が削減され、クエリのパフォーマンスが向上します。
利用シーン
製品エディション:Cluster Edition および Standard Edition。
MySQL バージョン:MySQL 8.0.2、リビジョン 8.0.2.2.31.1 以降。
JOIN 除去の有効化
パラメーター join_elimination_mode を設定することで、この最適化機能の動作を制御できます。
PolarDB クラスターのパラメーターの変更方法は、コンソールとデータベースセッションで異なります。違いは次のとおりです:
PolarDB コンソール の場合
互換性:MySQL の設定ファイルとの互換性を確保するため、PolarDB コンソールの一部のクラスターパラメーターには loose_ プレフィックスが付いています。
手順:
loose_プレフィックスが付いたパラメーターを検索して変更します。
データベースセッション (コマンドラインまたはクライアントを使用) の場合
手順:
SETコマンドを使用してデータベースセッションでパラメーターを変更する場合、loose_プレフィックスを削除し、元のパラメーター名を使用します。
パラメーター | レベル | 説明 |
| グローバル/セッション | この機能のメインスイッチを制御します。有効な値は次のとおりです:
|
最適化シナリオ
この機能は、次の 6 つのシナリオで JOIN の自動除去をサポートします。
シナリオ 1:定数 FALSE 条件を持つ LEFT JOIN の除去
LEFT JOIN の ON 条件が常に FALSE の場合、その結合は意味をなしません。オプティマイザーは内部テーブルを削除し、そのすべての列を NULL に置き換えます。
-- 最適化前
SELECT ..., ti1.*, ti2.*, ... FROM ... LEFT JOIN (ti1, ti2, ...) ON FALSE;
-- 最適化後
SELECT ..., NULL, NULL, ... FROM ...;除去の条件
LEFT JOIN の ON 条件が、1=0 や FALSE のように、常に FALSE となる式であること。
例
環境の準備
DROP TABLE IF EXISTS orders; CREATE TABLE orders ( order_id INT PRIMARY KEY, customer_id INT, order_date DATE ); DROP TABLE IF EXISTS customers; CREATE TABLE customers ( customer_id INT PRIMARY KEY, customer_name VARCHAR(100) ); INSERT INTO orders VALUES (1, 101, '2023-10-01');最適化前:JOIN 除去を無効にし、
ON FALSEを使用してクエリを実行します。SET SESSION join_elimination_mode = 'OFF'; EXPLAIN SELECT o.*, c.* FROM orders o LEFT JOIN customers c ON FALSE;SHOW warnings; +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Level | Code | Message | +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Note | 1003 | /* select#1 */ select `testdb`.`o`.`order_id` AS `order_id`,`testdb`.`o`.`customer_id` AS `customer_id`,`testdb`.`o`.`order_date` AS `order_date`,`testdb`.`c`.`customer_id` AS `customer_id`,`testdb`.`c`.`customer_name` AS `customer_name` from `testdb`.`orders` `o` left join `testdb`.`customers` `c` on(false) where true | +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+最適化後:JOIN 除去を有効にし、同じクエリを実行します。
SET SESSION join_elimination_mode = 'ON'; EXPLAIN SELECT o.*, c.* FROM orders o LEFT JOIN customers c ON FALSE;SHOW warnings; +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Level | Code | Message | +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Note | 1003 | /* select#1 */ select `testdb`.`o`.`order_id` AS `order_id`,`testdb`.`o`.`customer_id` AS `customer_id`,`testdb`.`o`.`order_date` AS `order_date`,NULL AS `customer_id`,NULL AS `customer_name` from `testdb`.`orders` `o` | +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+実行計画から、
customersテーブルへのアクセスが除去されたことがわかります。
シナリオ 2:結果に影響を与えない一意キーでの LEFT JOIN の除去
LEFT JOIN の内部テーブルがクエリの他の部分で参照されておらず、かつ JOIN 操作が外部テーブルの行数を変更しない場合、内部テーブルの JOIN は安全に除去できます。
-- 最適化前
SELECT to1.*, to2.*, ..., tom.* FROM to1, to2, ..., tom LEFT JOIN (ti1, ti2, ..., tin) ON cond_on WHERE cond_where ...
-- 最適化後
SELECT to1.*, to2.*, ..., tom.* FROM to1, to2, ..., tom WHERE cond_where ...除去の条件
内部テーブルが参照されていない:内部テーブル (つまり
ti1, ti2, ..., tin) の列が、LEFT JOINとその ON 条件の外で参照されていないこと。JOIN が外部テーブルのカーディナリティや結果に影響を与えない:次のいずれかの条件を満たす必要があります。
結合が一意である:外部テーブルの各行に対して、LEFT JOIN が内部テーブルのちょうど 1 行に一致する。
JOIN によって生成される重複行が結果に影響を与えない:JOIN によって外部テーブルの行が重複しても、クエリ内の後続の操作がこれらの重複の影響を排除する。一般的なケースは次のとおりです:
JOIN が
EXISTSまたはIN句 (SEMI JOIN) の中にある。サブクエリに
GROUP BY、LIMIT、またはウィンドウ関数などの条件が含まれている。上記の 2 つの条件の再帰的な使用。
シナリオ 3:自己結合の除去
ベーステーブル (またはその派生テーブル) が INNER JOIN を使用して自身と結合される場合、これは 2 つの同一または類似のデータセットを照合することに相当します。特定の条件が満たされると、オプティマイザーはデータセットの 1 つを冗長とみなし、同じデータを 2 回読み取ることを避けるために実行計画から除去できます。
ベーステーブルとベーステーブルの結合:
-- 最適化前 SELECT target.*, source.* FROM t1 as target JOIN t1 as source WHERE target.uk = source.uk; -- 最適化後 SELECT source.*, source.* FROM source WHERE source.uk = source.uk;派生テーブルと派生テーブルの結合:
-- 最適化前 SELECT target.*, source.* FROM (SELECT * FROM t1) target JOIN (SELECT * FROM t1 WHERE t1.a > 1) source WHERE target.uk = source.uk; -- 最適化後 SELECT source.*, source.* FROM (SELECT * FROM t1 WHERE t1.a > 1) source WHERE source.uk = source.uk;ベーステーブルと派生テーブルの結合:
-- 最適化前 SELECT target.*, source.* FROM t1 target JOIN (SELECT * FROM t1 WHERE t1.a > 1) source WHERE target.uk = source.uk; -- 最適化後 SELECT source.*, source.* FROM (SELECT * FROM t1 WHERE t1.a > 1) source WHERE source.uk = source.uk;
除去の条件
サブセット関係:
sourceテーブルの結果セットは、targetテーブルの結果セットのサブセットでなければなりません。列の可用性:
targetテーブルから参照されるすべての列は、sourceテーブルにも存在しなければなりません。これにより、保持されるsourceテーブルがクエリに必要なすべての列を含むことが保証されます。一意キー結合:
JOIN条件は、targetテーブルの一意キーまたはプライマリキーに基づく等価比較でなければなりません。この条件は、source テーブルの各行が
targetテーブルの最大 1 行に一致することを保証するため非常に重要です。これにより、targetテーブルを除去しても行数やクエリ結果が変わらないことが保証されます。特殊なケース:結合条件に一意キーに基づく等価比較がない場合、除去は
targetテーブル自体の結果セットに 0 行または 1 行しか含まれていない場合にのみ可能です。
シナリオ 4:自己セルフジョインの除去
テーブルが自身とセルフジョインされる場合 (SEMI JOIN、通常は IN または EXISTS 句として表現される)、特定の条件が満たされれば、サブクエリ内のテーブルを除去し、その条件を外部クエリにマージできます。
一意の列の等価条件がある場合。
-- 最適化前 SELECT source.* FROM t1 as source WHERE EXISTS (SELECT * FROM t1 as target WHERE source.uk = target.uk AND target.a > 1); -- 最適化後 SELECT source.* FROM t1 as source WHERE source.uk = source.uk AND source.a > 1;一意の列の等価条件がない場合。
-- 最適化前 SELECT source.* FROM t1 as source WHERE EXISTS (SELECT * FROM t1 as target WHERE source.a = target.a); -- 最適化後 SELECT source.* FROM t1 as source WHERE source.a = source.a;
除去の条件
サブセット関係:
sourceテーブルの結果セットは、targetテーブルの結果セットのサブセットでなければなりません。列の可用性:
targetテーブルから参照されるすべての列は、sourceテーブルにも存在しなければなりません。これにより、保持されるsourceテーブルがクエリに必要なすべての列を含むことが保証されます。さらに、次のいずれかの条件を満たす必要があります:
一意キー結合:
JOIN条件は、targetテーブルの一意キーまたはプライマリキーに基づく等価比較でなければなりません。この条件は、source テーブルの各行が
targetテーブルの最大 1 行に一致することを保証するため非常に重要です。これにより、targetテーブルを除去しても行数やクエリ結果が変わらないことが保証されます。特殊なケース:結合条件に一意キーに基づく等価比較がない場合、除去は
targetテーブル自体の結果セットに 0 行または 1 行しか含まれていない場合にのみ可能です。
すべての結合条件が等価結合 (つまり、
) であり、 ANDによって他の条件と接続されている。
シナリオ 5:外部キー結合の除去
2 つのテーブル間に外部キー制約が存在する場合、JOIN 条件がこの外部キー関係に基づいており、クエリが子テーブル (外部キーを持つテーブル) の列のみを参照している場合、親テーブルの結合は除去できます。これは、外部キー制約によって、子テーブルのすべての行が親テーブルに対応する行を持つことが保証されるためです。
CREATE TABLE target (a int primary key);
CREATE TABLE source (a int, foreign key (a) references target(a));
-- 最適化前
SELECT target.a, source.a FROM target, source WHERE target.a = source.a;
-- 最適化後
SELECT source.a, source.a FROM source WHERE source.a = source.a;除去の条件
親テーブルの参照が置換可能である:親テーブルから参照されるすべての列が、子テーブルの対応する外部キー列で置換可能であること。これは通常、親テーブルの結合キーのみが参照されることを意味します。
結合条件が外部キー関係に基づいている:
JOIN条件が、子テーブルの外部キー列と親テーブルのプライマリキーまたは一意キー列との間の等価比較であること。外部キーが null でない:子テーブルの外部キー列が
NOT NULLとして定義されていること。これにより、子テーブルのすべての行が親テーブルに対応する行を持つことが保証されます。
シナリオ 6:外部キーセルフジョインの除去
EXISTS または IN 句を使用して、外部キー関係に基づいて親テーブルに対応するレコードが存在するかどうかを確認する場合、このチェックは冗長です。外部キー制約によって、レコードが存在することはすでに保証されています。したがって、SEMI JOIN サブクエリは除去できます。
CREATE TABLE target (a int primary key);
CREATE TABLE source (a int, foreign key (a) references target(a));
-- 最適化前
SELECT source.a FROM source WHERE EXISTS(SELECT * FROM target WHERE target.a = source.a);
-- 最適化後
SELECT source.a FROM source WHERE source.a = source.a;除去の条件
親テーブルの参照が置換可能である:親テーブルから参照されるすべての列が、子テーブルの対応する外部キー列で置換可能であること。これは通常、親テーブルの結合キーのみが参照されることを意味します。
結合条件が外部キー関係に基づいている:
JOIN条件が、子テーブルの外部キー列と親テーブルのプライマリキーまたは一意キー列との間の等価比較であること。外部キーが null でない:子テーブルの外部キー列が
NOT NULLとして定義されていること。これにより、子テーブルのすべての行が親テーブルに対応する行を持つことが保証されます。