为啥 MySQL DELETE 无法对子查询使用索引?

Posted

技术标签:

【中文标题】为啥 MySQL DELETE 无法对子查询使用索引?【英文标题】:Why MySQL DELETE is unable to use indexes on sub-queries?为什么 MySQL DELETE 无法对子查询使用索引? 【发布时间】:2016-12-03 03:54:37 【问题描述】:

正如一些other questions 在这里建立的,在 mysql 删除中使用“子查询”会导致它变慢,而相同的“选择”查询执行得很快:

MariaDB [as_01_import]> explain select * from invoice_payment where invoice_id in (select id from dochead where system_id = 5786);
+------+-------------+-----------------+------+---------------------------------------+----------------------------+---------+-------------------------+------+-------------+
| id   | select_type | table           | type | possible_keys                         | key                        | key_len | ref                     | rows | Extra       |
+------+-------------+-----------------+------+---------------------------------------+----------------------------+---------+-------------------------+------+-------------+
|    1 | PRIMARY     | dochead         | ref  | PRIMARY,dochead_system_id             | dochead_system_id          | 4       | const                   |  891 | Using index |
|    1 | PRIMARY     | invoice_payment | ref  | invoice_payment_invoice_fk,invoice_id | invoice_payment_invoice_fk | 4       | as_01_import.dochead.id |    1 |             |
+------+-------------+-----------------+------+---------------------------------------+----------------------------+---------+-------------------------+------+-------------+


MariaDB [as_01_import]> explain delete from invoice_payment where invoice_id in (select id from dochead where system_id = 5786);
+------+--------------------+-----------------+-----------------+---------------------------+---------+---------+------+---------+-------------+
| id   | select_type        | table           | type            | possible_keys             | key     | key_len | ref  | rows    | Extra       |
+------+--------------------+-----------------+-----------------+---------------------------+---------+---------+------+---------+-------------+
|    1 | PRIMARY            | invoice_payment | ALL             | NULL                      | NULL    | NULL    | NULL | 1235451 | Using where |
|    2 | DEPENDENT SUBQUERY | dochead         | unique_subquery | PRIMARY,dochead_system_id | PRIMARY | 4       | func |       1 | Using where |
+------+--------------------+-----------------+-----------------+---------------------------+---------+---------+------+---------+-------------+
2 rows in set (0.44 sec)

知道JOIN可以使用索引,想请教高手:

是什么阻止 MySQL / MariaDB 在 DELETE 中使用带有 SUBQUERY 的索引?这是实施问题还是存在概念问题?有解决这个问题的计划吗?同样的问题会影响其他 SQL 供应商吗?

【问题讨论】:

【参考方案1】:

子查询是派生表,没有具体化。它们体现在临时表中。

正如我在this answer 中所写:

文档Derived Tables in MySQL 5.7 很好地描述了它 版本 5.6 和 5.7,后者将不提供任何惩罚,因为 物化派生表输出的变化被合并 进入外部查询。在以前的版本中,大量开销是 用派生的临时表忍受。

【讨论】:

【参考方案2】:

不要使用IN ( SELECT ... )。相反,请使用多表 DELETE,如下所述:http://dev.mysql.com/doc/refman/5.5/en/delete.html

这样做将在适用的情况下使用索引。

【讨论】:

我认为 Rick 专注于它的其他方面,比如以不同的方式做事,而不是解释子查询行为。就是说,我绝对不是这个Docs Multi-Table Delete。

以上是关于为啥 MySQL DELETE 无法对子查询使用索引?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 mysql 优化器没有使用完整的索引?

mongodb为啥比mysql快

你知道MySQL与MariaDB对子查询中order by的处理的差异吗?

NestJS 中的 TypeORM - 如何在实体中获取 MySQL 嵌套对象并对子关系进行“位置”查询?

在 MySQL 查询中使用 OR 时,有没有办法使用索引来避免文件排序?

我可以在 SQL 中一起做成对和非成对子查询吗