Postgresql

僅更新 UPSERT 中更改的行的語法簡寫

  • December 27, 2020

我有一個查詢,當鍵衝突時,只有在其他列之一發生更改時才將其插入 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 *;

我的問題是:這個有速記嗎?當插入的任何列與現有列不同時,我基本上想插入一條新記錄更新行。單獨寫出每一列會變得非常冗長,儘管它更明確。

SQL 中沒有規定說*“除此之外的所有列”*。jsonb(你可以用or做類似的事情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 *;

您仍然需要為 的目標列表拼出一次SET列列表。這是必需的。其餘的可以縮短。

無論如何,您甚至可以INSERT在針對所有列的同時省略目標列表,但在大多數情況下,將其拼寫出來是一種很好的形式,對未來的變化更加健壯。

表別名是可選的附加語法簡寫。請注意,與大多數地方的表別名不同AS,目標的表別名需要關鍵字。INSERT

甚至更短,但是:

...
WHERE  t IS DISTINCT FROM excluded
...

我們也可以將表名(或別名)用於(眾所周知的!)複合類型。唯一的極端情況缺點:如果還有一個同名的列“t”(或“排除”),由於 Postgres 語法規則,它優先。(t.*) IS DISTINCT FROM (excluded.*)不會弄錯,是稍微安全一點的形式。相關答案:

引用自:https://dba.stackexchange.com/questions/282197