繁体   English   中英

Postgresql触发器仅记录受影响列的更改

[英]Postgresql Trigger log only changes of affected columns

根据一篇关于在 PostgreSQL 中跟踪更改的有用文章,我得到了一个运行良好的触发器函数,它跟踪与该触发器函数关联的所有表的更改。 该函数将整个 OLD 和 NEW 行内容存储为 JSON,这很好,但是对于很多列,很难大致了解真正的变化。

我正在寻找一种方法来仅存储 NEW 和 OLD 值已更改的列的内容。 因此,我添加了一个新列“更改”。

这是存储历史记录的表:

CREATE TABLE public.t_history
(
  id integer serial,
  tstamptz timestamp with time zone DEFAULT now(),
  schemaname text,
  tabname text,
  operation text,
  who text DEFAULT "current_user"(),
  changes  text,
  new_val json,
  old_val json,
  CONSTRAINT pk_t_history PRIMARY KEY (id)
)

由于触发器函数非常通用,我想使用一种方法来比较两个 json-colums new_val 和 old_val,或者有一种方法使用 foreach 循环来查看每列而不指定硬编码列名。 结果,真正的更改应该存储在“更改”列中。

这是触发器函数的样子:

REATE OR REPLACE FUNCTION public.change_trigger()
  RETURNS trigger AS
$BODY$
        BEGIN
                IF      TG_OP = 'INSERT'
                THEN
                        INSERT INTO public.t_history (tabname, schemaname, operation, new_val)
                                VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, row_to_json(NEW));
                        RETURN NEW;

                ELSIF   TG_OP = 'UPDATE'
                THEN
                        INSERT INTO public.t_history (tabname, schemaname, operation, new_val, old_val)
                                VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP,
                                        row_to_json(NEW), row_to_json(OLD));
                        RETURN NEW;
                ELSIF   TG_OP = 'DELETE'
                THEN
                        INSERT INTO public.t_history (tabname, schemaname, operation, old_val)
                                VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, row_to_json(OLD));
                        RETURN OLD;
                END IF;
        END;

$BODY$
  LANGUAGE plpgsql VOLATILE SECURITY DEFINER
  COST 100;

是的,你真的需要升级。 9.2 中关于此的可能性相当有限。 因此,以下答案假定最低为 9.4,这是目前仍受支持的最低版本。


您可以使用json_each_text()生成一组键值对,然后过滤具有相同键的不同值。

然后可以从那里使用建立一些JSON,为每列例如一个JSON对象json_build_object()和使用它们聚集在一个JSON阵列json_agg() 您没有指定所需的确切格式。 所以这只是一个例子。 您可能会在那里提出不同的想法。

SELECT json_agg(json_build_object('column',
                                  old2.key,
                                  'old',
                                  old2.value,
                                  'new',
                                  new2.value))
       FROM (VALUES (1,
                     2,
                     3)) old(i,
                             j,
                             k)
            CROSS JOIN (VALUES (1,
                                3,
                                4)) new(i,
                                        j,
                                        k)
            CROSS JOIN LATERAL json_each_text(row_to_json(old)) old2
            CROSS JOIN LATERAL json_each_text(row_to_json(new)) new2
       WHERE old2.key = new2.key
             AND old2.value <> new2.value;

数据库<>小提琴

注意: VALUES x VALUES交叉连接仅用于演示,因此我有一个new和一个old ,毕竟我不在触发器中。 当然,在触发器中,您已经有了newold ,可以跳过它并使用FROM子句,例如:

FROM json_each_text(row_to_json(old)) old2
     CROSS JOIN json_each_text(row_to_json(new)) new2

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM