開發人員給了一個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次內部查詢,並且每次都對內部查詢的結果集做一下判斷是否有值,如果有值則再進行刪除
小小的記錄一下,在優化器的探索之路上慢慢爬