Postgresql 从临时表更新,为每个组拾取“最后”行

Postgresql 从临时表更新,为每个组拾取“最后”行,postgresql,sql-update,greatest-n-per-group,postgresql-performance,postgresql-11,Postgresql,Sql Update,Greatest N Per Group,Postgresql Performance,Postgresql 11,假设有一个包含数据的表: +----+-------+ | id | value | +----+-------+ | 1 | 0 | | 2 | 0 | +----+-------+ 我需要做一个批量更新。并使用COPY FROM STDIN快速插入临时表,无需任何约束,因此它可以在id列中包含重复的值 要从中更新的临时表: +----+-------+ | id | value | +----+-------+ | 1 | 1 | | 2 | 1 |

假设有一个包含数据的表:

+----+-------+
| 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 |
+----+-------+
我需要目标表包含临时表中最后出现的值


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

假设您希望从最后插入临时表的行中获取值,那么您可以使用系统列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
关于:

这建立在实现细节之上,不受SQL标准的支持。如果某个插入方法不应该像将来的并行插入那样按顺序写入行,则会中断。目前,它应该可以工作。关于ctid:

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

和target.value source.value

跳过空更新-假设两列都不为空。否则,请使用:

并且target.value与source.value不同

见:


请定义最后一个。关系表中没有顺序,除非由order by定义。你的意思是最后一次物理插入?是的,临时表中的最后一次是最后一次写入的值。在我的例子中,这是'2'谢谢。回答得很好