如果主键列名称未知,如何在触发器函数中获取主键值?

时间:2017-06-30 12:02:32

标签: sql postgresql

我正在使用postgres。 假设我要创建以下触发器:

CREATE OR REPLACE FUNCTION auditlogfunc() RETURNS TRIGGER AS $example_table$
   BEGIN
      INSERT INTO AUDIT(EMP_ID, ENTRY_DATE) VALUES (new.ID, current_timestamp);
      RETURN NEW;
   END;
$example_table$ LANGUAGE plpgsql;

我想在许多表中使用此触发器,而不是每个表都有名为“id”的主键(表中根本没有'id'列)。 所以我需要以某种方式找出如何在我的触发器函数中使用主键,无论它具有哪个列名。 我怎样才能做到这一点?

2 个答案:

答案 0 :(得分:1)

你的PostgreSQL版本是什么?您必须查询PostgreSQL目录才能实现您想要的目标。见下面的脚本:

我假设您的PostgreSQL支持JSONB(9.4 +)。

CREATE TABLE public.test_table 
(
    id BIGINT NOT NULL,
    a_column TEXT NOT NULL,
    CONSTRAINT test_tablepkey PRIMARY KEY (id)
);

CREATE OR REPLACE FUNCTION auditlogfunc() RETURNS TRIGGER AS $example_table$
DECLARE
    reg_id JSONB; 
    affected_row JSON;   
BEGIN

    IF TG_OP IN('INSERT', 'UPDATE') THEN
        affected_row := row_to_json(NEW);
    ELSE
        affected_row := row_to_json(OLD);
    END IF;

    --Get PK columns
    --You may want to extract this to a SQL function
    WITH pk_columns (attname) AS (
        SELECT 
            CAST(a.attname AS TEXT) 
        FROM 
            pg_index i 
            JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey) 
        WHERE 
            i.indrelid = TG_RELID 
            AND i.indisprimary
    )
    SELECT 
        json_object_agg(key, value) INTO reg_id
    FROM 
        json_each_text(affected_row)
    WHERE 
        key IN(SELECT attname FROM pk_columns);

    --Raise collected PK
    RAISE INFO 'PK: %', reg_id;

    --TODO: your insert into audit table goes here
    RETURN NEW;
END;
$example_table$ LANGUAGE plpgsql;

CREATE TRIGGER tg_audit_cadprodu_row AFTER INSERT OR UPDATE OR DELETE
  ON public.test_table FOR EACH ROW EXECUTE PROCEDURE public.auditlogfunc();

--A simple test
INSERT INTO test_table VALUES (CAST((random() * 10000) AS INTEGER), 'Test');

答案 1 :(得分:0)

我拿了Michel Milezzi's answer并将其清理干净,因此它只返回我们需要的内容,并且在更简洁的语句中具有更好的性能。在Postgres v12.1上测试。我想它可以在9.4+上运行。

CREATE OR REPLACE FUNCTION auditlogfunc() RETURNS TRIGGER AS $example_table$
    DECLARE
        col TEXT = (
            SELECT attname
            FROM pg_index
            JOIN pg_attribute ON 
                attrelid = indrelid 
                AND attnum = ANY(indkey) 
            WHERE indrelid = TG_RELID AND indisprimary
        );
   BEGIN
      INSERT INTO AUDIT(EMP_ID, ENTRY_DATE) VALUES ((row_to_json(NEW) ->> col), current_timestamp);
      RETURN NEW;
   END;
$example_table$ LANGUAGE plpgsql;

首先,我要查询表的主键列的名称,并将其存储在col变量中。我用特殊变量TG_RELID过滤查询,该变量是引起触发器的表的对象ID。 (请参见Postgres Docs。)

然后我们可以将其插入审核表中。我对这个问题所做的更改只是new.ID。现在我们知道主键列的名称,可以将新行(将(或已经)插入,更新或删除的行)转换为json,然后使用{{1 }}变量。这对colBEFORE触发器均适用。如果在AFTER上触发,请将DELETE更改为row_to_json(NEW)