我在 postgreSQL 数据库中有两个表(见下文)。
Table1
跟踪对每个 ID 所做的所有更改(即每个 ID 多行):
create table table1(id,record,name,data1,data2,data3)as values
(1,1,'Dave', 1.2,55,1)
,(1,2,'David', 1.2,55,1)
,(2,1,'Sam', .8,30,2)
,(3,1,'Jenn', .9,28,2)
,(4,1,'Arthur',1.1,77,1)
,(5,1,'Jim', .7,42,2)
,(5,2,'Jimmy', .7,42,2)
,(5,3,'James', .7,42,2)
,(6,1,'Sue', 1.3,32,2)
,(6,2,'Susan', 1.3,32,2);
Table2
每个 ID 仅包含一行:
create table table2(id,record,name,data1,data2,data3)as values
(1,1,'Dave', 1.2,55,1)
,(2,1,'Sam', .8,30,2)
,(3,1,'Jenn', .9,28,2)
,(4,1,'Arthur',1.1,77,1)
,(5,2,'Jimmy', .7,42,2)
,(6,1,'Sue', 1.3,32,2);
我需要一个 SQL 查询来Table2
根据每个 ID 的最高记录值进行更新Table1
,同时更新所有其他字段(本例中为 、 、 )。data1
这些data2
表
只是示例,但我的实际数据有很多列,所以我想使用某种通配符将所有字段包含在更新中。data3
我对 postgreSQL 中的 SQL 比较陌生,但这是我尝试获取返回的最新记录版本,但不确定如何从那里更新 Table2。我尝试过几次,但都没有成功,所以可能不值得发布该代码。
select * from
Table1 t1
where
(ID,Record) in
(
select
ID,
MAX(Record)
from
Table1 t1
group by
ID
)
预期结果:
ID | 记录 | 姓名 | 数据1 | 数据2 | 数据3 |
---|---|---|---|---|---|
1 | 2 | 大卫 | 1.2 | 55 | 1 |
2 | 1 | 山姆 | 0.8 | 三十 | 2 |
3 | 1 | 詹 | 0.9 | 二十八 | 2 |
4 | 1 | 亚瑟 | 1.1 | 77 | 1 |
5 | 3 | 詹姆斯 | 0.7 | 四十二 | 2 |
6 | 2 | 苏珊 | 1.3 | 三十二 | 2 |
只要您有足够的索引
table1
:甚至覆盖索引:
您可以使用子选择
UPDATE
语法来获得高性能:它只会从索引顶部弹出新值。演示在 db<>fiddle
在此 250k 行的测试中,它花费的时间大约为,与目前显示的使用、和
32ms
的替代方案相比,减少了 10 倍多。distinct on
max(record)
row_number()over()
如果您不想知道列名但可以信任
table1
和table2
匹配的结构,则可以运行重新插入:而且它仍然可以使用索引,在大约 160 毫秒内以大约 2 倍的速度完成。
如果您怀疑这会擦除并重写整个内容,从而导致某种效率低下,请不要担心。在Postgres MVCC中,每个
update
实际上都是delete
+insert
。实现此目的的最佳方法是使用利用的 cte
distinct on
。类似以下内容:它
distinct on
可以让你将结果集限制为每列(或多列)一行。order by
必须以 中的列开头的 决定了从剩余数据中返回哪一行。 因此,按记录降序排序与获取具有该 ID 的distinct on
的行相同。MAX(record)
只需
update
将 cte 连接到目标表并相应地更新值。恐怕我不知道有捷径,这意味着您不需要指定每一列!
为方便将来参考,请通过插入语句提供示例数据。这样可以节省我们的测试工作量。
我们获取 CTE(hgh)中每个 ID 的最大记录,并在相关的 UPDATE 语句中使用该记录来更新相应的列:
您可以使用 row_number 窗口函数来获取每个 id 的最新记录,如下所示:
根据您想要更新 table2 的频率,可能每次只需截断 table2 并在其中插入这些值。
小提琴