为什么IN条件比sql中的“=”慢?

时间:2010-08-05 16:45:30

标签: sql mysql performance comparison

检查问题This SELECT query takes 180 seconds to finish(查看问题本身的评论) IN只能与一个值进行比较,但时差仍然很大 为什么会那样?

4 个答案:

答案 0 :(得分:47)

总结:这是MySQL中的known problem,并在MySQL 5.6.x中得到修复。问题是由于使用IN的子查询被错误地识别为从属子查询而不是独立子查询时缺少优化。


在原始查询上运行EXPLAIN时,它会返回:

1  'PRIMARY'             'question_law_version'  'ALL'  ''  ''  ''  ''  10148  'Using where'
2  'DEPENDENT SUBQUERY'  'question_law_version'  'ALL'  ''  ''  ''  ''  10148  'Using where'
3  'DEPENDENT SUBQUERY'  'question_law'          'ALL'  ''  ''  ''  ''  10040  'Using where'

当您将IN更改为=时,您会收到以下信息:

1  'PRIMARY'   'question_law_version'  'ALL'  ''  ''  ''  ''  10148  'Using where'
2  'SUBQUERY'  'question_law_version'  'ALL'  ''  ''  ''  ''  10148  'Using where'
3  'SUBQUERY'  'question_law'          'ALL'  ''  ''  ''  ''  10040  'Using where'

每个依赖子查询在包含它的查询中每行运行一次,而子查询只运行一次。当有条件可以转换为连接时,MySQL有时可以优化从属子查询,但事实并非如此。

现在这当然留下了为什么MySQL认为IN版本需要是依赖子查询的问题。我已经制作了查询的简化版本以帮助调查此问题。我创建了两个表'foo'和'bar',其中前者只包含一个id列,后者包含id和foo id(虽然我没有创建外键约束)。然后我用1000行填充了两个表:

CREATE TABLE foo (id INT PRIMARY KEY NOT NULL);
CREATE TABLE bar (id INT PRIMARY KEY, foo_id INT NOT NULL);

-- populate tables with 1000 rows in each

SELECT id
FROM foo
WHERE id IN
(
    SELECT MAX(foo_id)
    FROM bar
);

此简化查询具有与以前相同的问题 - 内部选择被视为从属子查询,并且不执行优化,导致内部查询每行运行一次。查询需要几秒钟才能运行。将IN再次更改为=可让查询几乎立即运行。

我用来填充表格的代码如下,以防有人希望重现结果。

CREATE TABLE filler (
        id INT NOT NULL PRIMARY KEY AUTO_INCREMENT
) ENGINE=Memory;

DELIMITER $$

CREATE PROCEDURE prc_filler(cnt INT)
BEGIN
        DECLARE _cnt INT;
        SET _cnt = 1;
        WHILE _cnt <= cnt DO
                INSERT
                INTO    filler
                SELECT  _cnt;
                SET _cnt = _cnt + 1;
        END WHILE;
END
$$

DELIMITER ;

CALL prc_filler(1000);

INSERT foo SELECT id FROM filler;
INSERT bar SELECT id, id FROM filler;

答案 1 :(得分:1)

这是关于内部查询a.k.a子查询与连接,而不是关于IN vs =,蚂蚁的原因在那篇文章中有解释。 MySQL的5.4版本用于引入改进的优化器,可以将一些子查询重写为更有效的形式。

你能做的最糟糕的事情就是使用所谓的相关子查询 http://dev.mysql.com/doc/refman/5.1/en/correlated-subqueries.html

答案 2 :(得分:0)

SQL优化器并不总是按照您的期望执行。我不确定有什么比这更好的答案。这就是为什么你必须检查EXPLAIN PLAN输出,并分析你的查询以找出花费的时间。

答案 3 :(得分:0)

这很有趣,但问题也可以通过准备好的陈述来解决(不确定它是否适合所有人),例如:

mysql> EXPLAIN SELECT * FROM words WHERE word IN (SELECT word FROM phrase_words);
+----+--------------------+--------------+...
| id | select_type        | table        |...
+----+--------------------+--------------+...
|  1 | PRIMARY            | words        |...
|  2 | DEPENDENT SUBQUERY | phrase_words |...
+----+--------------------+--------------+...
mysql> EXPLAIN SELECT * FROM words WHERE word IN ('twist','rollers');
+----+-------------+-------+...
| id | select_type | table |...
+----+-------------+-------+...
|  1 | SIMPLE      | words |...
+----+-------------+-------+...

所以只需在存储过程中准备语句,然后执行它。这是一个想法:

SET @words = (SELECT GROUP_CONCAT(word SEPARATOR '\',\'') FROM phrase_words);
SET @words = CONCAT("'", @words, "'");
SET @query = CONCAT("SELECT * FROM words WHERE word IN (", @words, ");";
PREPARE q FROM @query;
EXECUTE q;