MySQL混淆了IN(CONST vs UNION vs SELECT FROM(UNION))

时间:2016-11-01 17:49:58

标签: mysql

有人可以解释为什么这些查询之间存在很大差异吗?

所有这些结果完全相同。

query 1的效果:非常好,query 2:不好,query 3:很好。

为什么query 2从表test(id 1)中选择包含所有行?为什么possible_keys不包含实际使用的PRIMARY

表:

CREATE TABLE `test` (
  `id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `test` ADD PRIMARY KEY (`id`);

数据:

DROP PROCEDURE IF EXISTS insert1000;
DELIMITER $$
CREATE PROCEDURE insert1000()
    BEGIN
        SET @i = 1;
        WHILE @i < 1000 DO
            INSERT INTO `test` VALUES (@i);
            SET @i = @i + 1;
        END WHILE;
    END
$$
DELIMITER ;
CALL insert1000();
DROP PROCEDURE insert1000;

查询1:

SELECT `id` FROM `test` WHERE `id` IN (2, 3)

查询1解释:

+----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra                    |
+----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+
| 1  | SIMPLE      | test  | range | PRIMARY       | PRIMARY | 4       | NULL | 2    | Using where; Using index |
+----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+

查询2:

SELECT `id` FROM `test` WHERE `id` IN (SELECT 2 UNION SELECT 3)

查询2解释:

+------+--------------------+------------+-------+---------------+---------+---------+------+------+--------------------------+
| id   | select_type        | table      | type  | possible_keys | key     | key_len | ref  | rows | Extra                    |
+------+--------------------+------------+-------+---------------+---------+---------+------+------+--------------------------+
| 1    | PRIMARY            | test       | index | NULL          | PRIMARY | 4       | NULL | 999  | Using where; Using index |
+------+--------------------+------------+-------+---------------+---------+---------+------+------+--------------------------+
| 2    | DEPENDENT SUBQUERY | NULL       | NULL  | NULL          | NULL    | NULL    | NULL | NULL | No tables used           |
+------+--------------------+------------+-------+---------------+---------+---------+------+------+--------------------------+
| 3    | DEPENDENT UNION    | NULL       | NULL  | NULL          | NULL    | NULL    | NULL | NULL | No tables used           |
+------+--------------------+------------+-------+---------------+---------+---------+------+------+--------------------------+
| NULL | UNION RESULT       | <union2,3> | ALL   | NULL          | NULL    | NULL    | NULL | NULL |                          |
+------+--------------------+------------+-------+---------------+---------+---------+------+------+--------------------------+

查询3:

SELECT `id` FROM `test` WHERE `id` IN (SELECT * FROM (SELECT 2 UNION SELECT 3) AS `derived`)

查询3解释:

+------+--------------+-------------+--------+---------------+---------+---------+-----------+------+--------------------------+
| id   | select_type  | table       | type   | possible_keys | key     | key_len | ref       | rows | Extra                    |
+------+--------------+-------------+--------+---------------+---------+---------+-----------+------+--------------------------+
| 1    | PRIMARY      | <subquery2> | ALL    | distinct_key  | NULL    | NULL    | NULL      | 2    |                          |
+------+--------------+-------------+--------+---------------+---------+---------+-----------+------+--------------------------+
| 1    | PRIMARY      | test        | eq_ref | PRIMARY       | PRIMARY | 4       | derived.2 | 1    | Using where; Using index |
+------+--------------+-------------+--------+---------------+---------+---------+-----------+------+--------------------------+
| 2    | MATERIALIZED | <derived3>  | ALL    | NULL          | NULL    | NULL    | NULL      | 2    |                          |
+------+--------------+-------------+--------+---------------+---------+---------+-----------+------+--------------------------+
| 3    | DERIVED      | NULL        | NULL   | NULL          | NULL    | NULL    | NULL      | NULL | No tables used           |
+------+--------------+-------------+--------+---------------+---------+---------+-----------+------+--------------------------+
| 4    | UNION        | NULL        | NULL   | NULL          | NULL    | NULL    | NULL      | NULL | No tables used           |
+------+--------------+-------------+--------+---------------+---------+---------+-----------+------+--------------------------+
| NULL | UNION RESULT | <union3,4>  | ALL    | NULL          | NULL    | NULL    | NULL      | NULL |                          |
+------+--------------+-------------+--------+---------------+---------+---------+-----------+------+--------------------------+

1 个答案:

答案 0 :(得分:1)

MySQL优化器的内部工作原理......

虽然query 2query 3都需要全表扫描(无法使用索引),但它们的不同语法会使优化程序使用不同的策略。

通过运行EXPLAIN EXTENDED SELECT ...然后运行SHOW WARNINGS;,您可以更清楚地看到它。

这是query 2的扩展计划:

select `test`.`id` AS `id` 
from `test` 
where <in_optimizer>(`test`.`id`,<exists>(select 2 having (<cache>(`test`.`id`) = <ref_null_helper>(2)) 
                                          union 
                                          select 3 having (<cache>(`test`.`id`) = <ref_null_helper>(3))
                                          ))

优化程序会将IN翻译为EXISTS,然后将2个查询SELECT 2SELECT 3的结果与test中扫描的行进行比较。< / p>

这是query 3的扩展计划:

select `test`.`id` AS `id` 
from `test` 
where <in_optimizer>(`test`.`id`,<exists>(select 1 from (select 2 AS `2` union select 3 AS `3`) `derived` where (<cache>(`test`.`id`) = `derived`.`2`)))

您可以看到,在这种情况下,优化器正在运行原始UNION以创建值为2和3的派生表,然后将此表与它在表{{1}中扫描的数据进行一次比较}。