在 json/jsonb 行上使用 IS DISTINCT FROM 触发而不进行比较逐项?

not*_*ain 6 postgresql trigger plpgsql json postgresql-9.6

当表具有 json 或 jsonb 列时,是否无法在 INSERT/UPDATE 触发器中进行全行比较?我收到的错误让我认为我需要单独比较每一列,因为该NEW.*/OLD.*格式不能用于比较 json 列。

CREATE OR REPLACE FUNCTION on_insert_update_modified()
RETURNS TRIGGER AS $$
BEGIN
  IF row(NEW.*) IS DISTINCT FROM row(OLD.*) THEN
    NEW.modified_at = now();
    RETURN NEW;
  ELSE
    RETURN OLD;
  END IF;
END;
$$ language 'plpgsql';

CREATE TRIGGER mytable_insert_update
BEFORE INSERT OR UPDATE ON mytable
FOR EACH ROW
EXECUTE PROCEDURE on_insert_update_modified();
Run Code Online (Sandbox Code Playgroud)

当我插入包含 json 和 jsonb 列的表时,出现以下错误:

CREATE OR REPLACE FUNCTION on_insert_update_modified()
RETURNS TRIGGER AS $$
BEGIN
  IF row(NEW.*) IS DISTINCT FROM row(OLD.*) THEN
    NEW.modified_at = now();
    RETURN NEW;
  ELSE
    RETURN OLD;
  END IF;
END;
$$ language 'plpgsql';

CREATE TRIGGER mytable_insert_update
BEFORE INSERT OR UPDATE ON mytable
FOR EACH ROW
EXECUTE PROCEDURE on_insert_update_modified();
Run Code Online (Sandbox Code Playgroud)

有更好的办法解决这个问题吗?

Erw*_*ter 5

直接问题:类型json

没有为 Postgres 数据类型定义相等或不等运算符json。看:

因此,涉及列的行比较json也必然会失败 - 有明显的例外,请参见下文。

有(不完美!)解决方法,但不必费心并切换到jsonb,它允许这些检查。

修复功能和触发器

您也无法比较OLDand NEWforINSERT运算,其中没有OLD- only for UPDATEor DELETE。我建议使用 的列默认值modified_at,或单独的触发函数和触发器BEFORE INSERTnow()无条件覆盖任何输入。

这个函数和触发器UPDATE

CREATE OR REPLACE FUNCTION on_update_modified()
  RETURNS trigger
  LANGUAGE plpgsql AS
$func$
BEGIN
   IF NEW IS DISTINCT FROM OLD THEN
      NEW.modified_at = now();
      RETURN NEW;
   ELSE
      RETURN NULL;  -- skip empty update, see below
   END IF;
END
$func$;

CREATE TRIGGER mytable_update  -- doesn't work!
BEFORE UPDATE ON tbl_js
FOR EACH ROW EXECUTE PROCEDURE on_update_modified();
Run Code Online (Sandbox Code Playgroud)

db<>在这里摆弄(Postgres 9.6)

Postgres 13 中没有什么不同:db<>fiddle这里

RETURN NULL;是可选的。如果该行根本没有更改(并且无论如何已经运行触发器函数),您也可以跳过该UPDATE行(如果它没有更改)。这节省了徒劳地编写新行版本的大量成本。如果其他触发器依赖于它,或者对于其他奇异的边缘情况,您可能仍然想继续使用它。然后RETURN NEW;- 无条件地在触发函数结束时。RETURN OLD;在我们确定两行值相同后,将没有任何好处。

行与列的比较json还能用吗?

奇怪的是,是的。行比较是从左到右处理的。对于表达式NEW IS DISTINCT FROM OLDPostgres 可以true在找到第一个不匹配时立即返回(在遇到问题之前json)。这可能会也可能不会发生,具体取决于代码路径。引用有关逐行比较的手册

笔记

如果使用较早的列解决比较,则可能不会发生与元素数量或类型相关的错误。