PostgreSQL 多个触发器和函数

koa*_*ala 6 sql postgresql triggers

我有以下表格。

tbl_groups包含所有当前数据,当名称发生更改时,我会将其存储在历史表中。

CREATE TABLE tbl_groups (
    id integer NOT NULL,
    name varchar NOT NULL,
    active boolean NOT NULL
);

CREATE TABLE tbl_groups_history (
    id integer NOT NULL,
    group_id integer NOT NULL,
    name varchar NOT NULL        
);
Run Code Online (Sandbox Code Playgroud)

当活动字段为 false 时,可能不会发生更新。因此,我创建了一个触发器,以便在发生这种情况时抛出异常:

CREATE OR REPLACE FUNCTION fn_group_deny_update_when_inactive() RETURNS TRIGGER AS
$BODY$
    BEGIN
        IF 
            OLD.active = false
        THEN 
            RAISE EXCEPTION 'Record is inactive';
        END IF;
        RETURN NEW;
    END;
$BODY$
LANGUAGE plpgsql;

CREATE TRIGGER 01_group_can_update
BEFORE UPDATE ON tbl_groups
FOR EACH ROW
EXECUTE PROCEDURE fn_group_deny_update_when_inactive();
Run Code Online (Sandbox Code Playgroud)

我还有一个触发器,可以将记录写入历史表

CREATE OR REPLACE FUNCTION fn_group_log_history() RETURNS trigger as 
$BODY$
    BEGIN
        IF
           (NEW.name <> OLD.name)       
        THEN
            INSERT INTO tbl_groups_history (group_id, name)
            VALUES (OLD.id, OLD.name);
        END IF;
        RETURN NEW;
    END;
$BODY$
LANGUAGE plpgsql;

CREATE TRIGGER 02_group_write_history_log
BEFORE UPDATE ON tbl_groups
FOR EACH ROW
EXECUTE PROCEDURE fn_group_log_history();
Run Code Online (Sandbox Code Playgroud)

我使用 01 和 02 作为前缀命名触发器,因此我知道当 PostgreSQL 按字母顺序触发它们时,它们将按什么顺序执行。

我对这种方法有一些疑问,因为我不确定这是否是好的做法:

  1. 是否可以有一个 BEFORE UPDATE on tbl_groups 触发器以特定顺序执行这两个函数?
  2. 是否最好只有一个函数首先检查“活动”字段,如果继续检查,则执行历史记录?所以我只有 1 个(更大的)函数和 1 个触发器。

一切尽在一个函数中:

 CREATE OR REPLACE FUNCTION fn_group_before_update() RETURNS trigger as 
$BODY$
    BEGIN
        IF 
            OLD.active = false
        THEN 
            RAISE EXCEPTION 'Record is inactive';
        END IF;
        IF
           (NEW.name <> OLD.name)       
        THEN
            INSERT INTO tbl_groups_history (group_id, name)
            VALUES (OLD.id, OLD.name);
        END IF;
        RETURN NEW;
    END;
$BODY$
Run Code Online (Sandbox Code Playgroud)

考虑到性能、代码维护等等,对此有什么想法吗?

kli*_*lin 7

两个问题。01_group_can_update不是一个正确的名称,因此将其更改为例如:

CREATE TRIGGER trg_01_group_can_update
BEFORE UPDATE ON tbl_groups
FOR EACH ROW
EXECUTE PROCEDURE fn_group_deny_update_when_inactive();
Run Code Online (Sandbox Code Playgroud)

接下来,id输入tbl_groups_history应该是串行的:

CREATE TABLE tbl_groups_history (
    id serial NOT NULL,
    group_id integer NOT NULL,
    name varchar NOT NULL        
);
Run Code Online (Sandbox Code Playgroud)

在您的情况下没有理由有多个触发器,因此第二种解决方案似乎更好。然而,第一个变体也应该有效。根据文档

如果为同一关系上的同一事件定义了多个触发器,则触发器将按触发器名称的字母顺序触发。对于 BEFORE 和 INSTEAD OF 触发器,每个触发器返回的可能修改的行将成为下一个触发器的输入。如果任何 BEFORE 或 INSTEAD OF 触发器返回 NULL,则放弃该行的操作,并且不会触发后续触发器(对于该行)。