【问题标题】:How to obtain primary key value in trigger function if primary key column name is unknown?如果主键列名未知,如何在触发函数中获取主键值?
【发布时间】:2020-01-31 12:00:22
【问题描述】:

我正在使用 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”列)。 所以我需要以某种方式找出如何在我的触发器函数中使用主键,不管它有哪个列名。 我怎样才能做到这一点?

【问题讨论】:

  • 我认为您必须使用 information_schema.columns 为每个表动态生成它来识别键..
  • 查看herehere 了解通用审计触发器的两种解决方案

标签: sql postgresql


【解决方案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');

【讨论】:

    【解决方案2】:

    我使用了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,并选择带有col 的值多变的。这应该适用于BEFOREAFTER 触发器。如果在DELETE 上触发,请将row_to_json(NEW) 更改为row_to_json(OLD)

    【讨论】:

    • 请注意,此答案看起来更简单,但不适用于具有组合主键的表。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多