用于仅更新 UPSERT 中更改的行的语法简写

Cau*_*tic 6 postgresql syntax update upsert

我有一个查询,当一个键冲突时,只有在其他列之一发生更改时才会向上插入 Postgres(以避免不必要的插入和返回的行,但实际上没有任何更改):

INSERT INTO public.test_upsert (some_id, a, b, note) 
VALUES 
  ('c', 1, true, 'asdf')
ON CONFLICT (some_id)
DO UPDATE SET 
  a = excluded.a, 
  b = excluded.b, 
  note = excluded.note 
WHERE
  test_upsert.a IS DISTINCT FROM excluded.a OR 
  test_upsert.b IS DISTINCT FROM excluded.b OR
  test_upsert.note IS DISTINCT FROM excluded.note
RETURNING *;
Run Code Online (Sandbox Code Playgroud)

我的问题是:这个有简写吗?我基本上想在插入的任何列与现有列不同时插入新记录更新行。单独写出每一列会变得非常冗长,尽管它更明确。

Erw*_*ter 9

SQL 中没有规定说“除了这一列之外的所有列”。(您可以使用jsonb或来做类似的事情hstore。) 相关答案的详细信息:

再想一想,你不需要这个。您不妨更新所有列。无论如何,Postgres 都会写一个新的行版本,没有任何损失。

INSERT INTO test_upsert AS t (some_id, a, b, note)  -- list is optional
VALUES ('c', 5, true, 'asdf')
ON     CONFLICT (some_id)
DO     UPDATE
SET    (some_id, a, b, note) = ROW (excluded.*)  -- ROW syntax
WHERE  (t.*) IS DISTINCT FROM (excluded.*)       -- again, compare whole row
RETURNING *;
Run Code Online (Sandbox Code Playgroud)

您仍然需要为 的目标列表拼写一次列列表SET。这是必需的。其余的可以缩短。

INSERT无论如何,您甚至可以暂时忽略目标列表,同时针对所有列,但在大多数情况下,最好将其拼写出来,这样可以更有效地应对未来的变化。

表别名是可选的附加语法速记。请注意,与AS表别名的INSERT大多数地方不同,目标的表别名需要关键字。

或者更短,但是:

...
WHERE  t IS DISTINCT FROM excluded
...
Run Code Online (Sandbox Code Playgroud)

我们也可以只使用表名(或别名)作为(众所周知的!)复合类型。唯一的极端情况的缺点是:如果还有一个同名的列“t”(或“排除”),由于 Postgres 语法规则,它优先。(t.*) IS DISTINCT FROM (excluded.*)不会弄错,是稍微安全一点的形式。相关回答: