MySQL会自动优化子查询吗?

时间:2012-08-02 17:46:40

标签: mysql sql

我想运行以下查询:

-- Main Query    
SELECT COUNT(*) FROM table_name WHERE device_id IN 
     (SELECT DISTINCT device_id FROM table_name WHERE NAME = 'SOME_PARA')

以下查询(来自主查询的子查询):

SELECT DISTINCT device_id FROM table_name WHERE NAME = 'SOME_PARA'

在7秒内执行,从2.1M行的表中提供2691行。

我解除了上面的主查询,并且在等待5分钟后仍在执行。

最后,我分别执行了子查询,从结果中取出了2691条记录,执行了以下查询:

-- Main Query (improvised)    
SELECT COUNT(*) FROM table_name WHERE device_id IN 
     ("device_id_1", "device_id_2", ....., "device_id_2691")

令人惊讶的是,这给了我40秒内的答案。

是什么给出的?为什么MySQL不使用我使用的相同技术并快速给出答案?我做错了吗?

4 个答案:

答案 0 :(得分:5)

不幸的是,MySQL并不擅长使用IN优化子查询。这来自MySQL documentation

  

IN的子查询优化不如=运算符有效   或者用于IN(value_list)运算符。

     

子查询性能差的典型情况是子查询   返回少量行,但外部查询返回一个大行   要与子查询结果进行比较的行数。

     

问题在于,对于使用IN子查询的语句,   优化器将其重写为相关子查询。考虑以下   使用不相关子查询的语句:

     

SELECT ... FROM t1 WHERE t1.a IN(SELECT b FROM t2);

     

优化器将语句重写为相关子查询:

     

SELECT ... FROM t1 WHERE EXISTS(SELECT 1 FROM t2 WHERE t2.b = t1.a);

     

如果内部和外部查询分别返回M和N行   执行时间变为O(M×N),而不是O(M + N)   这将是一个不相关的子查询。

     

这意味着IN子查询可能比查询慢得多   使用列出相同值的IN(value_list)运算符编写   子查询将返回。

尝试使用JOIN。

因为MySQL从内到外工作,有时你可以通过将子查询包装在另一个子查询中来欺骗MySQL,如下所示:

SELECT COUNT(*) FROM table_name WHERE device_id IN
     (SELECT * FROM (SELECT DISTINCT device_id FROM table_name WHERE NAME = 'SOME_PARA') tmp)

这是JOIN解决方案:

SELECT COUNT(DISTINCT t2.id) FROM table_name t1
  JOIN table_name t2
    ON t2.device_id = t1.device_id
  WHERE t1.NAME = 'SOME_PARA'

请注意,我从内部开始也出去。

答案 1 :(得分:4)

编辑:我不知道MySQL在这种情况下的愚蠢是什么原因:),this bug report似乎与案例有关。 解决方法是使用JOIN

SELECT 
    COUNT(t1.device_id) 
FROM table_name t1 
JOIN (
    SELECT DISTINCT device_id FROM table_name WHERE NAME = 'SOME_PARA'
) as t2 ON t2.device_id = t1.device_id 

答案 2 :(得分:2)

我认为您可以将查询重写为:

 SELECT sum(NumOnDevice) 
 from (SELECT device_id, count(*) as NumOnDevice
       FROM table_name
       having sum(case when NAME = 'SOME_PARA' then 1 else 0 end) > 0
      ) t

我意识到这不能回答你的问题,但它可能对你有帮助。

就优化而言,在给查询提供一堆常量和给查询提供子查询(即使结果相同)之间存在着天壤之别。在第一种情况下,查询优化器具有更多用于决定查询计划的信息。在第二种情况下,信息在编译时不可用。

Mysql - 比大多数数据库更多 - 似乎根据查询的表达方式生成查询计划。 SQL被设计为声明性语言而不是过程语言。这意味着SQL查询描述了所需的结果集,并且查询引擎应该决定实现该结果的最佳方法。但是,在许多情况下,必须帮助数据库引擎才能获得最佳结果。

答案 3 :(得分:1)

看看你要求MySQL做什么,它必须查看table_name中的每条记录,确定device_id是否在通过运行查询获得的列表中,然后决定是否&# 39; s将它添加到计数中。所以它运行子查询2.1M次。

这也是为什么当手动定义该列表时,它可以相当快速地通过它。