我尝试在最近的日期合并两个具有不同时间分辨率的表格。
表格是这样的:
表1:
id | date | device | value1
----------------------------------
1 | 10:22 | 13 | 0.53
2 | 10:24 | 13 | 0.67
3 | 10:25 | 14 | 0.83
4 | 10:25 | 13 | 0.32
表2:
id | date | device | value2
----------------------------------
22 | 10:18 | 13 | 0.77
23 | 10:21 | 14 | 0.53
24 | 10:23 | 13 | 0.67
25 | 10:28 | 14 | 0.83
26 | 10:31 | 13 | 0.23
我想在第一个表中合并这些表。所以我想将value2附加到Table1,其中,对于每个设备,最新的值2出现。
结果:
id | date | device | value1 | value2
-------------------------------------------
1 | 10:22 | 13 | 0.53 | 0.77
2 | 10:24 | 13 | 0.67 | 0.67
3 | 10:25 | 14 | 0.83 | 0.53
4 | 10:25 | 13 | 0.32 | 0.67
我有一些(20-30)个设备,Table2(= m)中有数千行,Table1(= n)中有数百万行。
我可以按日期(O(n*logn)
)对所有表进行排序,将它们写入文本文件并像合并一样迭代Table1,同时从Table2中提取数据直到它更新(我必须管理它~20) -30指向每个设备的最新数据,但没有更多),并且在合并之后我可以将其上传回数据库。然后复杂性为O(n*log(n))
用于排序,O(n+m)
用于迭代表。
但是在数据库中完成它会好得多。但我能得到的最好的查询是O(n ^ 2)复杂度:
SELECT DISTINCT ON (Table1.id)
Table1.id, Table1.date, Table1.device, Table1.value1, Table2.value2
FROM Table1, Table2
WHERE Table1.date > Table2.date and Table1.device = Table2.device
ORDER BY Table1.id, Table1.date-Table2.date;
我需要处理的数据量真的很慢,有更好的方法吗?或者只是用下载的数据做这些事情?
答案 0 :(得分:5)
您的查询可以重写为:
SELECT DISTINCT ON (t1.id)
t1.id, t1.date, t1.device, t1.value1, t2.value2
FROM table1 t1
JOIN table2 t2 USING (device)
WHERE t1.date > t2.date
ORDER BY t1.id, t2.date DESC;
您不需要为每个行组合计算日期差异(这是昂贵的而不是sargable),只需从每个集合中选择具有最大t2.date
的行。你需要戈登已经提到过的索引支持
DISTINCT ON
的详细信息:
但这可能不够快。鉴于您的数据分布,您需要一个松散索引扫描,可以使用相关子查询(如Gordon的查询)或更现代和多功能JOIN LATERAL
进行模拟:
SELECT t1.id, t1.date, t1.device, t1.value1, t2.value2
FROM table1 t1
LEFT JOIN LATERAL (
SELECT value2
FROM table2
WHERE device = t1.device
AND date < t1.date
ORDER BY date DESC
LIMIT 1
) t2 ON TRUE;
当LEFT JOIN
中找不到匹配项时,t2
可以避免丢失行。详细说明:
但那 仍然不是很快 ,因为你有“Table2中的数千行,而Table1中的数百万行”。
可能更快,但也更复杂。
UNION ALL
加窗函数在Table1
查询中合并Table2
和UNION ALL
,并在派生表上运行窗口函数。 "moving aggregate support" in Postgres 9.4或更高版本增强了这一点。
SELECT id, date, device, value1, value2
FROM (
SELECT id, date, device, value1
, min(value2) OVER (PARTITION BY device, grp) AS value2
FROM (
SELECT *
, count(value2) OVER (PARTITION BY device ORDER BY date) AS grp
FROM (
SELECT id, date, device, value1, NULL::numeric AS value2
FROM table1
UNION ALL
SELECT id, date, device, NULL::numeric AS value1, value2
FROM table2
) s1
) s2
) s3
WHERE value1 IS NOT NULL
ORDER BY date, id;
你必须测试它是否可以竞争。大量work_mem
对内存中排序的好处。
Table2
中每个设备的光标,循环遍历Table1
,在前进到cursor.date > t1.date
之前从相应的设备光标中选择值,并在最后一行之前保留value2
。与此处的获胜实施类似:
可能最快,但写的代码更多。不确定你还有兴趣。
答案 1 :(得分:2)
因为表1非常小,所以使用相关子查询可能更有效:
select t1.*,
(select t2.value2
from table2 t2
where t2.device = t.device and t2.date <= t1.date
order by t2.date desc
limit 1
) as value2
from table1 t1;
还要在table2(device, date, value2)
上创建一个索引,以提高效果。