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

检查问题此SELECT查询需要180秒才能完成 (请检查问题本身的注释)。
IN只能与一个值比较,但时间差异仍然很大。
为什么这样?

简介:这是MySQL中的一个已知问题 ,已在MySQL 5.6.x中修复。 这个问题是由于当使用IN的子查询被错误地识别为依赖子查询而不是独立子查询时缺less优化。


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

 1'PRIMARY''question_law_version''ALL''''''''''10148'使用where'
 2'依赖子查询''question_law_version''全部''''''''''10148'使用where'
 3'依赖性子查询''question_law''全部''''''''''10040'使用where'

当你改变为=你得到这个:

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

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

现在这当然留下了为什么MySQL认为IN版本需要成为一个从属子查询的问题。 我已经做了一个简化版本的查询来帮助调查。 我创build了两个表'foo'和'bar',前者只包含一个id列,后者包含一个id和一个foo id(虽然我没有创build外键约束)。 然后我用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 ); 

这个简化的查询具有和以前相同的问题 – 内部select被视为从属子查询,并且不执行优化,导致内部查询每行运行一次。 查询需要几乎一秒的时间才能运行。 将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; 

这是关于内部查询又名子查询VS连接,而不是关于IN vs =,ant的原因在那篇文章中解释。 MySQL的5.4版本旨在介绍一个改进的优化器,可以将某些子查询重写为更有效的forms。

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

SQL优化器并不总是做你期望的事情。 我不确定有没有比这更好的答案。 这就是为什么你必须检查EXPLAIN PLAN输出,并且查询你的查询来找出花费的时间。

这很有趣,但是这个问题也可以用准备好的陈述来解决(不知道它是否适合每个人),例如:

 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;