IN (SELECT subquery) 的问题在于 MySQL “优化”了它,而这往往是一个性能很差的选择。
根据documentation,带有这样一个模板的查询:
WHERE outer_expr IN (SELECT inner_expr FROM ... WHERE subquery_where)
由优化器自动转换为:
存在的位置(从 ... WHERE subquery_where AND outer_expr=inner_expr 选择 1)
问题是这个子查询是针对外部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 在随后的JOINs 上处理的就越少,而且处理得越快。例如,您可以直接从开头过滤 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 ... 来强制执行JOINs 的顺序,以防它执行得更好(通常会这样做)。