MySQL 子查詢優化案例


開發人員給了一個sql ,結構如下delete from B where ID in (select NID from H where guid='xxx');

內部sql滿足條件的結果集只有一條,但是整個刪除操作執行了將近1分鍾,如果是將結果集放在括號里或者將in改為= ,執行的速度可以實現毫秒級別

但是如果內部查詢結果集多於一行,采用第一種方案的話需要更改程序,后來又試了一種更改為join,速度也是極快。

測試表,t1.id上有索引,t2.id無索引

mysql> select * from t1;         mysql> select * from t2;    
+------+------+----------+       +------+---------+         
| id   | name | class_id |       | id   | name    |         
+------+------+----------+       +------+---------+         
|    1 | aa   |     NULL |       |    2 | myname2 |         
|    2 | aa   |     NULL |       |    6 | myname5 |         
|    3 | dd   |     NULL |       +------+---------+         
|    6 | cc   |     NULL |       2 rows in set (0.01 sec)   
+------+------+----------+                                  
4 rows in set (0.00 sec)                                    

 

使用子查詢及改為join后的執行計划

mysql> explain delete from t1 where id in (select id from t2 where name='aa');                                                   
+----+--------------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type        | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+--------------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | DELETE             | t1    | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    4 |   100.00 | Using where |
|  2 | DEPENDENT SUBQUERY | t2    | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    2 |    50.00 | Using where |
+----+--------------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
2 rows in set (0.00 sec)     

mysql> explain delete t1.* from t1 inner join t2 where t1.id=t2.id and t2.name='aa';                                             
+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------------+    
| id | select_type | table | partitions | type | possible_keys | key    | key_len | ref   | rows | filtered | Extra       |    
+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------------+    
|  1 | SIMPLE      | t2    | NULL       | ALL  | NULL          | NULL   | NULL    | NULL  |    2 |    50.00 | Using where |    
|  1 | DELETE      | t1    | NULL       | ref  | idx_id        | idx_id | 5       | const |    1 |   100.00 | Using where |    
+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------------+    
2 rows in set (0.01 sec)  

對於子查詢的執行計划可以看出先對t1進行全表掃描,然后執行select id from t2 where name='aa' and t1.id=t2.id ,如果有值則刪除t.* where id=t1.id

而對於改為join的sql來說,優化器會很智能的選取小表來作為驅動表,然后再走索引刪除t1.* , 而對於子查詢官方文檔解釋為由外向內執行

為了更加直觀的看兩種方式的執行過程,打開回話級別的profiling

mysql> show profiles;
+----------+------------+------------------------------------------------------------------------------+
| Query_ID | Duration   | Query                                                                        |
+----------+------------+------------------------------------------------------------------------------+
|        3 | 0.00137075 | delete from t1 where id in (select id from t2 where name='aa')               |
|        4 | 0.00211725 | explain delete t1.* from t1 inner join t2 where t1.id=t2.id and t2.name='aa' |
|        5 | 0.00132050 | delete t1.* from t1 inner join t2 where t1.id=t2.id and t2.name='aa'         |
+----------+------------+------------------------------------------------------------------------------+

mysql> show profile for query 3                mysql> show profile for query 5                                                                                                               
    -> ;                                           -> ;                                      
+----------------------+----------+            +--------------------------------+----------+ 
| Status               | Duration |            | Status                         | Duration | 
+----------------------+----------+            +--------------------------------+----------+ 
| starting             | 0.000388 |            | starting                       | 0.000360 | 
| checking permissions | 0.000026 |            | checking permissions           | 0.000013 | 
| checking permissions | 0.000008 |            | checking permissions           | 0.000007 | 
| Opening tables       | 0.000105 |            | checking permissions           | 0.000004 | 
| init                 | 0.000152 |            | init                           | 0.000005 | 
| System lock          | 0.000083 |            | Opening tables                 | 0.000048 | 
| updating             | 0.000084 |            | init                           | 0.000048 | 
| optimizing           | 0.000031 |            | deleting from main table       | 0.000022 | 
| statistics           | 0.000083 |            | System lock                    | 0.000028 | 
| preparing            | 0.000052 |            | optimizing                     | 0.000043 | 
| executing            | 0.000013 |            | statistics                     | 0.000144 | 
| Sending data         | 0.000114 |            | preparing                      | 0.000144 | 
| executing            | 0.000009 |            | executing                      | 0.000009 | 
| Sending data         | 0.000017 |            | Sending data                   | 0.000246 | 
| executing            | 0.000005 |            | deleting from reference tables | 0.000073 | 
| Sending data         | 0.000019 |            | end                            | 0.000012 | 
| executing            | 0.000006 |            | end                            | 0.000010 | 
| Sending data         | 0.000018 |            | query end                      | 0.000016 | 
| end                  | 0.000019 |            | closing tables                 | 0.000015 | 
| query end            | 0.000020 |            | freeing items                  | 0.000037 | 
| closing tables       | 0.000021 |            | cleaning up                    | 0.000039 | 
| freeing items        | 0.000054 |            +--------------------------------+----------+ 
| cleaning up          | 0.000046 |            21 rows in set, 1 warning (0.00 sec)          
+----------------------+----------+          
23 rows in set, 1 warning (0.01 sec)         

我第一眼關注的是兩條語句senting data的次數,子查詢對應的sending data是4次,子查詢先對外部表進行全表掃描,結果集是4行,然后進行循環遍歷拿出每一行與內部查詢進行關聯,共執行了4次內部查詢,並且每次都對內部查詢的結果集做一下判斷是否有值,如果有值則再進行刪除

小小的記錄一下,在優化器的探索之路上慢慢爬

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM