MySQL オプティマイザーは、OR/IN 式を含む複雑なクエリ、特に複数テーブルの JOIN 操作を処理する際に、インデックスを効果的に使用できない場合があります。これにより、全表スキャンが実行され、クエリパフォーマンスが低下する可能性があります。PolarDB for MySQL のクエリ書き換え最適化機能は、対象の OR/IN 式を UNION ALL 構造に書き換え、コストに基づいて最適な実行パスを選択します。これにより、クエリでインデックスを最大限に活用でき、実行パフォーマンスが大幅に向上します。
仕組み
MySQL では、オプティマイザーは OR 句を処理する際に機能が制限されます。OR 条件に複数のテーブルが含まれる場合、オプティマイザーは多くの場合、それを JOIN 操作後のフィルター条件としてのみ扱います。単一の条件に対してインデックスを効果的に使用することはできません。これにより、全表スキャンが発生し、クエリパフォーマンスが大幅に低下する可能性があります。
たとえば、次のクエリでは、オプティマイザーは t1.b または t3.c1 カラムのインデックスを使用できないため、非常に非効率な全表スキャンとハッシュ結合しか実行できません。
-- Before optimization, the execution plan is a full table scan, and the execution duration is long.
EXPLAIN ANALYZE SELECT * FROM t1,t3 WHERE t3.c1 > 98 OR t1.b <= 0;
-> Filter: ((t3.c1 > 98) or (t1.b <= 0)) ... (actual time=115.259..5416.434 ...)
-> Inner hash join ...
-> Table scan on t3 ...
-> Hash
-> Table scan on t1 ...論理的に、この OR クエリは、UNION ALL を使用して 2 つの個別のクエリの結果をマージすることと同等です。クエリを手動で再書き込みすると、それぞれのインデックスが使用可能になり、パフォーマンスが大幅に向上します。
-- Manually rewritten to UNION ALL, the execution plan can use indexes, and the execution duration is significantly shorter.
EXPLAIN ANALYZE
SELECT * FROM t1 ,t3 WHERE t1.b <= 0
UNION ALL
SELECT * FROM t1,t3 WHERE t3.c1 > 98 AND (t1.b > 0 OR (t1.b <= 0) IS NULL);
-> Append (actual time=58.272..302.546 ...)
...
-> Index range scan on t3 using idx_c1 ...OR/IN 式を UNION ALL 構造に変換する PolarDB の機能は、この手動の最適化プロセスを自動化します。実行計画の生成フェーズにおいて、オプティマイザーは OR 式を UNION ALL 構造に書き換えることによるメリットを評価します。元の実行計画とコストを比較し、より低コストのオプションを実行用に選択します。これにより、SQL を変更することなくクエリが高速化されます。
適用範囲
製品シリーズ:Cluster Edition、Standard Edition。
エンジンバージョン:MySQL 8.0.2、リビジョンバージョンは 8.0.2.2.32 以降である必要があります。
この機能はカナリアリリース中です。読み取り専用 (RO) ノードではデフォルトで有効になっていますが、読み書き (RW) ノードでは追加の設定が必要です。この機能を使用するには、チケットを送信してください。
クエリ再書き込み最適化の有効化と設定
関連するパラメーターを設定することで、この最適化機能の動作を制御できます。
PolarDB クラスターのパラメーターを変更する方法は、コンソールとデータベースセッションで異なります。違いは次のとおりです。
互換性:MySQL 設定ファイルとの互換性のため、PolarDB コンソールの一部のクラスターパラメーターには loose_ プレフィックスが付いています。
手順:
loose_プレフィックスが付いているパラメーターを検索して変更します。
データベースセッション (コマンドラインまたはクライアントを使用) の場合
手順: データベースセッションで
SETコマンドを使用してパラメーターを変更する場合、loose_プレフィックスを取り除き、元のパラメーター名を使用します。
パラメーター | レベル | 説明と提案 |
| グローバル/セッション | この機能のメインスイッチです。
|
| グローバル/セッション | 最適化トリガーのしきい値を制御します。オプティマイザーは、 値の範囲:0 から 18446744073709551615。 デフォルト値:100000。 説明 デフォルト値のままにしてください。このパラメーターを |
制限事項
この機能は、以下のすべての条件が満たされた場合にのみトリガーされます。
一般的な制限:
OR句または IN-LIST のパラメーターの数は 10 を超えることはできません。クエリブロックには、サブクエリ、
GROUP BY句、ウィンドウ関数、DISTINCT句、または集計関数を含めることはできません。
一般的な
UNION ALL変換 (主に複数テーブルの JOIN 用):OR句:OR条件は、2 つ以上のテーブルに関連している必要があります。すべての
OR句は、field=constパターンを使用するか、インデックスを効果的に使用できる必要があります。field=constパターン:fieldはテーブルの列を指し、constは定数値を指します。インデックスの効果的な使用: たとえば、
t1.f1=t2.f2では、f1はt1のインデックスのプレフィックスであり、f2はt2のインデックスのプレフィックスです。
IN-LIST:rangeアクセス方式の方が最適であるため、UNION ALLに変換する必要はありません。
Top-K変換 (主に単一テーブルのORDER BY...LIMITクエリ):OR句:OR条件は同じカラムに適用する必要があります。このカラムとORDER BYカラムは、同じインデックスのプレフィックスである必要があります。たとえば、インデックスが(c2, c3)の場合、クエリはWHERE c2=... OR c2=... ORDER BY c3となります。IN-LIST:IN-LISTの左側の式の列とORDER BY列は、同じインデックスのプレフィックスである必要があります。
例:最適化効果の検証
データ準備
-- Create and populate table t1
CREATE TABLE `t1` (
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
KEY `idx_a` (`a`)
) ENGINE=InnoDB;
-- Insert data
INSERT INTO `t1` VALUES (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9),(10,10);
-- Run this statement repeatedly to increase the data volume
INSERT INTO t1 SELECT * FROM t1;
INSERT INTO t1 SELECT * FROM t1;
INSERT INTO t1 SELECT * FROM t1;
INSERT INTO t1 SELECT * FROM t1;
INSERT INTO t1 SELECT * FROM t1;
INSERT INTO t1 SELECT * FROM t1;
INSERT INTO t1 SELECT * FROM t1;
-- Create and populate table t3
CREATE TABLE `t3` (
`c1` int(11) NOT NULL,
`c2` int(11) DEFAULT NULL,
`c3` int(11) DEFAULT NULL,
`c4` int(11) DEFAULT NULL,
KEY `idx_c1`(`c1`),
KEY `idx_c2_c3` (`c2`,`c3`)
) ENGINE=InnoDB;
-- Insert a large amount of data
INSERT INTO `t3` VALUES (1,0,1,0),(2,0,2,0),(3,0,3,0),(4,0,4,0),(5,0,5,0),(6,0,6,0),(7,0,7,0),(8,0,8,0),(9,0,9,0),(10,0,10,0),(11,0,11,0),(12,0,12,0),(13,0,13,0),(14,0,14,0),(15,0,15,0),(16,0,16,0),(17,0,17,0),(18,0,18,0),(19,0,19,0),(20,0,20,0),(21,0,21,0),(22,0,22,0),(23,0,23,0),(24,0,24,0),(25,1,25,0),(26,1,26,0),(27,1,27,0),(28,1,28,0),(29,1,29,0),(30,1,30,0),(31,1,31,0),(32,1,32,0),(33,1,33,0),(34,1,34,0),(35,1,35,0),(36,1,36,0),(37,1,37,0),(38,1,38,0),(39,1,39,0),(40,1,40,0),(41,1,41,0),(42,1,42,0),(43,1,43,0),(44,1,44,0),(45,1,45,0),(46,1,46,0),(47,1,47,0),(48,1,48,0),(49,1,49,0),(50,1,50,1),(51,1,51,1),(52,1,52,1),(53,1,53,1),(54,1,54,1),(55,1,55,1),(56,1,56,1),(57,1,57,1),(58,1,58,1),(59,1,59,1),(60,1,60,1),(61,1,61,1),(62,1,62,1),(63,1,63,1),(64,1,64,1),(65,1,65,1),(66,1,66,1),(67,1,67,1),(68,1,68,1),(69,1,69,1),(70,1,70,1),(71,1,71,1),(72,1,72,1),(73,1,73,1),(74,1,74,1),(75,2,75,1),(76,2,76,1),(77,2,77,1),(78,2,78,1),(79,2,79,1),(80,2,80,1),(81,2,81,1),(82,2,82,1),(83,2,83,1),(84,2,84,1),(85,2,85,1),(86,2,86,1),(87,2,87,1),(88,2,88,1),(89,2,89,1),(90,2,90,1),(91,2,91,1),(92,2,92,1),(93,2,93,1),(94,2,94,1),(95,2,95,1),(96,2,96,1),(97,2,97,1),(98,2,98,1),(99,2,99,1),(100,2,100,1);
-- Run this statement repeatedly to increase the data volume
INSERT INTO t3 SELECT * FROM t3;
INSERT INTO t3 SELECT * FROM t3;
INSERT INTO t3 SELECT * FROM t3;
INSERT INTO t3 SELECT * FROM t3;
INSERT INTO t3 SELECT * FROM t3;
INSERT INTO t3 SELECT * FROM t3;
-- Analyze the tables
ANALYZE TABLE t1, t3;シナリオ 1:複数テーブルの JOIN クエリの最適化
このシナリオは、OR 条件が 2 つのテーブルにまたがる場合に、オプティマイザーが書き換えによってインデックスを活用する方法を示しています。
最適化機能を無効にし、元の実行計画を確認します。
-- Disable the optimization feature SET polar_optimizer_switch='or_expansion=off'; -- Analyze the statement DESC SELECT * FROM t1,t3 WHERE t3.c1 >98 OR t1.a<5;結果分析: 実行計画には、
Hash Joinと、t1およびt3での全表スキャンが示されています。オプティマイザーは、t1.aとt3.c1のインデックスの使用に失敗しました。+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------------------+ | 1 | SIMPLE | t1 | NULL | ALL | idx_a | NULL | NULL | NULL | 1280 | 100.00 | NULL | | 1 | SIMPLE | t3 | NULL | ALL | idx_c1 | NULL | NULL | NULL | 6591 | 100.00 | Using where; Using join buffer (hash join) | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+--------------------------------------------+最適化機能を有効にし、書き換えられた実行計画を表示します。
-- Enable the optimization feature SET polar_optimizer_switch='or_expansion=on'; -- Lower the threshold to trigger the optimization SET cbqt_cost_threshold=1; -- Analyze the statement DESC SELECT * FROM t1,t3 WHERE t3.c1 >98 OR t1.a<5;結果分析:実行計画が
UNION ALLを使用するように調整されたことで、クエリはt1.aとt3.c1のインデックスを使用できるようになり、UNION ALLへの手動での再書き込みと同じ効果が得られます。+----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+--------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+--------------------------------------------+ | 1 | PRIMARY | t1 | NULL | range | idx_a | idx_a | 5 | NULL | 256 | 100.00 | Using index condition; Using MRR | | 1 | PRIMARY | t3 | NULL | ALL | NULL | NULL | NULL | NULL | 6400 | 100.00 | Using join buffer (hash join) | | 2 | UNION | t3 | NULL | range | idx_c1 | idx_c1 | 4 | NULL | 128 | 100.00 | Using index condition; Using MRR | | 2 | UNION | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 640 | 66.67 | Using where; Using join buffer (hash join) | +----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+--------------------------------------------+
シナリオ 2:Top-K クエリの最適化 (OR 句)
このシナリオでは、オプティマイザーが単一テーブルの ORDER BY ... LIMIT クエリに対して OR 条件を UNION ALL に再書き込みし、LIMIT 句をプッシュダウンする方法を示します。このプロセスにより、大規模なソートが回避されます。
最適化機能を無効にし、元の実行計画を確認します。
-- Disable the optimization feature SET polar_optimizer_switch='or_expansion=off'; -- Analyze the statement DESC ANALYZE SELECT c2 FROM t3 WHERE (c2 = 2 OR c2= 0 ) ORDER BY t3.c3 DESC LIMIT 5;結果分析: 実行計画は、
Index range scanを使用してc2=2またはc2=0の条件を満たすすべての行を取得し、次にSort操作を実行します。実行時間は約 200 ミリ秒です。+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | EXPLAIN | +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | -> Limit: 5 row(s) (actual time=193.389..193.393 rows=5 loops=1) -> Sort: t3.c3 DESC, limit input to 5 row(s) per chunk (cost=641.82 rows=3200) (actual time=193.386..193.388 rows=5 loops=1) -> Index range scan on t3 using idx_c2_c3, with index condition: ((t3.c2 = 2) or (t3.c2 = 0)) (actual time=0.348..187.455 rows=3200 loops=1) | +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.20 sec)最適化機能を有効にし、書き換えられた実行計画を表示します。
-- Disable the optimization feature SET polar_optimizer_switch='or_expansion=on'; -- Lower the threshold to trigger the optimization SET cbqt_cost_threshold=1; -- Analytic statement DESC ANALYZE SELECT c2 FROM t3 WHERE (c2 = 2 OR c2= 0 ) ORDER BY t3.c3 DESC LIMIT 5;結果分析: 実行計画は
UNION ALLを使用するように変更されます。各ブランチ (c2=2およびc2=0) に対してIndex lookupを実行し、LIMIT 5を適用します。次に、オプティマイザーが 2 つのソートされた 5 行の結果セットをマージするため、グローバルソートは不要になります。実行時間は約 1 ミリ秒に短縮されます。+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | EXPLAIN | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | -> Limit: 5 row(s) (actual time=1.249..1.254 rows=5 loops=1) -> Sort: derived_1_2.Name_exp_1 DESC, limit input to 5 row(s) per chunk (actual time=0.104..0.106 rows=5 loops=1) -> Table scan on derived_1_2 (actual time=0.006..0.013 rows=10 loops=1) -> Union materialize (actual time=1.246..1.249 rows=5 loops=1) -> Limit: 5 row(s) (actual time=0.336..0.571 rows=5 loops=1) -> Index lookup on t3 using idx_c2_c3 (c2=2; iterate backwards) (cost=0.00 rows=5) (actual time=0.333..0.566 rows=5 loops=1) -> Limit: 5 row(s) (actual time=0.215..0.431 rows=5 loops=1) -> Index lookup on t3 using idx_c2_c3 (c2=0; iterate backwards) (cost=0.00 rows=5) (actual time=0.214..0.427 rows=5 loops=1) | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.01 sec)
シナリオ 3:Top-K クエリの最適化 (IN-LIST)
IN-LIST は OR 句と論理的に等価であるため、Top-K 最適化もサポートします。
最適化機能を無効にし、元の実行計画を確認します。
-- Disable the optimization feature SET polar_optimizer_switch='or_expansion=off'; -- Analyze the statement DESC ANALYZE SELECT c2 FROM t3 WHERE c2 IN (2, 0) ORDER BY t3.c3 DESC LIMIT 5;結果分析: 実行計画は、
Index range scanを使用してt3.c2 in (2,0)条件を満たすすべての行を取得し、次にSort操作を実行します。実行時間は約 200 ミリ秒です。+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | EXPLAIN | +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | -> Limit: 5 row(s) (actual time=197.497..197.501 rows=5 loops=1) -> Sort: t3.c3 DESC, limit input to 5 row(s) per chunk (cost=641.82 rows=3200) (actual time=197.494..197.496 rows=5 loops=1) -> Index range scan on t3 using idx_c2_c3, with index condition: (t3.c2 in (2,0)) (actual time=0.319..191.560 rows=3200 loops=1) | +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.20 sec)最適化機能を有効にし、書き換えられた実行計画を表示します。
-- Disable the optimization feature SET polar_optimizer_switch='or_expansion=on'; -- Lower the threshold to trigger the optimization SET cbqt_cost_threshold=1; -- Analytic statement DESC ANALYZE SELECT c2 FROM t3 WHERE c2 IN (2, 0) ORDER BY t3.c3 DESC LIMIT 5;結果分析: 実行計画は
UNION ALLを使用するように変更されます。各ブランチ (c2=2およびc2=0) に対してIndex lookupを実行し、LIMIT 5を適用します。その後、オプティマイザーは 2 つのソート済みの 5 行の結果セットをマージするため、グローバルソートが不要になります。+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | EXPLAIN | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | -> Limit: 5 row(s) (actual time=1.256..1.260 rows=5 loops=1) -> Sort: derived_1_2.Name_exp_1 DESC, limit input to 5 row(s) per chunk (actual time=0.090..0.093 rows=5 loops=1) -> Table scan on derived_1_2 (actual time=0.005..0.012 rows=10 loops=1) -> Union materialize (actual time=1.252..1.255 rows=5 loops=1) -> Limit: 5 row(s) (actual time=0.259..0.545 rows=5 loops=1) -> Index lookup on t3 using idx_c2_c3 (c2=2; iterate backwards) (cost=0.00 rows=5) (actual time=0.256..0.540 rows=5 loops=1) -> Limit: 5 row(s) (actual time=0.237..0.455 rows=5 loops=1) -> Index lookup on t3 using idx_c2_c3 (c2=0; iterate backwards) (cost=0.00 rows=5) (actual time=0.236..0.451 rows=5 loops=1) +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)
手動介入のためのヒントの使用
特定のシナリオでは、HINT を使用して、単一の クエリに対してこの最適化を有効にするかどうかを制御できます。
NO_OR_EXPAND(@QB_NAME): 指定されたクエリブロックのOR展開最適化を強制的に無効にします。DESC SELECT /*+NO_OR_EXPAND(@subq1) */ * FROM t1 WHERE EXISTS (SELECT /*+ QB_NAME(subq1) */ 1 FROM t3 WHERE (t1.a = 1 OR t1.b = 2) AND t3.c1 < 5 AND t1.b = t3.c1); +----+-------------+-------+------------+------+---------------+--------+---------+------------+------+----------+-----------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+--------+---------+------------+------+----------+-----------------------------+ | 1 | SIMPLE | t1 | NULL | ALL | idx_a | NULL | NULL | NULL | 640 | 19.00 | Using where | | 1 | SIMPLE | t3 | NULL | ref | idx_c1 | idx_c1 | 4 | test2.t1.b | 64 | 100.00 | Using index; FirstMatch(t1) | +----+-------------+-------+------------+------+---------------+--------+---------+------------+------+----------+-----------------------------+WHERE句に複数のOR式が含まれている場合、OR_EXPAND(@QB_NAME idx)を使用して、特定の式を強制的にUNION ALL構造に変換できます。idxパラメーターは、WHERE句における式の位置を指定します。インデックスは 0 から始まります。 この例では、式(t3.c2 = 1 OR t1.b = 2)はUNION ALL構造に展開されます。DESC format=tree SELECT /*+OR_EXPAND(@subq1 3) */ * FROM t1 WHERE EXISTS (SELECT /*+ QB_NAME(subq1) */ 1 FROM t3 WHERE (t3.c2 = 999 OR t1.b = 999) AND t3.c1 < 5 AND t1.b = t3.c1 AND (t3.c2= 1 OR t1.b = 2)); +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | EXPLAIN | +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | -> Filter: exists(select #2) (cost=64.75 rows=640) -> Table scan on t1 (cost=64.75 rows=640) -> Select #2 (subquery in condition; dependent) -> Limit: 1 row(s) -> Append -> Stream results -> Filter: (t3.c2 = 1) (cost=17.45 rows=32) -> Index lookup on t3 using idx_c1 (c1=t1.b), with index condition: ((t1.b = 999) and (t3.c1 < 5)) (cost=17.45 rows=64) -> Stream results -> Filter: (t3.c1 = 2) (cost=0.51 rows=0) -> Index lookup on t3 using idx_c2_c3 (c2=999), with index condition: ((t1.b = 2) and lnnvl((t3.c2 = 1))) (cost=0.51 rows=1)OR_EXPAND(@QB_NAME):OR展開の最適化を、指定されたクエリブロック (qb_name) に対して強制的に有効にします。DESC SELECT /*+OR_EXPAND(@subq1) */ * FROM t1 WHERE EXISTS (SELECT /*+ QB_NAME(subq1) */ 1 FROM t3 WHERE (t3.c1 = 1 OR t1.b = 2) AND t3.c1 < 5 AND t1.b = t3.c1); +----+--------------------+-------+------------+------+---------------+--------+---------+-------+------+----------+------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+--------------------+-------+------------+------+---------------+--------+---------+-------+------+----------+------------------------------------+ | 1 | PRIMARY | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 640 | 100.00 | Using where | | 2 | DEPENDENT SUBQUERY | t3 | NULL | ref | idx_c1 | idx_c1 | 4 | const | 64 | 100.00 | Using where; Using index | | 3 | DEPENDENT UNION | t3 | NULL | ref | idx_c1 | idx_c1 | 4 | const | 64 | 100.00 | Using index condition; Using index | +----+--------------------+-------+------------+------+---------------+--------+---------+-------+------+----------+------------------------------------+