MySQL 8.
MySQL 5.
今回は、
セミジョインの変更点
MySQL 8.
- INまたは=ANYを使用したサブクエリであること
よって、
mysql> EXPLAIN SELECT count(*) FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.id2=t1.id); +----+-------------+-------+------------+--------+---------------+------+---------+----------+--------+----------+-----------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+--------+---------------+------+---------+----------+--------+----------+-----------------------------------------+ | 1 | SIMPLE | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 785178 | 100.00 | Using where; Start temporary | | 1 | SIMPLE | t1 | NULL | eq_ref | id | id | 8 | t.t2.id2 | 1 | 100.00 | Using where; Using index; End temporary | +----+-------------+-------+------------+--------+---------------+------+---------+----------+--------+----------+-----------------------------------------+ 2 rows in set, 2 warnings (0.00 sec) Note (Code 1276): Field or reference 't.t1.id' of SELECT #2 was resolved in SELECT #1 Note (Code 1003): /* select#1 */ select count(0) AS `count(*)` from `t`.`t1` semi join (`t`.`t2`) where (`t`.`t1`.`id` = `t`.`t2`.`id2`)
Note
部分に注目すると、semi join
と記述されていることがわかります。
Note (Code 1003): /* select#1 */ select count(0) AS `count(*)` from `t`.`t1` semi join (`t`.`t2`) where (`t`.`t1`.`id` = `t`.`t2`.`id2`)
アンチジョイン
さらに、NOT EXISTS
やNOT IN
を使用したサブクエリを最適化する機能です。また、
たとえば、
EXPLAIN SELECT count(*) FROM t1 WHERE NOT EXISTS (SELECT 1 FROM t2 WHERE t2.id2=t1.id); +----+--------------------+-------+------------+-------+---------------+------+---------+------+--------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+--------------------+-------+------------+-------+---------------+------+---------+------+--------+----------+--------------------------+ | 1 | PRIMARY | t1 | NULL | index | NULL | id | 8 | NULL | 785175 | 100.00 | Using where; Using index | | 2 | DEPENDENT SUBQUERY | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 785178 | 10.00 | Using where | +----+--------------------+-------+------------+-------+---------------+------+---------+------+--------+----------+--------------------------+ 2 rows in set, 2 warnings (0.00 sec) Note (Code 1276): Field or reference 't.t1.id' of SELECT #2 was resolved in SELECT #1 Note (Code 1003): /* select#1 */ select count(0) AS `count(*)` from `t`.`t1` where exists(/* select#2 */ select 1 from `t`.`t2` where (`t`.`t2`.`id2` = `t`.`t1`.`id`)) is false
select_
がDEPENDENT SUBQUERY
となっています。
この動作は外側のクエリで抽出された件数分を内側のクエリ
MySQL 8.
mysql> EXPLAIN SELECT count(*) FROM t1 WHERE NOT EXISTS (SELECT 1 FROM t2 WHERE t2.id2=t1.id); +----+--------------+-------------+------------+--------+---------------+------------+---------+---------+--------+----------+-------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+--------------+-------------+------------+--------+---------------+------------+---------+---------+--------+----------+-------------------------+ | 1 | SIMPLE | t1 | NULL | index | NULL | id | 8 | NULL | 785175 | 100.00 | Using index | | 1 | SIMPLE | <subquery2> | NULL | eq_ref | <auto_key> | <auto_key> | 5 | t.t1.id | 1 | 100.00 | Using where; Not exists | | 2 | MATERIALIZED | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 785178 | 100.00 | NULL | +----+--------------+-------------+------------+--------+---------------+------------+---------+---------+--------+----------+-------------------------+ 3 rows in set, 2 warnings (0.00 sec) Note (Code 1276): Field or reference 't.t1.id' of SELECT #2 was resolved in SELECT #1 Note (Code 1003): /* select#1 */ select count(0) AS `count(*)` from `t`.`t1` anti join (`t`.`t2`) on((`t`.`t1`.`id` = `<subquery2>`.`id2`)) where true
Note
部分に注目すると、anti join
と記述されていることがわかります。
Note (Code 1003): /* select#1 */ select count(0) AS `count(*)` from `t`.`t1` anti join (`t`.`t2`) on((`t`.`t1`.`id` = `t`.`t2`.`id2`)) where true
上記の実行計画では、materialization
によるセミジョイン戦略が使用されています。この動作はt2テーブルから内部的に実体化した一時テーブル作成し、
セミジョインとアンチジョインの戦略を調整する方法
セミジョインまたは、
optimizer_switch
MySQ L5.
ヒント句
MySQL5.
強制的にセミジョインまたはアンチジョインを有効にする場合は、/*+ SEMIJOIN(@QB_
を使用します。強制的にセミジョインまたはアンチジョインを無効にする場合は、/*+ NO_
を使用します。
@QB_
mysql> SELECT /*+ NO_SEMIJOIN(@test) */ count(*) FROM t1 WHERE id IN (SELECT /*+ QB_NAME(test) */ id2 FROM t2 );
上記のように、/*+ QB_
のヒント句を記述して、test
という名前をつけました。外側のクエリで/*+ NO_
を記述することで、
また、materialization
戦略を選択しています。
mysql> explain SELECT /*+ SEMIJOIN(@test) */ count(*) FROM t1 WHERE id in (SELECT /*+ QB_NAME(test) */ id2 FROM t2 ); +----+--------------+-------------+------------+--------+---------------+------------+---------+---------+--------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+--------------+-------------+------------+--------+---------------+------------+---------+---------+--------+----------+--------------------------+ | 1 | SIMPLE | t1 | NULL | index | id | id | 8 | NULL | 785175 | 100.00 | Using where; Using index | | 1 | SIMPLE | <subquery2> | NULL | eq_ref | <auto_key> | <auto_key> | 5 | t.t1.id | 1 | 100.00 | Using where | | 2 | MATERIALIZED | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 785179 | 100.00 | NULL | +----+--------------+-------------+------------+--------+---------------+------------+---------+---------+--------+----------+--------------------------+
これをduplicateweedout
戦略を選択するように変更したい場合は、
mysql> explain SELECT /*+ SEMIJOIN(@test DUPSWEEDOUT) */ count(*) FROM t1 WHERE id in (SELECT /*+ QB_NAME(test) */ id2 FROM t2 ); +----+-------------+-------+------------+--------+---------------+------+---------+----------+--------+----------+-----------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+--------+---------------+------+---------+----------+--------+----------+-----------------------------------------+ | 1 | SIMPLE | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 785179 | 100.00 | Using where; Start temporary | | 1 | SIMPLE | t1 | NULL | eq_ref | id | id | 8 | t.t2.id2 | 1 | 100.00 | Using where; Using index; End temporary | +----+-------------+-------+------------+--------+---------------+------+---------+----------+--------+----------+-----------------------------------------+
NO_
有効な戦略名は以下の通りになっています。
- DUPSWEEDOUT … duplicateweedout戦略
- FIRSTMATCH … Fisrtmarch戦略
- LOOSESCAN … LooseScan戦略
- MATERIALIZATION … materialization戦略