ãã®èšäºã¯ããMySQL Advent Calendar 2022ãã®13æ¥ç®ã®èšäºã§ãã qiita.com æ ªåŒäŒç€Ÿãšã¹ã»ãšã ã»ãšã¹ã§ãšã³ãžãã¢ãããŠãã @koma_koma_d ã§ããä»åã¯MySQLã«ãããã»ããžã§ã€ã³æé©åã«ã€ããŠèª¿ã¹ãå
å®¹ãæžããŸãã â»èšèŒå
容ã«èª€ããªã©ãããå Žåã¯çè
ã®Twitterå®ã«é£çµ¡ãããã ãããšå¹žãã§ãã å眮ã ãã®èšäºã§æžãããš ãã®èšäºã§ã¯ãMySQLã«ãããã»ããžã§ã€ã³æé©åã«ã€ããŠããµã³ãã«ããŒãã«ãçšããå®è¡äŸã瀺ããªããã ã»ããžã§ã€ã³æé©åãšã¯äœã ã»ããžã§ã€ã³æé©åã¯ãªãæçšã ã»ããžã§ã€ã³æé©åã«ã¯ã©ã®ãããªçš®é¡ãããã®ã ã©ã®ããã«ã¯ãšãªãããŒãã«å®çŸ©ãå€ãããšæŠç¥ãå€åããã ã©ã®ãããªå®è¡èšç»ã«ãªãã ã©ã®ãããªãªããã£ãã€ã¶ãã¬ãŒã¹ã«ãªãã ãªã©ã玹ä»ããŸãã å·çã«ããã£ãŠãWebäžã®ãªãœãŒã¹ãªã©ãããçšåºŠèª¿ã¹ãŸããããèªåãæ°ã«ãªã£ãäžèšã®ãããªå
容ãç¶²çŸ
ããŠãããã®ãèŠåœãããªãã£ãã®ã§ã誰ãã®åœ¹ã«ç«ã€ãããšæã£ãŠæžããŠããŸãã ãã®èšäºã§æžããªãããš MySQLã®å
¬åŒããã¥ã¡ã³ããèªãã ãã§ãããããš å
¬åŒããã¥ã¡ã³ããèªãã ãã§ãããããšã«ã€ããŠã¯ããã®èšäºã§ã¯æžããŸããïŒå¿
èŠã«å¿ããŠå
¬åŒããã¥ã¡ã³ãããåŒçšãããå Žåã¯ãããŸãïŒã MySQL :: MySQL 8.0 リファレンスマニュアル :: 8.2.2 サブクエリー、導出テーブル、ビュー参照および共通テーブル式の最適化 MySQL :: MySQL 8.0 Reference Manual :: 8.2.2.1 Optimizing IN and EXISTS Subquery Predicates with Semijoin Transformations ã³ãŒããªãŒãã£ã³ã°ã«åºã¥ããç¥èŠ MySQLã®ã³ãŒãã¯GitHubã§å
¬éãããŠããã®ã§ãã³ãŒããèªãããšã§åäœãèªã¿è§£ãããšãå¯èœã§ã¯ãããŸãããçè
ã«ã¯ãã®æéãŸã§ã¯ãªãã®ã§ä»åã¯å¯Ÿè±¡å€ã§ãã GitHub - mysql/mysql-server: MySQL Server, the world's most popular open source database, and MySQL Cluster, a real-time, open source transactional database. MySQLã®ããŒãžã§ã³éã®æ¯èŒ MySQLã¯ã»ããžã§ã€ã³æé©åãå°å
¥ãããåŸã鲿©ãç¶ããŠããããã®éçšã§ã»ããžã§ã€ã³æé©åã«é¢é£ããããŒãžã§ã³ã¢ãããè¡ãããŠããããã§ããããããã«ã€ããŠçްããèšåããããšã¯ä»åã®èšäºã§ã¯ããŸããã ä»ã®RDBMSãšã®æ¯èŒ Oracleãªã©ã®ä»ã®RDBMSã«ãé¡äŒŒããæé©åãããããã§ãããããããšã®æ¯èŒã¯ä»åã¯å¯Ÿè±¡å€ãšããŸãã ã»ããžã§ã€ã³æé©åæŠèª¬ å眮ããé·ããªããŸããããæ¬é¡ã«å
¥ã£ãŠãããŸããä»åã®ããŒãã§ããã»ããžã§ã€ã³æé©åã¯ãMySQL 5.6 ãããµãã¯ãšãªã®å®è¡ã«é¢ããæé©åãšããŠå°å
¥ããããã®ã§ãããŸãããªãã»ããžã§ã€ã³æé©åããæé©åãããããã®ããããã©ãŒãã³ã¹çã«å¬ããã®ãã説æããŸãã ãªãã»ããžã§ã€ã³æé©åãããã©ãŒãã³ã¹çã«å¬ããã®ãïŒ æ¬æ¥ããµãã¯ãšãªã¯ã¡ã€ã³ã¯ãšãªã«åŸå±ããŠããããµãã¯ãšãªåŽããã¯ã¡ã€ã³ã¯ãšãªåŽã®ã«ã©ã ãåç
§ã§ããŸãïŒçžé¢ãµãã¯ãšãªã¯ãããå©çšãããã®ïŒããµãã¯ãšãªåŽããã¡ã€ã³ã¯ãšãªåŽã®ã«ã©ã ãåç
§ã§ãããšããããšã¯ãã¡ã€ã³ã¯ãšãªåŽã®ããŒãã«ãå
ã«ã¢ã¯ã»ã¹ããããšããããšã§ãã ããããã»ããžã§ã€ã³æé©åãè¡ããããšãçµåïŒJOINïŒãšããŠåŠçããããšã«ãªãã®ã§ãâ å
ã«ãµãã¯ãšãªåŽã®ããŒãã«ã«ã¢ã¯ã»ã¹ããããšãå¯èœã«ãªããŸãããã®ããããµãã¯ãšãªåŽã®ããŒãã«ã«å
ã«ã¢ã¯ã»ã¹ããæ¹ãå¹çãè¯ãå Žåã«ã¯ãããã©ãŒãã³ã¹äžã®ã¡ãªããã享åã§ããå¯èœæ§ããããŸãã ã詳解MySQL 5.7ã ã§ã¯ä»¥äžã®ããã«èšèŒãããŠããŸãã MySQL 5.6 ã§ã¯ã IN ãµãã¯ãšãªã SEMIJOIN ãšããç¹æ®ãª JOIN ãžãšå€æããããšã§ãããå¹ççãªå®è¡èšç»ãéžæãããããã«ãªã£ããSEMIJOINãšã¯ãé§å衚ã®1è¡ã«å¯ŸããŠå
éšè¡šãããããããè¡ã1è¡ã ãã«ãªããšããç¹æ®ãªçµæãç£ãJOINã§ããã SEMIJOINããªãunique_subqueryãindex_subqueryããåªããŠããããšãããšãããŒãã«ãJOINããé åºãå
¥ãæ¿ããããããã§ããããµãã¯ãšãªå
ã§ã¢ã¯ã»ã¹ãããããŒãã«ãå
ã«ã¢ã¯ã»ã¹ãããããªå®è¡èšç»ã®ã»ããå¹ççãªãã®ã«ãªãã±ãŒã¹ã¯å°ãªããªãã ïŒã詳解MySQL 5.7ãp.110ïŒ www.shoeisha.co.jp æŽã«ãã»ããžã§ã€ã³æé©åãé©çšããã¯ãšãªã¯ãéåžžã®çµåã§ã¯å¿
ãããæºããããŠããªãæ¡ä»¶ã§ããã æçµçãªçµæã«ãµãã¯ãšãªåŽã®ã«ã©ã ãå«ãŸããªã ã¡ã€ã³ã¯ãšãªåŽã®1è¡ã«å¯ŸããŠãµãã¯ãšãªåŽã®è€æ°è¡ãããããããªãïŒãããããããšãèæ
®ããªããŠããïŒ ãšããæ¡ä»¶ãæºãããããâ¡éåžžã®çµåã§ã¯åãããšã®ã§ããªãåŠçã®å¹çåãè¡ãããšãã§ããŸããïŒMySQLã«çŠç¹ãåœãŠãèšè¿°ã§ã¯ãããŸãããïŒ ãSQLå®è·µå
¥éã ã§ã¯ä»¥äžã®ããã«èšèŒãããŠããŸãã ãSemi-Joinãã¯æ¥æ¬èªã§ã¯ãæºçµåããŸãã¯ãåçµåããšåŒã°ããŠããŸããããã¯éåžžã®çµåã®éã«ã¯çŸããªããEXISTSè¿°èªïŒãšINè¿°èªïŒã䜿ã£ããšãã«ç¹æã®ã¢ã«ãŽãªãºã ã§ãã ãã®ã¢ã«ãŽãªãºã ã®ç¹åŸŽã¯æ¬¡ã®2ã€ã§ãã æ©èœçã«ã¯ãçµæã«ã¯é§å衚ãšãªãããŒãã«ã®ããŒã¿ããå«ãŸãããããã1è¡ã«ã€ãå¿
ã1è¡ããçµæãçæãããªãïŒéåžžã®çµåã®å Žåã1察Nã®çµåã®å Žåã¯è¡æ°ãå¢ããããšãããïŒ å
éšè¡šã«ãããããè¡ã1è¡ã§ãçºèŠããæç¹ã§æ®ãã®è¡ã®æ€çŽ¢ãæã¡åãããããéåžžã®çµåãããããã©ãŒãã³ã¹ãè¯ã ïŒãSQLå®è·µå
¥éãp.341ïŒ gihyo.jp 以äžã§èšèŒããã â å
ã«ãµãã¯ãšãªåŽã®ããŒãã«ã«ã¢ã¯ã»ã¹ããããšãå¯èœ â¡éåžžã®çµåã§ã¯åãããšã®ã§ããªãåŠçã®å¹çåãè¡ãããšãã§ãã ãšãã2ã€ã®ç¹æ§ãã©ã®ããã«æŽ»ãããããæ¬¡ã«ç޹ä»ããããããã®ã»ããžã§ã€ã³ã®æŠç¥ã§éã£ãŠããŸãã ã»ããžã§ã€ã³ã«ã¯ã©ã®ãããªãæŠç¥ããããã®ãïŒ ã»ããžã§ã€ã³ã«ã¯ãããã€ãã®çš®é¡ããããŸããMySQLã§ã¯ãããããæŠç¥ïŒStrategyïŒããšè¡šçŸããŠããŸããåæŠç¥ã®ç¹åŸŽã¯ã以äžã®ããã«æŽçã§ããŸãã ãªãã衚äžã®ãéè€ã®é€å»ãã¯ãå
è¿°ã®ãã¡ã€ã³ã¯ãšãªåŽã®1è¡ã«å¯ŸããŠãµãã¯ãšãªåŽã®è€æ°è¡ãããããããªãïŒãããããããšãèæ
®ããªããŠããïŒããšããåŽé¢ã«é¢ãããã®ã§ãã¡ã€ã³ã¯ãšãªåŽã®1è¡ããµãã¯ãšãªçµæãšã®çµåã«ãã£ãŠæçµçãªçµæã®äžã§éè€ããªãçç±ãéè€ãããªãæ¹æ³ãèšèŒããŠããŸãã æŠç¥ã®åç§° ãµãã¯ãšãªåŽã®çµåããŒåã®INDEXã®å¿
èŠæ§ é§å衚ãšå
éšè¡š éè€ã®é€å» Table Pullout UNIQUEå¶çŽå¿
èŠ å¯å€ UNIQUEå¶çŽã«ããä¿èšŒ LooseScan å¿
èŠ ãµãã¯ãšãªåŽãé§å衚 ã€ã³ããã¯ã¹ã掻çšããªããçµåæã«å®æœ Materialization äžèŠïŒäžæè¡šã§èªåäœæïŒ å¯å€ ãµãã¯ãšãªå®äœåæã«é€å» Duplicate Weedout äžèŠ å¯å€ çµæãè¿ãåã«é€å» FirstMatch äžèŠ ã¡ã€ã³ã¯ãšãªåŽãé§å衚 çµåæã«é€å» 以äžãåå¥ã«è£è¶³èª¬æãå ããŸããèšèŒããŠããå
容ã¯åèè³æã«äŸæ ããŠããã»ãããµã³ãã«ããŒãã«ã䜿ã£ãå®è¡äŸããåããå
容ãèšèŒããŠããŸãã äž»ãªåèè³æã¯ä»¥äžã®2ã€ã§ãã MySQLéæ®è«äŸ¿ã 第43å MySQLã®æºçµåïŒã»ããžã§ã€ã³ïŒã«ã€ã㊠ã»ããžã§ã€ã³ã«ã€ããŠã®èŠªåãªç޹ä»ãé§å衚ãšå
éšè¡šãã©ããªããã¯ãã¡ãã«äž»ã«äŸæ ããã MariaDB 10.6 [æ¥æ¬èª] æé©åãšãã¥ãŒãã³ã° ã»ããžã§ã€ã³å¯åãåããã®æé©å ïŒMySQLã§ã¯ãªãMariaDBã§ããïŒããããã®æŠç¥ãå³ä»ãã§è§£èª¬ãããŠããŠããããããã åæŠç¥ã®ç¹åŸŽ ããããã¯ãäžã§èšèŒãã衚ã®å
å®¹ãæŠç¥ããšã«è£è¶³ããŠãããŸããåæŠç¥ã«ã€ããŠçްããè«ããŠããã«ããã£ãŠããµã³ãã«ããŒãã«ãçšããŠå®éã«å®è¡èšç»ããªããã£ãã€ã¶ãã¬ãŒã¹ãååŸããçµæãéæç€ºããŸããå®è¡èšç»ããªããã£ãã€ã¶ãã¬ãŒã¹ã«ã€ããŠã¯ãå®ç©ã瀺ãã®ãæãåèã«ãªããšæããŸããã®ã§ãåãïŒå®è¡èšç»ã¯å
šéšããªããã£ãã€ã¶ãã¬ãŒã¹ã¯äžéšæç²ïŒã«èŒããŸããããèšäºãé·ããªã£ãŠããŸãã®ã§æããããã§ããŸããå±éãããå Žåã¯ãâ¶ïžè©³çްããšãªã£ãŠãããšãããã¯ãªãã¯ããŠãã ããã ãµã³ãã«ããŒãã«ã®åæ ãµã³ãã«ããŒãã«ã®åææ¡ä»¶ãèšèŒããŠãããŸãã 䜿çšããMySQLã®ããŒãžã§ã³ MySQL 8.0.21 â»çŸæç¹ã§ã®ææ°ã¯ 8.0.31 ã§ããããŸããŸèª¿ã¹ãããšæã£ãã¿ã€ãã³ã°ã§ããŒã«ã«ã«å
¥ã£ãŠããã®ããã®ããŒãžã§ã³ïŒ 以åå人ããã°ã®æ¹ã«æžããèšäº ã®æ€èšŒã§äœ¿ã£ãããŒãžã§ã³ã ã£ãïŒã ã£ãããã«éããŸããã ãµã³ãã«ããŒãã« CREATE TABLE `employee` ( `emp_id` int unsigned NOT NULL AUTO_INCREMENT, `main_floor` int unsigned DEFAULT NULL , `gender` int unsigned DEFAULT NULL , PRIMARY KEY (`emp_id`), ) ENGINE=InnoDB; CREATE TABLE `department` ( `dept_id` int unsigned NOT NULL AUTO_INCREMENT, `main_floor` int unsigned DEFAULT NULL , `dept_name` varchar ( 100 ) DEFAULT NULL , PRIMARY KEY (`dept_id`) ) ENGINE=InnoDB; ããŒã¿å
容ïŒã¯ãªãã¯ã§å±éïŒ employee ããŒãã« emp_id main_floor gender 1 1 1 2 2 2 3 3 3 4 4 1 5 5 2 6 6 3 7 7 1 8 8 2 9 9 3 10 10 1 11 11 2 12 12 3 13 13 1 14 14 2 15 15 3 16 16 1 17 17 2 18 18 3 19 1 1 20 2 2 21 3 3 22 4 1 23 5 2 24 6 3 25 7 1 26 8 2 27 9 3 28 10 1 29 11 2 30 12 3 31 13 1 32 14 2 33 15 3 34 16 1 35 17 2 36 18 3 department ããŒãã« dept_id main_floor dept_name 1 1 Finance 2 2 Legal 3 3 Human Resorces 4 4 Corporate Planning 5 5 Sales 1 6 6 Sales 2 7 7 Accounting 8 8 Development 1 9 9 Development 2 Table Pullout Table Pullout æŠç¥ã¯ã以äžã®ãããªç¹åŸŽãæã¡ãŸãã éåžžã®JOINãšããŠåŠçãã çµåããŒã«UNIQUEå¶çŽïŒPRIMARY KEYå«ãïŒãããå Žåã«å©çšå¯èœ ã¡ã€ã³ã¯ãšãªã®çµæ1è¡ã«å¯ŸããŠãµãã¯ãšãªãã0or1è¡ããè¿ããªãããšãä¿èšŒãããŠããã®ã§ãéåžžã®JOINã«ããŠçµåé åºãå
¥ãæ¿ããŠãã¡ã€ã³ã¯ãšãªåŽã®è¡ãæçµçµæã®äžã§éè€ããããšããªã ãµã³ãã«ããŒãã«ãçšããå®è¡äŸããåããããšã¯ä»¥äžã®éãã§ãã department 衚㮠main_floor åã«UNIQUEå¶çŽãä»äžãããšããéžæããã ãªããã£ãã€ã¶ãã¬ãŒã¹ã® pulled_out_semijoin_tables ãšããé
ç®ã« department 衚ã衚ããŠãã EXPLAIN ANALYZE ãã¿ããšã Remove duplicates from ... ãšããéè€é€å»ã衚ãæ
å ±ããªã ãããåŸè¿°ã®LooseScanãšã®éã SELECT * FROM employee e WHERE e.main_floor IN ( SELECT main_floor FROM department d ) å®è¡èšç» mysql> explain select * from employee e where e.main_floor in ( select main_floor from department d ); +----+-------------+-------+------------+-------+---------------------------+---------------------------+---------+----------------------+------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------------------+---------------------------+---------+----------------------+------+----------+--------------------------+ | 1 | SIMPLE | d | NULL | index | department_main_floor_IDX | department_main_floor_IDX | 5 | NULL | 9 | 100.00 | Using where ; Using index | | 1 | SIMPLE | e | NULL | ref | employee_main_floor_IDX | employee_main_floor_IDX | 5 | sandbox.d.main_floor | 2 | 100.00 | NULL | +----+-------------+-------+------------+-------+---------------------------+---------------------------+---------+----------------------+------+----------+--------------------------+ 2 rows in set, 1 warning ( 0.00 sec) Note (Code 1003 ): /* select#1 */ select `sandbox`.`e`.`emp_id` AS `emp_id`,`sandbox`.`e`.`main_floor` AS `main_floor`,`sandbox`.`e`.`gender` AS `gender` from `sandbox`.`department` `d` join `sandbox`.`employee` `e` where (`sandbox`.`e`.`main_floor` = `sandbox`.`d`.`main_floor`) mysql> explain analyze select * from employee e where e.main_floor in ( select main_floor from department d )\G *************************** 1 . row *************************** EXPLAIN : -> Nested loop inner join (cost= 7.45 rows = 18 ) (actual time = 0.040 .. 0.078 rows = 18 loops= 1 ) -> Filter: (d.main_floor is not null ) (cost= 1.15 rows = 9 ) (actual time = 0.023 .. 0.026 rows = 9 loops= 1 ) -> Index scan on d using department_main_floor_IDX (cost= 1.15 rows = 9 ) (actual time = 0.022 .. 0.024 rows = 9 loops= 1 ) -> Index lookup on e using employee_main_floor_IDX (main_floor=d.main_floor) (cost= 0.52 rows = 2 ) (actual time = 0.005 .. 0.005 rows = 2 loops= 9 ) ãªããã£ãã€ã¶ãã¬ãŒã¹ïŒäžéšæç²ïŒ { "steps" : [ { "join_preparation" : { "select#" : 1, "steps" : [ // äžç¥ { "transformations_to_nested_joins" : { "transformations" : [ "semijoin" ] , "expanded_query" : "/* select#1 */ select `e`.`emp_id` AS `emp_id`,`e`.`main_floor` AS `main_floor`,`e`.`gender` AS `gender` from `employee` `e` semi join (`department` `d`) where ((`e`.`main_floor` = `d`.`main_floor`))" } } ] } } , { "join_optimization" : { "select#" : 1, "steps" : [ // äžç¥ { "pulled_out_semijoin_tables" : [ { "table" : "`department` `d`" , "functionally_dependent" : true } ] } , // äžç¥ ] } } , { "join_explain" : { "select#" : 1, "steps" : [ ] } } ] } LooseScan LooseScan æŠç¥ã¯ä»¥äžã®ãããªç¹åŸŽãæã¡ãŸãã ãµãã¯ãšãªåŽã®ããŒãã«ã®çµåããŒã®ã«ã©ã ã«ã€ã³ããã¯ã¹ãããå Žåã«å©çšå¯èœ â»UNIQUEå¶çŽãã€ããŠããã° Table Pullout ã䜿ããããšã«ãªã ãµãã¯ãšãªåŽã®ã€ã³ããã¯ã¹ãéè€ãé¿ããªããã¹ãã£ã³ããŠãã ãµãã¯ãšãªåŽãå¿
ãé§å衚ã«ãªã éè€ã®é€å»ãããµãã¯ãšãªã®ã€ã³ããã¯ã¹ã䜿ã£ãŠå®çŸãããã ãµã³ãã«ããŒãã«ãçšããå®è¡äŸããåããããšã¯ä»¥äžã®éãã§ãã department 衚㮠main_floor åã«ã€ã³ããã¯ã¹ã远å ãããšãããLooseScan ãåè£ã«äžããããã«ãªã£ã UNIQUEå¶çŽãã€ãããš Table Pullout ãå©çšå¯èœã«ãªã ãªããã£ãã€ã¶ãã¬ãŒã¹ã® final_semijoin_strategy ã LooseScan å®è¡èšç»ã® Extra åã« LooseScan ãšè¡šç€º EXPLAIN ANALYZE ãã¿ããšã Remove duplicates from ... ãšããéè€é€å»ã衚ãæ
å ±ããã ãããTable Pullout ãšã®éãã«ãªã SELECT * FROM employee e WHERE e.main_floor IN ( SELECT main_floor FROM department d ) â» Table Pullout ãšåãã¯ãšãª å®è¡èšç» mysql> explain select * from employee e where e.main_floor in ( select main_floor from department d ); +----+-------------+-------+------------+-------+---------------------------+---------------------------+---------+----------------------+------+----------+-------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------------------+---------------------------+---------+----------------------+------+----------+-------------------------------------+ | 1 | SIMPLE | d | NULL | index | department_main_floor_IDX | department_main_floor_IDX | 5 | NULL | 9 | 100.00 | Using where ; Using index ; LooseScan | | 1 | SIMPLE | e | NULL | ref | employee_main_floor_IDX | employee_main_floor_IDX | 5 | sandbox.d.main_floor | 2 | 100.00 | NULL | +----+-------------+-------+------------+-------+---------------------------+---------------------------+---------+----------------------+------+----------+-------------------------------------+ 2 rows in set, 1 warning ( 0.00 sec) Note (Code 1003 ): /* select#1 */ select `sandbox`.`e`.`emp_id` AS `emp_id`,`sandbox`.`e`.`main_floor` AS `main_floor`,`sandbox`.`e`.`gender` AS `gender` from `sandbox`.`employee` `e` semi join (`sandbox`.`department` `d`) where (`sandbox`.`e`.`main_floor` = `sandbox`.`d`.`main_floor`) mysql> explain analyze select * from employee e where e.main_floor in ( select main_floor from department d )\G *************************** 1 . row *************************** EXPLAIN : -> Nested loop inner join (actual time = 0.109 .. 0.141 rows = 18 loops= 1 ) -> Remove duplicates from input sorted on department_main_floor_IDX (actual time = 0.091 .. 0.095 rows = 9 loops= 1 ) -> Filter: (d.main_floor is not null ) (cost= 1.15 rows = 9 ) (actual time = 0.090 .. 0.093 rows = 9 loops= 1 ) -> Index scan on d using department_main_floor_IDX (cost= 1.15 rows = 9 ) (actual time = 0.089 .. 0.091 rows = 9 loops= 1 ) -> Index lookup on e using employee_main_floor_IDX (main_floor=d.main_floor) (cost= 4.70 rows = 2 ) (actual time = 0.004 .. 0.005 rows = 2 loops= 9 ) ãªããã£ãã€ã¶ãã¬ãŒã¹ïŒäžéšæç²ïŒ { "steps" : [ { "join_preparation" : { "select#" : 1, "steps" : [ // äžç¥ { "transformations_to_nested_joins" : { "transformations" : [ "semijoin" ] , "expanded_query" : "/* select#1 */ select `e`.`emp_id` AS `emp_id`,`e`.`main_floor` AS `main_floor`,`e`.`gender` AS `gender` from `employee` `e` semi join (`department` `d`) where ((`e`.`main_floor` = `d`.`main_floor`))" } } ] } } , { "join_optimization" : { "select#" : 1, "steps" : [ // äžç¥ { "considered_execution_plans" : [ { "plan_prefix" : [ ] , "table" : "`department` `d`" , "best_access_path" : { "considered_access_paths" : [ { "access_type" : "ref" , "index" : "department_main_floor_IDX" , "usable" : false , "chosen" : false } , { "rows_to_scan" : 9, "filtering_effect" : [ ] , "final_filtering_effect" : 1, "access_type" : "scan" , "resulting_rows" : 9, "cost" : 1.15, "chosen" : true } ] } , "condition_filtering_pct" : 100, "rows_for_plan" : 9, "cost_for_plan" : 1.15, "semijoin_strategy_choice" : [ { "strategy" : "MaterializeScan" , "choice" : "deferred" } ] , "rest_of_plan" : [ { "plan_prefix" : [ "`department` `d`" ] , "table" : "`employee` `e`" , "best_access_path" : { "considered_access_paths" : [ { "access_type" : "ref" , "index" : "employee_main_floor_IDX" , "rows" : 2, "cost" : 6.3, "chosen" : true } , { "rows_to_scan" : 36, "filtering_effect" : [ ] , "final_filtering_effect" : 1, "access_type" : "scan" , "using_join_cache" : true , "buffers_needed" : 1, "resulting_rows" : 36, "cost" : 32.65, "chosen" : false } ] } , "condition_filtering_pct" : 100, "rows_for_plan" : 18, "cost_for_plan" : 7.45, "semijoin_strategy_choice" : [ { "strategy" : "LooseScan" , "recalculate_access_paths_and_cost" : { "tables" : [ { "table" : "`department` `d`" , "best_access_path" : { "considered_access_paths" : [ { "access_type" : "ref" , "index" : "department_main_floor_IDX" , "usable" : false , "chosen" : false } , { "rows_to_scan" : 9, "filtering_effect" : [ ] , "final_filtering_effect" : 1, "access_type" : "scan" , "resulting_rows" : 9, "cost" : 1.15, "chosen" : true } ] } , "unknown_key_1" : { "searching_loose_scan_index" : { "indexes" : [ { "index" : "department_main_floor_IDX" , "covering_scan" : { "cost" : 0.2522, "chosen" : true } } ] } } } ] } , "cost" : 7.4522, "rows" : 2, "chosen" : true } , { "strategy" : "MaterializeScan" , "recalculate_access_paths_and_cost" : { "tables" : [ { "table" : "`employee` `e`" , "best_access_path" : { "considered_access_paths" : [ { "access_type" : "ref" , "index" : "employee_main_floor_IDX" , "rows" : 2, "cost" : 6.3, "chosen" : true } , { "rows_to_scan" : 36, "filtering_effect" : [ ] , "final_filtering_effect" : 1, "access_type" : "scan" , "using_join_cache" : true , "buffers_needed" : 1, "resulting_rows" : 36, "cost" : 32.65, "chosen" : false } ] } } ] } , "cost" : 10.25, "rows" : 2, "duplicate_tables_left" : false , "chosen" : false } , { "strategy" : "DuplicatesWeedout" , "cost" : 12.05, "rows" : 18, "duplicate_tables_left" : false , "chosen" : false } ] , "chosen" : true } ] } , { "plan_prefix" : [ ] , "table" : "`employee` `e`" , "best_access_path" : { "considered_access_paths" : [ { "access_type" : "ref" , "index" : "employee_main_floor_IDX" , "usable" : false , "chosen" : false } , { "rows_to_scan" : 36, "filtering_effect" : [ ] , "final_filtering_effect" : 1, "access_type" : "scan" , "resulting_rows" : 36, "cost" : 3.85, "chosen" : true } ] } , "condition_filtering_pct" : 100, "rows_for_plan" : 36, "cost_for_plan" : 3.85, "semijoin_strategy_choice" : [ ] , "rest_of_plan" : [ { "plan_prefix" : [ "`employee` `e`" ] , "table" : "`department` `d`" , "best_access_path" : { "considered_access_paths" : [ { "access_type" : "ref" , "index" : "department_main_floor_IDX" , "rows" : 1, "cost" : 12.6, "chosen" : true } , { "access_type" : "scan" , "chosen" : false , "cause" : "covering_index_better_than_full_scan" } ] } , "condition_filtering_pct" : 100, "rows_for_plan" : 36, "cost_for_plan" : 16.45, "semijoin_strategy_choice" : [ { "strategy" : "FirstMatch" , "recalculate_access_paths_and_cost" : { "tables" : [ ] } , "cost" : 16.45, "rows" : 36, "chosen" : true } , { "strategy" : "MaterializeLookup" , "cost" : 10.5, "rows" : 36, "duplicate_tables_left" : false , "chosen" : true } , { "strategy" : "DuplicatesWeedout" , "cost" : 24.65, "rows" : 36, "duplicate_tables_left" : false , "chosen" : false } ] , "pruned_by_cost" : true } ] } , { "final_semijoin_strategy" : "LooseScan" , // äžç¥ } } ] } , //äžç¥ ] } } , { "join_explain" : { "select#" : 1, "steps" : [ ] } } ] } Materialization Materialization æŠç¥ã¯ä»¥äžã®ç¹åŸŽãæã¡ãŸãã ãµãã¯ãšãªã®çµæãå®äœåããŠãçµåããŒã«ã€ã³ããã¯ã¹ãäœæããŠéè€ãåãé€ããŠããã¡ã€ã³ã¯ãšãªãšJOINãã â»çµåããŒã®ã«ã©ã ã«ã€ã³ããã¯ã¹ãããã° LooseScan ã䜿ããããUNIQUEå¶çŽãã€ããŠããã° Table Pullout ã䜿ãã ãã ããã³ã¹ã次第ã§ä»ã®æŠç¥ã§ã¯ãªããã¡ããéžæãããããšã¯ãã ããšãã°ããµãã¯ãšãªã§Whereå¥ã§ã®çµã蟌ã¿ãããŠããŠãçµã蟌ã¿åŸã«å®äœåãã Materialization ã®æ¹ããLooseScanïŒJOINæã«çµã蟌ã¿ãè¡ãïŒãããã³ã¹ããäœããªãã±ãŒã¹ãªã© äœæãããã€ã³ããã¯ã¹ã®ããŒã <auto_key> ãšãã圢ã§å®è¡èšç»ã«çŸããããšããã ã¡ã€ã³ã¯ãšãªåŽãšãµãã¯ãšãªåŽã®ã©ã¡ããé§å衚ãå
éšè¡šãšãªããã¯ã³ã¹ãã§æ±ºãŸã å®äœåããããµãã¯ãšãªåŽãå
éšè¡šã«ãªãå Žåã MaterializeLookup å®äœåããããµãã¯ãšãªåŽãé§å衚ã«ãªãå Žåã MaterializeScan åèïŒ MySQL: Query Optimizer 4.MaterializeLookup (Materialize inner tables, then setup a scan over outer correlated tables, lookup in materialized table) 5.MaterializeScan (Materialize inner tables, then setup a scan over materialized tables, perform lookup in outer tables) ãµã³ãã«ããŒãã«ãçšããå®è¡äŸããåããããšã¯ä»¥äžã®éãã§ãã ãããŸã§ã®ã¯ãšãªã®ãµãã¯ãšãªã«WHEREå¥ã远å ãããšããéžæããã ãªããã£ãã€ã¶ãã¬ãŒã¹ã® final_semijoin_strategy ã MaterializeScan ä»åã¯ãµãã¯ãšãªåŽã®æ¹ãé§å衚ã«ãªã£ããã¿ãŒã³ å®è¡èšç»ã® select_type ã« MATERIALIZED ãšè¡šç€º SELECT * FROM employee e WHERE e.main_floor IN ( SELECT main_floor FROM department d WHERE d.dept_name LIKE ' Sales% ' ) å®è¡èšç» mysql> Explain select * from employee e where e.main_floor in ( select main_floor from department d where d.dept_name like 'Sales%' ); +----+--------------+-------------+------------+------+-------------------------+-------------------------+---------+------------------------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+--------------+-------------+------------+------+-------------------------+-------------------------+---------+------------------------+------+----------+-------------+ | 1 | SIMPLE | <subquery2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | 100.00 | Using where | | 1 | SIMPLE | e | NULL | ref | employee_main_floor_IDX | employee_main_floor_IDX | 5 | <subquery2>.main_floor | 2 | 100.00 | NULL | | 2 | MATERIALIZED | d | NULL | ALL | NULL | NULL | NULL | NULL | 9 | 11.11 | Using where | +----+--------------+-------------+------------+------+-------------------------+-------------------------+---------+------------------------+------+----------+-------------+ 3 rows in set, 1 warning ( 0.00 sec) Note (Code 1003 ): /* select#1 */ select `sandbox`.`e`.`emp_id` AS `emp_id`,`sandbox`.`e`.`main_floor` AS `main_floor`,`sandbox`.`e`.`gender` AS `gender` from `sandbox`.`employee` `e` semi join (`sandbox`.`department` `d`) where ((`sandbox`.`e`.`main_floor` = `<subquery2>`.`main_floor`) and (`sandbox`.`d`.`dept_name` like 'Sales%' )) ãªããã£ãã€ã¶ãã¬ãŒã¹ïŒäžéšæç²ïŒ { "steps" : [ { "join_preparation" : { "select#" : 1, "steps" : [ // äžç¥ { "transformations_to_nested_joins" : { "transformations" : [ "semijoin" ] , "expanded_query" : "/* select#1 */ select `e`.`emp_id` AS `emp_id`,`e`.`main_floor` AS `main_floor`,`e`.`gender` AS `gender` from `employee` `e` semi join (`department` `d`) where ((`d`.`dept_name` like 'Sales%') and (`e`.`main_floor` = `d`.`main_floor`))" } } ] } } , { "join_optimization" : { "select#" : 1, "steps" : [ // äžç¥ { "considered_execution_plans" : [ { "plan_prefix" : [ ] , "table" : "`department` `d`" , "best_access_path" : { "considered_access_paths" : [ { "rows_to_scan" : 9, "filtering_effect" : [ ] , "final_filtering_effect" : 0.1111, "access_type" : "scan" , "resulting_rows" : 1, "cost" : 1.15, "chosen" : true } ] } , "condition_filtering_pct" : 100, "rows_for_plan" : 1, "cost_for_plan" : 1.15, "semijoin_strategy_choice" : [ { "strategy" : "MaterializeScan" , "choice" : "deferred" } ] , "rest_of_plan" : [ { "plan_prefix" : [ "`department` `d`" ] , "table" : "`employee` `e`" , "best_access_path" : { "considered_access_paths" : [ { "access_type" : "ref" , "index" : "employee_main_floor_IDX" , "rows" : 2, "cost" : 0.7, "chosen" : true } , { "rows_to_scan" : 36, "filtering_effect" : [ ] , "final_filtering_effect" : 1, "access_type" : "scan" , "using_join_cache" : true , "buffers_needed" : 1, "resulting_rows" : 36, "cost" : 3.8503, "chosen" : false } ] } , "condition_filtering_pct" : 100, "rows_for_plan" : 2, "cost_for_plan" : 1.85, "semijoin_strategy_choice" : [ { "strategy" : "MaterializeScan" , "recalculate_access_paths_and_cost" : { "tables" : [ { "table" : "`employee` `e`" , "best_access_path" : { "considered_access_paths" : [ { "access_type" : "ref" , "index" : "employee_main_floor_IDX" , "rows" : 2, "cost" : 0.7, "chosen" : true } , { "rows_to_scan" : 36, "filtering_effect" : [ ] , "final_filtering_effect" : 1, "access_type" : "scan" , "using_join_cache" : true , "buffers_needed" : 1, "resulting_rows" : 36, "cost" : 3.8503, "chosen" : false } ] } } ] } , "cost" : 3.05, "rows" : 2, "duplicate_tables_left" : true , "chosen" : true } , { "strategy" : "DuplicatesWeedout" , "cost" : 3.25, "rows" : 2, "duplicate_tables_left" : false , "chosen" : false } ] , "chosen" : true } ] } , { "plan_prefix" : [ ] , "table" : "`employee` `e`" , "best_access_path" : { "considered_access_paths" : [ { "access_type" : "ref" , "index" : "employee_main_floor_IDX" , "usable" : false , "chosen" : false } , { "rows_to_scan" : 36, "filtering_effect" : [ ] , "final_filtering_effect" : 1, "access_type" : "scan" , "resulting_rows" : 36, "cost" : 3.85, "chosen" : true } ] } , "condition_filtering_pct" : 100, "rows_for_plan" : 36, "cost_for_plan" : 3.85, "semijoin_strategy_choice" : [ ] , "pruned_by_cost" : true } , { "final_semijoin_strategy" : "MaterializeScan" , // äžç¥ } ] } , // äžç¥ ] } } , { "join_explain" : { "select#" : 1, "steps" : [ ] } } ] } Duplicate Weedout Duplicate Weedout ã«ã¯ä»¥äžã®ç¹åŸŽããããŸãã JOINããŠãããäžæããŒãã«ãäœæããŠéè€ãåãé€ã ã¡ã€ã³ã¯ãšãªã®1è¡ã«å¯ŸããŠãµãã¯ãšãªã®çµæãè€æ°è¡ãããããå Žåã«ã¯ãJOINã§åŠçããããšã«ãã£ãŠéè€ãçºçããã®ã§ããããåãé€ããšããæŠç¥ JOINæã«ã¡ã€ã³ã¯ãšãªãšãµãã¯ãšãªã®ã©ã¡ããé§å衚ã»å
éšè¡šãšãªããã¯ã³ã¹ãã«ãã£ãŠæ±ºãŸãïŒã©ã¡ããšãããããïŒ éè€ã®é€å»ãæåŸã«ããã®ã§ãJOINã¯ã©ã¡ããé§å衚ãšããŠè¡ã£ãŠããããã ãµã³ãã«ããŒãã«ãçšããå®è¡äŸããåããããšã¯ä»¥äžã®éãã§ãã Materialization æŠç¥ãéžæããããšãã®ã¯ãšãªãããŒã¹ãšããŠãã¡ã€ã³ã¯ãšãªåŽã« AND ã§æ¡ä»¶ãè¶³ããŠãã final_semijoin_strategy ã DuplicateWeedout å®è¡èšç»ã® Extra åã« Start temporary ãš End temporary ãšè¡šç€º EXPLAIN ANALYZE ã§ã¯äžçªäžã« Remove duplicate e rows using temporary table (weedout) ãšè¡šç€º éè€ã®é€å»ãäžæããŒãã«ãçšããŠå®æœããŠãã SELECT * FROM employee e WHERE e.main_floor IN ( SELECT main_floor FROM department d WHERE d.dept_name LIKE ' Sales% ' ) AND gender = 2 ; å®è¡èšç» mysql> explain select * from employee e where e.main_floor in ( select main_floor from department d where d.dept_name like 'Sales%' ) and gender = 2 ; +----+-------------+-------+------------+------+-------------------------+-------------------------+---------+----------------------+------+----------+------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+-------------------------+-------------------------+---------+----------------------+------+----------+------------------------------+ | 1 | SIMPLE | d | NULL | ALL | NULL | NULL | NULL | NULL | 9 | 11.11 | Using where ; Start temporary | | 1 | SIMPLE | e | NULL | ref | employee_main_floor_IDX | employee_main_floor_IDX | 5 | sandbox.d.main_floor | 2 | 10.00 | Using where ; End temporary | +----+-------------+-------+------------+------+-------------------------+-------------------------+---------+----------------------+------+----------+------------------------------+ 2 rows in set, 1 warning ( 0.00 sec) Note (Code 1003 ): /* select#1 */ select `sandbox`.`e`.`emp_id` AS `emp_id`,`sandbox`.`e`.`main_floor` AS `main_floor`,`sandbox`.`e`.`gender` AS `gender` from `sandbox`.`employee` `e` semi join (`sandbox`.`department` `d`) where ((`sandbox`.`e`.`main_floor` = `sandbox`.`d`.`main_floor`) and (`sandbox`.`e`.`gender` = 2 ) and (`sandbox`.`d`.`dept_name` like 'Sales%' )) mysql> explain analyze select * from employee e where e.main_floor in ( select main_floor from department d where d.dept_name like 'Sales%' ) and gender = 2 \G *************************** 1 . row *************************** EXPLAIN : -> Remove duplicate e rows using temporary table (weedout) (cost= 1.85 rows = 0 ) (actual time = 0.051 .. 0.062 rows = 2 loops= 1 ) -> Nested loop inner join (cost= 1.85 rows = 0 ) (actual time = 0.047 .. 0.057 rows = 2 loops= 1 ) -> Filter: ((d.dept_name like 'Sales%' ) and (d.main_floor is not null )) (cost= 1.15 rows = 1 ) (actual time = 0.027 .. 0.030 rows = 2 loops= 1 ) -> Table scan on d (cost= 1.15 rows = 9 ) (actual time = 0.022 .. 0.025 rows = 9 loops= 1 ) -> Filter: (e.gender = 2 ) (cost= 0.52 rows = 0 ) (actual time = 0.011 .. 0.012 rows = 1 loops= 2 ) -> Index lookup on e using employee_main_floor_IDX (main_floor=d.main_floor) (cost= 0.52 rows = 2 ) (actual time = 0.011 .. 0.012 rows = 2 loops= 2 ) ãªããã£ãã€ã¶ãã¬ãŒã¹ïŒäžéšæç²ïŒ { "steps" : [ { "join_preparation" : { "select#" : 1, "steps" : [ // äžç¥ { "transformations_to_nested_joins" : { "transformations" : [ "semijoin" ] , "expanded_query" : "/* select#1 */ select `e`.`emp_id` AS `emp_id`,`e`.`main_floor` AS `main_floor`,`e`.`gender` AS `gender` from `employee` `e` semi join (`department` `d`) where ((`e`.`gender` = 2) and (`d`.`dept_name` like 'Sales%') and (`e`.`main_floor` = `d`.`main_floor`))" } } ] } } , { "join_optimization" : { "select#" : 1, "steps" : [ // äžç¥ { "considered_execution_plans" : [ { "plan_prefix" : [ ] , "table" : "`department` `d`" , "best_access_path" : { "considered_access_paths" : [ { "rows_to_scan" : 9, "filtering_effect" : [ ] , "final_filtering_effect" : 0.1111, "access_type" : "scan" , "resulting_rows" : 1, "cost" : 1.15, "chosen" : true } ] } , "condition_filtering_pct" : 100, "rows_for_plan" : 1, "cost_for_plan" : 1.15, "semijoin_strategy_choice" : [ { "strategy" : "MaterializeScan" , "choice" : "deferred" } ] , "rest_of_plan" : [ { "plan_prefix" : [ "`department` `d`" ] , "table" : "`employee` `e`" , "best_access_path" : { "considered_access_paths" : [ { "access_type" : "ref" , "index" : "employee_main_floor_IDX" , "rows" : 2, "cost" : 0.7, "chosen" : true } , { "rows_to_scan" : 36, "filtering_effect" : [ ] , "final_filtering_effect" : 0.1, "access_type" : "scan" , "using_join_cache" : true , "buffers_needed" : 1, "resulting_rows" : 3.6, "cost" : 3.8541, "chosen" : false } ] } , "condition_filtering_pct" : 10, "rows_for_plan" : 0.2, "cost_for_plan" : 1.85, "semijoin_strategy_choice" : [ { "strategy" : "MaterializeScan" , "recalculate_access_paths_and_cost" : { "tables" : [ { "table" : "`employee` `e`" , "best_access_path" : { "considered_access_paths" : [ { "access_type" : "ref" , "index" : "employee_main_floor_IDX" , "rows" : 2, "cost" : 0.7, "chosen" : true } , { "rows_to_scan" : 36, "filtering_effect" : [ ] , "final_filtering_effect" : 0.1, "access_type" : "scan" , "using_join_cache" : true , "buffers_needed" : 1, "resulting_rows" : 3.6, "cost" : 3.8541, "chosen" : false } ] } } ] } , "cost" : 3.05, "rows" : 0.2, "duplicate_tables_left" : true , "chosen" : true } , { "strategy" : "DuplicatesWeedout" , "cost" : 2.89, "rows" : 0.2, "duplicate_tables_left" : false , "chosen" : true } ] , "chosen" : true } ] } , { "plan_prefix" : [ ] , "table" : "`employee` `e`" , "best_access_path" : { "considered_access_paths" : [ { "access_type" : "ref" , "index" : "employee_main_floor_IDX" , "usable" : false , "chosen" : false } , { "rows_to_scan" : 36, "filtering_effect" : [ ] , "final_filtering_effect" : 0.1, "access_type" : "scan" , "resulting_rows" : 3.6, "cost" : 3.85, "chosen" : true } ] } , "condition_filtering_pct" : 100, "rows_for_plan" : 3.6, "cost_for_plan" : 3.85, "semijoin_strategy_choice" : [ ] , "pruned_by_cost" : true } , { "final_semijoin_strategy" : "DuplicateWeedout" } ] } , // äžç¥ ] } } , { "join_execution" : { "select#" : 1, "steps" : [ ] } } ] } FirstMatch FirstMatch æŠç¥ã«ã¯ä»¥äžã®ç¹åŸŽããããŸãã éåžžã®NLJïŒNested Loop JoinïŒã«äŒŒãŠããããå
åŽã®ïŒãµãã¯ãšãªåŽã®ïŒã«ãŒããåããŠãããšãã«1è¡ã§ãèŠã€ãã£ããå³åº§ã«å
åŽã®ã«ãŒããæã¡åã£ãŠå€åŽã®ã«ãŒãã®æ¬¡ã®åšåã«é²ãããšãã§ããããå¹çãè¯ã ã¡ã€ã³ã¯ãšãªåŽãå¿
ãé§å衚ã«ãªã éè€ã®é€å»ããå
åŽã®ã«ãŒããéäžã§æã¡åãããšã«ãã£ãŠå®çŸããŠãããã ãµã³ãã«ããŒãã«ãçšããå®è¡äŸããåããããšã¯ä»¥äžã®éãã§ãã ãããŸã§ã®ã¯ãšãªãšã¯éããdepartment衚ãžã®ã¯ãšãªãã¡ã€ã³ã¯ãšãªãšããŠãã ãããŸã§ã¯ããµãã¯ãšãªã®ããŒãã«ã®æ¹ãå°ããã£ãããä»åã®ã¯ãšãªã¯ã¡ã€ã³ã¯ãšãªã®ããŒãã«ã®æ¹ãå°ãã ãªããã£ãã€ã¶ãã¬ãŒã¹ã® final_semijoin_strategy ã FirstMatch å®è¡èšç»ã® Extra åã« FirstMatch(department) ã®è¡šç€ºããã SELECT * FROM department WHERE department.main_floor IN ( SELECT main_floor FROM employee ); å®è¡èšç» mysql> explain select * from department where department.main_floor in ( select main_floor from employee ); +----+-------------+------------+------------+------+-------------------------+-------------------------+---------+-------------------------------+------+----------+-------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+------------+------------+------+-------------------------+-------------------------+---------+-------------------------------+------+----------+-------------------------------------+ | 1 | SIMPLE | department | NULL | ALL | NULL | NULL | NULL | NULL | 9 | 100.00 | Using where | | 1 | SIMPLE | employee | NULL | ref | employee_main_floor_IDX | employee_main_floor_IDX | 5 | sandbox.department.main_floor | 2 | 100.00 | Using index ; FirstMatch(department) | +----+-------------+------------+------------+------+-------------------------+-------------------------+---------+-------------------------------+------+----------+-------------------------------------+ 2 rows in set, 1 warning ( 0.00 sec) Note (Code 1003 ): /* select#1 */ select `sandbox`.`department`.`dept_id` AS `dept_id`,`sandbox`.`department`.`main_floor` AS `main_floor`,`sandbox`.`department`.`dept_name` AS `dept_name` from `sandbox`.`department` semi join (`sandbox`.`employee`) where (`sandbox`.`employee`.`main_floor` = `sandbox`.`department`.`main_floor`) mysql> explain analyze select * from department where department.main_floor in ( select main_floor from employee )\G *************************** 1 . row *************************** EXPLAIN : -> Nested loop semijoin (cost= 5.20 rows = 18 ) (actual time = 0.034 .. 0.050 rows = 9 loops= 1 ) -> Filter: (department.main_floor is not null ) (cost= 1.15 rows = 9 ) (actual time = 0.022 .. 0.026 rows = 9 loops= 1 ) -> Table scan on department (cost= 1.15 rows = 9 ) (actual time = 0.022 .. 0.025 rows = 9 loops= 1 ) -> Index lookup on employee using employee_main_floor_IDX (main_floor=department.main_floor) (cost= 0.54 rows = 2 ) (actual time = 0.002 .. 0.002 rows = 1 loops= 9 ) ãªããã£ãã€ã¶ãã¬ãŒã¹ïŒäžéšæç²ïŒ { "steps" : [ { "join_preparation" : { "select#" : 1, "steps" : [ // äžç¥ { "transformations_to_nested_joins" : { "transformations" : [ "semijoin" ] , "expanded_query" : "/* select#1 */ select `department`.`dept_id` AS `dept_id`,`department`.`main_floor` AS `main_floor`,`department`.`dept_name` AS `dept_name` from `department` semi join (`employee`) where ((`department`.`main_floor` = `employee`.`main_floor`))" } } ] } } , { "join_optimization" : { "select#" : 1, "steps" : [ // äžç¥ { "considered_execution_plans" : [ { "plan_prefix" : [ ] , "table" : "`department`" , "best_access_path" : { "considered_access_paths" : [ { "rows_to_scan" : 9, "filtering_effect" : [ ] , "final_filtering_effect" : 1, "access_type" : "scan" , "resulting_rows" : 9, "cost" : 1.15, "chosen" : true } ] } , "condition_filtering_pct" : 100, "rows_for_plan" : 9, "cost_for_plan" : 1.15, "semijoin_strategy_choice" : [ ] , "rest_of_plan" : [ { "plan_prefix" : [ "`department`" ] , "table" : "`employee`" , "best_access_path" : { "considered_access_paths" : [ { "access_type" : "ref" , "index" : "employee_main_floor_IDX" , "rows" : 2, "cost" : 4.0525, "chosen" : true } , { "access_type" : "scan" , "chosen" : false , "cause" : "covering_index_better_than_full_scan" } ] } , "condition_filtering_pct" : 100, "rows_for_plan" : 18, "cost_for_plan" : 5.2025, "semijoin_strategy_choice" : [ { "strategy" : "FirstMatch" , "recalculate_access_paths_and_cost" : { "tables" : [ ] } , "cost" : 5.2025, "rows" : 9, "chosen" : true } , { "strategy" : "MaterializeLookup" , "cost" : 10.5, "rows" : 9, "duplicate_tables_left" : false , "chosen" : false } , { "strategy" : "DuplicatesWeedout" , "cost" : 8.9025, "rows" : 9, "duplicate_tables_left" : false , "chosen" : false } ] , "chosen" : true } ] } , { "plan_prefix" : [ ] , "table" : "`employee`" , "best_access_path" : { "considered_access_paths" : [ { "access_type" : "ref" , "index" : "employee_main_floor_IDX" , "usable" : false , "chosen" : false } , { "rows_to_scan" : 36, "filtering_effect" : [ ] , "final_filtering_effect" : 1, "access_type" : "scan" , "resulting_rows" : 36, "cost" : 3.85, "chosen" : true } ] } , "condition_filtering_pct" : 100, "rows_for_plan" : 36, "cost_for_plan" : 3.85, "semijoin_strategy_choice" : [ { "strategy" : "MaterializeScan" , "choice" : "deferred" } ] , "pruned_by_heuristic" : true } , { "final_semijoin_strategy" : "FirstMatch" , "recalculate_access_paths_and_cost" : { "tables" : [ ] } } ] } , // äžç¥ ] } } , { "join_execution" : { "select#" : 1, "steps" : [ ] } } ] } çµãã㫠以äžãã»ããžã§ã€ã³æé©åã«ã€ããŠãåã
ã®æŠç¥ã®å
容ãå«ããŠæžããŠããŸããããµã³ãã«ããŒãã«ãçšããŠååŸããå®è¡èšç»ããªããã£ãã€ã¶ãã¬ãŒã¹ã¯ãããŸã§äžäŸã«éãããå°ãæ¡ä»¶ãå€ãããšãŸãéã£ãçµæãåŸããããããããŸãããåé ã«ãæžããããã«ãèšèŒããŠããå
容ã«èª€ããªã©ãèŠã€ããå Žå㯠çè
ã®Twitter ãŸã§ãäžå ±ããã ãããšå¹žãã§ãã åèè³æ å
¬åŒ 5.6 8.2.1.18 ãµãã¯ãšãªãŒã®æé©å 8.2.2.1 Optimizing Subqueries with Semijoin Transformations 8.2.2.2 Optimizing Subqueries with Materialization 8.8.5.2 åãæ¿ãå¯èœãªæé©åã®å¶åŸ¡ 8.0 8.2.2.1 Optimizing IN and EXISTS Subquery Predicates with Semijoin Transformations 8.9.3 ãªããã£ãã€ã¶ãã³ã MySQL 8.0.30 Source Code Documentation ãã®ä» MySQLéæ®è«äŸ¿ã 第43å MySQLã®æºçµåïŒã»ããžã§ã€ã³ïŒã«ã€ã㊠MariaDB 10.6 [æ¥æ¬èª] æé©åãšãã¥ãŒãã³ã° ã»ããžã§ã€ã³å¯åãåããã®æé©å INãšEXISTSã¯ã©ã¡ããéãã®ãïŒ MySQL ã®ãµãã¯ãšãªã£ãŠãã»ããšã«é
ãã®ïŒ MySQL 8.0.21 ã§ã¯ Multi-Table Trick ãå¿
èŠãªããªã£ãããã MySQLéæ®è«äŸ¿ã第103åMySQL 8.0ã®ã»ããžã§ã€ã³ã®å€æŽç¹