【问题标题】:UPDATE from temp table picking the "last" row per group从临时表更新每组选择“最后”行
【发布时间】:2020-07-02 21:30:48
【问题描述】:

假设有一张数据表:

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

我需要进行批量更新。并使用COPY FROM STDIN 快速插入临时表而不受约束,因此它可以在id 列中包含重复值

要更新的临时表:

+----+-------+
| 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 |
+----+-------+

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

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

【问题讨论】:

  • 请定义“最后一个”。除非由ORDER BY 定义,否则关系表中没有顺序。你的意思是最后插入,身体上?
  • 是的,临时表中的最后一个值是最后写入的值。在我的示例中,这是“2”

标签: postgresql sql-update greatest-n-per-group postgresql-performance postgresql-11


【解决方案1】:

假设您想从最后插入临时表的行中获取value,在物理上,您可以(ab-)使用系统列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

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

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

AND target.value &lt;&gt; source.value

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

AND target.value IS DISTINCT FROM source.value

见:

【讨论】:

  • 谢谢。很好的答案
猜你喜欢
  • 1970-01-01
  • 2017-03-17
  • 2015-06-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-15
相关资源
最近更新 更多