我有三张桌子,设置如下:
TEMPERATURE_1
time
zone (FK)
temperature
TEMPERATURE_2
time
zone (FK)
temperature
TEMPERATURE_3
time
zone (FK)
temperature
每个表中的数据会定期更新,但不一定同时更新(即时间条目不相同)。
我希望每次能够访问每张表中最接近的读数,即:
TEMPERATURES
time
zone (FK)
temperature_1
temperature_2
temperature_3
换句话说,对于我的三个表中的每个唯一时间,我想在TEMPERATURES表中有一行,其中temperature_n值是每个原始表中与时间最接近的温度读数。
目前,我已经使用两种观点进行了设置:
create view temptimes
as select time, zone
from temperature_1
union
select time, zone
from temperature_2
union
select time, zone
from temperature_3;
create view temperatures
as select tt.time,
tt.zone,
(select temperature
from temperature_1
order by abs(timediff(time, tt.time))
limit 1) as temperature_1,
(select temperature
from temperature_2
order by abs(timediff(time, tt.time))
limit 1) as temperature_2,
(select temperature
from temperature_3
order by abs(timediff(time, tt.time))
limit 1) as temperature_3,
from temptimes as tt
order by tt.time;
这种方法有效,但在生产中使用速度太慢(对于每个温度约1000条记录的小数据集,需要几分钟)。
我对SQL不太满意,所以我确定我错过了正确的方法。我该如何处理这个问题?
答案 0 :(得分:0)
这很慢的原因是它需要3次表扫描来计算和排序差异。
我假设您已经在时区列上拥有索引 - 目前他们无法帮助解决表扫描问题。
根据您的需要和数据收集率,有许多选项可以避免这种情况。
您已经说过,数据是定期收集的,但不是同时收集的。这表明了一些选择。
我理解你想要完成的事情 - 问问自己为什么以及一个更简单的解决方案是否能满足你的需求。
答案 1 :(得分:0)
昂贵的部分是相关子查询必须为每个temperature_*
表的每一行计算时间差,以便为一个一个最近的行>主查询中一行行的列。
如果您可以根据索引在当前时间之前选择一个行并且一个行,那么速度会快得多计算这两个候选人的时差。您需要的只是表格中time
列的索引。
我忽略了专栏zone
,因为它的作用在问题中仍然不清楚,它只会给核心问题增加更多噪音。应该很容易添加到查询中。
如果没有其他视图,此查询会立即执行所有操作:
SELECT time
,COALESCE(temp1
,CASE WHEN timediff(time, time1a) > timediff(time1b, time) THEN
(SELECT t.temperature
FROM temperature_1 t
WHERE t.time = y.time1b)
ELSE
(SELECT t.temperature
FROM temperature_1 t
WHERE t.time = y.time1a)
END) AS temp1
,COALESCE(temp2
,CASE WHEN timediff(time, time2a) > timediff(time2b, time) THEN
(SELECT t.temperature
FROM temperature_2 t
WHERE t.time = y.time2b)
ELSE
(SELECT t.temperature
FROM temperature_2 t
WHERE t.time = y.time2a)
END) AS temp2
,COALESCE(temp3
,CASE WHEN timediff(time, time3a) > timediff(time3b, time) THEN
(SELECT t.temperature
FROM temperature_3 t
WHERE t.time = y.time3b)
ELSE
(SELECT t.temperature
FROM temperature_3 t
WHERE t.time = y.time3a)
END) AS temp3
FROM (
SELECT time
,max(t1) AS temp1
,max(t2) AS temp2
,max(t3) AS temp3
,CASE WHEN max(t1) IS NULL THEN
(SELECT t.time FROM temperature_1 t
WHERE t.time < x.time
ORDER BY t.time DESC LIMIT 1) ELSE NULL END AS time1a
,CASE WHEN max(t1) IS NULL THEN
(SELECT t.time FROM temperature_1 t
WHERE t.time > x.time
ORDER BY t.time LIMIT 1) ELSE NULL END AS time1b
,CASE WHEN max(t2) IS NULL THEN
(SELECT t.time FROM temperature_2 t
WHERE t.time < x.time
ORDER BY t.time DESC LIMIT 1) ELSE NULL END AS time2a
,CASE WHEN max(t2) IS NULL THEN
(SELECT t.time FROM temperature_2 t
WHERE t.time > x.time
ORDER BY t.time LIMIT 1) ELSE NULL END AS time2b
,CASE WHEN max(t3) IS NULL THEN
(SELECT t.time FROM temperature_3 t
WHERE t.time < x.time
ORDER BY t.time DESC LIMIT 1) ELSE NULL END AS time3a
,CASE WHEN max(t3) IS NULL THEN
(SELECT t.time FROM temperature_3 t
WHERE t.time > x.time
ORDER BY t.time LIMIT 1) ELSE NULL END AS time3b
FROM (
SELECT time, temperature AS t1, NULL AS t2, NULL AS t3 FROM temperature_1
UNION ALL
SELECT time, NULL AS t1, temperature AS t2, NULL AS t3 FROM temperature_2
UNION ALL
SELECT time, NULL AS t1, NULL AS t2, temperature AS t3 FROM temperature_3
) AS x
GROUP BY time
) y
ORDER BY time;
suqquery x 替换您的视图temptimes
并将温度带入结果中。如果所有三个表都是同步的并且具有所有相同时间点的温度,则其余的甚至不需要并且非常快
对于三个表中没有一个没有行的每个时间点,按照指示获取温度:从每个表中取出“最接近”的温度。
suqquery y 根据当前时间聚合x
中的行并获取上一次(time1a
)和下一次(time1b
)每个表缺少温度。这些查找应该使用索引快速。
最终查询从行中获取温度,其中每个温度最接近实际丢失的时间。
如果MySQL允许从当前子查询之上的多个级别引用列,则此查询可能更简单。它不能。在 PostgreSQL :->sqlfiddle
中工作得很好如果可以从相关子查询中返回多个列,这也会更简单,但我不知道如何在MySQL中执行此操作。
使用 CTE 和窗口函数会更简单,但MySQL不知道这些现代SQL功能(与其他相关的不同) RDBMS)。
答案 2 :(得分:0)
我的建议是你没有采取最接近的时间,但你是在特定时间或之前的第一次。原因很简单:通常,给定时间的数据是当时已知的数据。在大多数情况下,纳入未来信息通常不是一个好主意。
通过此更改,您可以修改查询以利用time
上的索引。查询索引的问题在于该函数排除了索引的使用。
因此,如果您想要最近的温度,请使用此代替每个变量:
(select temperature
from temperature_1 t2
where t2.time <= tt.time
order by t2.time desc
limit 1
) as temperature_1,
实际上,您也可以像这样构建它:
(select time
from temperature_1 t2
where t2.time <= tt.time
order by t2.time desc
limit 1
) as time_1,
然后加入温度信息。使用索引,这将是有效的。
考虑到这一点,您实际上可以有两个变量time_1_before
和time_1_after
,以获得最佳时间或之前的最佳时间以及最佳时间。您可以使用select中的逻辑来选择最接近的值。使用索引可以有效地恢复温度。
但是,我会重申,我认为最后的温度可能是最好的选择。