这个SELECT查询需要180秒才能完成。

12

更新:

只是想在更显眼的地方提一下。当我把IN改成=时,查询执行时间从180秒降至0.00008秒。速度差异荒谬。


这个 SQL 查询需要180秒才能完成!这怎么可能?有没有办法优化它以使其更快?

SELECT IdLawVersionValidFrom 
FROM question_law_version 
WHERE IdQuestionLawVersion IN 
  (
  SELECT MAX(IdQuestionLawVersion) 
  FROM question_law_version 
  WHERE IdQuestionLaw IN 
    (
    SELECT MIN(IdQuestionLaw) 
    FROM question_law 
    WHERE IdQuestion=236 AND IdQuestionLaw>63
    )
  )

每个表中只有大约5000行数据,因此不应该如此缓慢。


5
如果将IN改为=,会有任何不同吗? - Martin Smith
8
这些列名让我头疼。你能解释一下这个查询应该做什么吗? - D'Arcy Rittich
@Martin Smith 如果这真的有任何区别的话,那对我来说会很惊讶(也很有教育意义)。 - Itay Moav -Malimovka
@Itay - 的确,这只是一个有些投机的建议。 - Martin Smith
@Martin Smith 那真的有效 :o 它从180秒降到了0.00008。请将其发布为答案,以便我可以接受它 :) - Richard Knop
@Martin Smith - 对我来说非常有教育意义 - 谢谢(但仍然很奇怪,我想知道为什么会这样) - Itay Moav -Malimovka
3个回答

16

将我的评论作为答案发布,因为显然它确实有所不同!

如果您将IN更改为=,会有任何区别吗?

如果有人想进一步调查此事,我刚刚进行了测试,并发现很容易复现。

创建表格

CREATE TABLE `filler` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`)
) 

创建存储过程

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
填充表格
  call prc_filler(5000)

查询1

SELECT id 
FROM filler 
WHERE id =  (SELECT MAX(id) FROM filler  WHERE id =   
 ( SELECT MIN(id) 
    FROM filler
    WHERE id between 2000 and 3000
    )
  )

等于号解释输出 http://img689.imageshack.us/img689/5592/equals.png

查询2(同一问题)

SELECT id 
FROM filler 
WHERE id in  (SELECT MAX(id) FROM filler  WHERE id in   
 ( SELECT MIN(id) 
    FROM filler
    WHERE id between 2000 and 3000
    )
  )

在展示输出中的图片 http://img291.imageshack.us/img291/8129/52037513.png


我很想请一些MySQL专家就性能差异发表评论。显然,子查询只会返回1行,因此等号是正确的运算符,但为什么使用IN会在仅检查值是否在1行结果集中找到时产生如此大的性能差异呢? - wadesworld
3
@Wade - 我想Mark在这里的回答已经解决了这个问题 https://dev59.com/pXA75IYBdhLWcg3wOGLS#3417190 - Martin Smith

12

这里有一个很好的解释,为什么使用等号比IN更好

Mysql在内部查询方面存在问题 - 不会很好地使用索引(如果使用的话)。

  1. 确保在连接/where/order等中所有字段上都有索引。
  2. 在单独的查询中获取那些最大值和最小值(如果想要跳过多个请求开销,可以使用存储过程来完成整个操作,或者只需使用多个查询执行单个请求)。

无论如何:

SELECT
         IdLawVersionValidFrom 
FROM 
         question_law_version 
    JOIN 
         question_law
      ON 
         question_law_version.IdQuestionLaw = question_law.IdQuestionLaw
WHERE 
         question_law.IdQuestion=236 
     AND 
         question_law.IdQuestionLaw>63

ORDER BY 
         IdQuestionLawVersion DESC, 
         question_law.IdQuestionLaw ASC
LIMIT 1

明天我就能测试了。我把应用程序放在工作电脑里了,而我现在已经回家了。 - Richard Knop

4

您可以使用EXPLAIN来查找查询为什么执行如此缓慢。

MySQL并不真正喜欢嵌套子查询,所以可能发生的情况是它会在磁盘上进行排序以获得最小值和最大值,并且无法重用结果。

将其重写为连接可能会有所帮助。

如果只是寻找快速解决方案,请尝试:

SET @temp1 =     
  (
  SELECT MIN(IdQuestionLaw) 
  FROM question_law 
  WHERE IdQuestion = 236 AND IdQuestionLaw > 63
  )

SET @temp2 = 
  (
  SELECT MAX(IdQuestionLawVersion) 
  FROM question_law_version 
  WHERE IdQuestionLaw = @temp1
  )

SELECT IdLawVersionValidFrom 
FROM question_law_version 
WHERE IdQuestionLawVersion = @temp2

完全同意并给予+1支持。请注意他对EXPLAIN的使用 :) - Unreason

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接