【问题标题】:Postgres function to show new column values after update as JSONPostgres 函数在更新为 JSON 后显示新列值
【发布时间】:2020-04-23 10:13:26
【问题描述】:

我正在尝试为AFTER UPDATE 触发器编写一个 postgres 函数,该函数创建一个 JSON 对象,其中包含仅更改的列的列名及其更新值。我正在尝试创建一个可用于任何表的通用函数,因此使用 Record 作为函数参数。 oldnew 来自触发器的 OLDNEW 变量。最终目标是将返回的 JSON 保存到审计表中的 JSON 字段中。

CREATE OR REPLACE FUNCTION row_updates(old RECORD, new RECORD) RETURNS JSON AS
$$
DECLARE
    updates JSON;
BEGIN
    WITH columns AS (
        SELECT json_object_keys(row_to_json(new)) "column"
    )
    SELECT
        json_object_agg("column", new_value) INTO updates
    FROM (
        SELECT 
            "column",
            (row_to_json(new)->"column" #>> '{}') as new_value,
            (row_to_json(old)->"column" #>> '{}') as old_value
        FROM
            columns
    ) changes
    WHERE 
        new_value IS DISTINCT FROM old_value;

    RETURN updates;
END;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION audit_change() RETURNS TRIGGER AS
$$
DECLARE
    updates JSON;
BEGIN
    IF (TG_OP = 'INSERT') THEN
        raise NOTICE 'Logging insert on relation (%.%) %', TG_TABLE_SCHEMA, TG_TABLE_NAME;

        RETURN NEW;
    ELSIF (TG_OP = 'UPDATE') THEN
        updates := row_updates(OLD, NEW)
        raise NOTICE 'Logging update on relation (%.%) %', TG_TABLE_SCHEMA, TG_TABLE_NAME, updates;
        RETURN NEW;
    ELSIF (TG_OP = 'DELETE') THEN
        raise NOTICE 'Logging delete on relation (%.%) %', TG_TABLE_SCHEMA, TG_TABLE_NAME;
        RETURN OLD;
    END IF;
END;
$$ LANGUAGE plpgsql;

我收到此错误: ERROR: PL/pgSQL functions cannot accept type record.

有没有更好的方法来做到这一点?显然我想在非触发器函数中执行它,以便我可以在不同表的不同触发器中重用它。另外,如果可能的话,我可能希望将其用于AFTER INSERTAFTER DELETE 触发器,因此我不想在触发器函数中分支TG_OP 并重复上面SQL 中显示的逻辑。

最终目标:

=> select * from org_user;

 id | org_id | user_id 
----+--------+---------
  1 |      1 |       1
 23 |      1 |       3

=> update org_user set org_id=3, user_id = 4 where id = 23; 

-- trigger creates following row in audit table

 id | relation  | record_id | updates 
----+-----------+-----------+--------------
  1 |  org_user |     23    | {'org_id': 3, 'user_id': 4} 

【问题讨论】:

    标签: json postgresql plpgsql jsonb postgresql-9.5


    【解决方案1】:

    创建一个比较两个JSONB 值的函数:

    create or replace function jsonb_diff(jsonb, jsonb)
    returns jsonb language sql immutable as $$
        select jsonb_object_agg(n.key, n.value)
        from jsonb_each($1) o
        join jsonb_each($2) n on o.key = n.key
        where o.value <> n.value;
    $$;
    

    并在您的触发函数中使用它:

        updates := jsonb_diff(to_jsonb(OLD), to_jsonb(NEW));
        raise NOTICE 'Logging update on relation (%.%) %', TG_TABLE_SCHEMA, TG_TABLE_NAME, updates;
        RETURN NEW;
    

    顺便说一句,在 Postgres 11+ 中,您可以使用 record 类型的函数参数。

    【讨论】:

    • 感谢您的回答。您能否提供 record 类型在 Postgres 11+ 中被允许作为函数参数的参考?
    • 恐怕我找不到关于它的官方说明。您可以通过比较 DbFiddle-10DbFiddle-11. 来检查这一点
    猜你喜欢
    • 2021-06-27
    • 2018-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多