MYSQL优化

时间:2014-08-21 13:18:18

标签: mysql sql optimization exists

我无法将我的SQL查询的IN语句调整为EXISTS。我知道IN速度较慢,这似乎反映在查询的性能中。

SELECT 
    t.dt as 'Log Time',
    sn.name as 'Snake Name',
    sen.type as 'Sensor Type',
    t.temp as Temperature
FROM
    temps as t
        JOIN
    sensors as sen ON t.sensor = sen.sensorid
        JOIN
    locations as l ON sen.location = l.id
        JOIN
    snakes as sn ON sen.location = sn.location
WHERE
    dt IN (SELECT 
            max(dt)
        FROM
            temps
        GROUP BY sensor)
ORDER BY sn.name ASC , sen.type DESC
;

如何改进这一点的任何想法都将受到赞赏。

4 个答案:

答案 0 :(得分:1)

IN (SELECT subquery)的问题在于MySQL使用通常非常糟糕的性能选择“优化”它。

根据documentation,使用如下模板的查询:

  
    

WHERE outer_expr IN(SELECT inner_expr FROM ... WHERE subquery_where

  

由优化器自动转换为:

  
    

WHERE EXISTS(SELECT 1 FROM ... WHERE subquery_where AND outer_expr = inner_expr

  

问题是该子查询是针对外部SELECT中的每一行执行的。如果我们在WHERE之前讨论数百,数千甚至数十万行,MySQL将花费很长时间来消化这个东西,一遍又一遍地迭代相同的表来查找每个匹配。不酷!

解决方案是强制它使用临时表创建JOIN。这个想法是,尽管创建临时表的速度比简单查询慢,并且需要更多内存,但它确实比数千个查询更快。所以这就是你要做的事情:

SELECT 
    t.dt `Log Time`,
    sn.name `Snake Name`,
    sen.type `Sensor Type`,
    t.temp `Temperature`
FROM
    temps t
        JOIN
    sensors sen ON t.sensor = sen.sensorid
        JOIN
    locations l ON sen.location = l.id
        JOIN
    snakes sn ON sen.location = sn.location
        JOIN
    (SELECT sensor, MAX(dt) maxdt FROM temps GROUP BY sensor) m
        ON m.maxdt = t.dt AND m.sensor = t.sensor
ORDER BY sn.name ASC, sen.type DESC;

JOIN使用子查询SELECT时,它不会做出愚蠢的假设。在场景后面创建一个临时表,它将按照您的说法执行JOIN

然而,请注意这可以进一步优化。我们可以通过在JOIN表中保留游戏中的少量记录来实现此目的。您越早撤出查询中不需要的记录,MySQL将不得不在随后的JOIN上处理的内容越少,它就越快。例如,您可以直接从头开始过滤所需的行,只需重新组织temps序列:

JOIN

这个看似简单的变化对于性能非常有意义,这个查询应该比第一个快得多,特别是如果SELECT t.dt `Log Time`, sn.name `Snake Name`, sen.type `Sensor Type`, t.temp `Temperature` FROM (SELECT sensor, MAX(dt) maxdt FROM temps GROUP BY sensor) m JOIN temps t ON m.maxdt = t.dt AND m.sensor = t.sensor JOIN sensors sen ON t.sensor = sen.sensorid JOIN locations l ON sen.location = l.id JOIN snakes sn ON sen.location = sn.location ORDER BY sn.name ASC, sen.type DESC; 是一个大表。

您还可以使用temps强制执行SELECT STRAIGHT_JOIN ...的顺序,以防它的表现更好(通常情况下)。

答案 1 :(得分:0)

您是对的,IN子查询的通常EXISTS慢。

EXISTS的工作方式不同,因为您可以直接在其中使用以前的列。您还可以使用LIMIT来限制您的子集仅限于您真正需要的金额。使用EXISTS时,选择的内容无关紧要,因为它只是询问:是否至少返回了1行。

确保在每列上使用前缀。

SELECT 
    t.dt as 'Log Time',
    sn.name as 'Snake Name',
    sen.type as 'Sensor Type',
    t.temp as Temperature
FROM
    temps as t
        JOIN
    sensors as sen ON t.sensor = sen.sensorid
        JOIN
    locations as l ON sen.location = l.id
        JOIN
    snakes as sn ON sen.location = sn.location
WHERE
    EXISTS(
        SELECT 'hi'
        FROM   temps
        GROUP  BY temps.sensor
        HAVING max(temps.dt) = t.dt
        LIMIT 1
    )
ORDER BY sn.name ASC , sen.type DESC
;

答案 2 :(得分:0)

事实证明,EXISTS和IN都不是最佳解决方案。玩完游戏后,我想出了以下内容:

SELECT distinct
    t.dt as 'Log Time',
    sn.name as 'Snake Name',
    sen.type as 'Sensor Type',
    t.temp as Temperature
FROM
    (SELECT 
        *
    FROM
        temps
    ORDER BY dt DESC) as t
        JOIN
    sensors as sen ON t.sensor = sen.sensorid
        JOIN
    locations as l ON sen.location = l.id
        JOIN
    snakes as sn ON sen.location = sn.location
WHERE
    dt != '0000-00-00 00:00:00'
GROUP BY sensor
ORDER BY sn.name ASC , sen.type DESC

运行时间为0.047秒,与原始的~50秒查询相反。

答案 3 :(得分:0)

虽然看起来你有一个解决方案和非常好的时间,但Havenard有一个很好的观点,关于每个传感器不一定正确。我建议如下。

在你的临时表上,索引(传感器,dt),然后,你的第一个将是由每个传感器分组的选择,因此每个传感器出现一次及其各自的日期/时间。然后,使用它作为基础,通过相同的传感器/最大值(dt)重新加入临时值并获取其余数据。

这与Havenard发布的内容非常接近,除了我正在预先移动我的预查询并添加" STRAIGHT_JOIN"按照书面顺序强制加入。从最大日期/时间开始,使用非常有限的设置,然后加入其余部分以获取描述和临时信息。

SELECT STRAIGHT_JOIN
      t.dt `Log Time`,
      sn.name `Snake Name`,
      sen.type `Sensor Type`,
      t.temp `Temperature`
   FROM
      ( select t1.sensor, max( t1.dt ) as MaxDT
           from temps t1
           group by t1.sensor ) PreQuery
         JOIN temps t
            on PreQuery.sensor = t.sensor
           AND PreQuery.MaxDT = t.dt
        JOIN sensors sen 
           ON PreQuery.sensor = sen.sensorid
           JOIN locations l 
              ON sen.location = l.id
           JOIN snakes sn 
              ON sen.location = sn.location
   ORDER BY 
      sn.name,
      sen.type DESC;