如果 NEW 等于 OLD 没有触发器,Postgres 将跳过更新

Postgres skip update if NEW equals OLD without triggers

假设我有一个 table 我需要混合使用新的、更改的和未更改的现有数据进行更新(由于遗留原因,我们不能将它们分成 3 个不同的调用)。

最小的例子table是这样的:

(
    id        integer,
    name      text
        constraint name_unique
            unique,
    metadata  jsonb,
    version   integer default 0
);

需要发生 3 件不同的事情:

  1. 如果 name 不存在则插入新值 (INSERT ... ON CONFLICT (name) ...)
  2. 如果 name 已经存在并且 OLD.metadata <> NEW.metadata <- 这是我无法写下来的,则 metadata 更新 metadata 并使用 1 增加 version ON CONFLICT 语法,因为它仅 (?) 允许我访问 NEW 值作为 EXCLUDED 但不是 ROW.
  3. 中手头的值
  4. 如果OLD.metadata = NEW.metadata
  5. 跳过更新和版本提升

我目前最好的猜测是将 jsonb 转换为纯文本并查看两者是否相同,但我不知道是否可以在不必编写 [=24= 的情况下做到这一点] 和 TRIGGER.

这 3 件事可以在一个查询中执行吗?

我想我已经解决了这个问题。

INSERT
INTO example (id, name, metadata, version)
VALUES (DEFAULT, 'Random', '{}', DEFAULT)
on conflict(name) do
    update set
               metadata = excluded.metadata,
               version = example.version + 1
where
        example.name = excluded.name and
        cast(example.metadata as text) <> cast(excluded.metadata as text);

如果我有任何错误,请告诉我。

您可以在 ON CONFLICT ... DO UPDATE 子句中使用 EXCLUDED 引用新行版本:

INSERT INTO tab (name, metadata)
VALUES ('obadja', '{"silly": 42}')
ON CONFLICT ON name_unique
DO UPDATE SET metadata = EXCLUDED.metadata,
              version = tab.version + 1
WHERE tab.metadata IS DISTINCT FROM EXCLUDED.metadata;