hun*_*dym 3 sql algorithm postgresql merge greatest-n-per-group
我尝试将两个具有不同时间分辨率的表合并到最近的日期。
表格如下:
表格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
Run Code Online (Sandbox Code Playgroud)
表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
Run Code Online (Sandbox Code Playgroud)
我想将这些表与第一个表合并。因此,我想将value2附加到Table1,其中对于每个设备,最新的value2出现。
结果:
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
Run Code Online (Sandbox Code Playgroud)
我有一些(20-30)设备,表2(= m)中有数千行,而表1(= 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;
Run Code Online (Sandbox Code Playgroud)
我需要处理的数据量真的很慢,是否有更好的方法来做到这一点?还是只是将这些东西与下载的数据一起使用?
您的查询可以重写为:
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;Run Code Online (Sandbox Code Playgroud)
您无需为行的每种组合计算日期差(这是昂贵的且不可固定的),只需t2.date从每个组中选择最大的行即可。您需要像Gordon提到的索引支持。
详细信息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;
Run Code Online (Sandbox Code Playgroud)
LEFT JOIN在中找不到匹配项时,避免避免丢失行t2。细节:
但这还不是很快,因为“表2中有数千行,表1中有数百万行”。
可能更快,但也更复杂。
UNION ALL加窗功能结合Table1并Table2在UNION ALL查询和运行在派生表的窗口功能。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;
Run Code Online (Sandbox Code Playgroud)
您必须测试它是否可以竞争。从大量work_mem内存中排序中受益。
所有三个查询的 SQL Fiddle。
在中的每个设备的游标Table2,循环Table1,从前进后的各个设备游标中选择值,直到cursor.date > t1.date并保持value2在最后一行之前。与此处的获胜实现类似:
可能最快,但是要编写更多代码。不确定您是否仍然感兴趣。
因为表 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;
Run Code Online (Sandbox Code Playgroud)
还要table2(device, date, value2)为性能创建一个索引。
| 归档时间: |
|
| 查看次数: |
2156 次 |
| 最近记录: |