从 temp table 更新每组选择 "last" 行

UPDATE from temp table picking the "last" row per group

假设有一个 table 数据:

+----+-------+
| id | value |
+----+-------+
|  1 |     0 |
|  2 |     0 |
+----+-------+

我需要进行批量更新。并使用 COPY FROM STDIN 不受约束地快速插入临时 table,因此它可以在 id

中包含重复值

要更新的温度 table:

+----+-------+
| id | value |
+----+-------+
|  1 |     1 |
|  2 |     1 |
|  1 |     2 |
|  2 |     2 |
+----+-------+

如果我只是 运行 查询,例如:

UPDATE test target SET value = source.value FROM tmp_test source WHERE target.id = source.id;

我得到了错误的结果:

+----+-------+
| id | value |
+----+-------+
|  1 |     1 |
|  2 |     1 |
+----+-------+

我需要目标 table 包含最后出现在临时 table 中的值。

考虑到目标 table 可能包含数百万条记录,而临时 table 可能包含数万条记录,最有效的方法是什么?**

假设您想要从最后插入临时 table 的行中获取 value,实际上,您可以(滥用)使用系统列 ctid,表示物理位置:

UPDATE test AS target
SET    value = source.value
FROM  (
   SELECT DISTINCT ON (id)
          id, value
   FROM   tmp_test
   ORDER  BY id, ctid DESC
   ) source
WHERE  target.id = source.id
AND    target.value <> source.value;  -- skip empty updates

关于DISTINCT ON

  • Select first row in each GROUP BY group?

这建立在实现细节之上,并且不受 SQL 标准的支持。如果某些插入方法不应该按顺序写入行(比如未来的“平行”INSERT),它就会中断。目前,它应该工作。关于 ctid:

如果你想要一种安全的方式,你需要添加一些用户列来表示行的顺序,比如 serial 列。但你真的在乎吗?您的决胜局似乎相当武断。参见:

  • Temporary sequence within a SELECT

AND target.value <> source.value

跳过空更新 - 假设两列都是 NOT NULL。否则,使用:

AND target.value IS DISTINCT FROM source.value

参见:

  • How do I (or can I) SELECT DISTINCT on multiple columns?