PostgreSQL多个触发器和函数

6
我有以下的表格。 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        
);

当激活字段为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();

我还有一个触发器,会将记录写入历史表中。
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();

我已经以01和02作为前缀命名了触发器,这样我就知道它们将按照PostgreSQL以字母顺序触发的顺序执行。

我对这种方法有一些问题,因为我不确定它是否是良好的实践:

  1. 是否可能有一个BEFORE UPDATE on tbl_groups触发器来按特定顺序执行这2个函数?
  2. 是否更好只有一个函数首先检查“active”字段,如果它继续,则进行历史记录处理?所以我将只有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$

考虑到性能、代码维护等因素,你对此有何想法?

1个回答

6

有两个问题。 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();

接下来,tbl_groups_history中的id应该是序列化的:
CREATE TABLE tbl_groups_history (
    id serial NOT NULL,
    group_id integer NOT NULL,
    name varchar NOT NULL        
);

在您的情况下,没有理由使用多个触发器,因此第二种解决方案似乎更好。然而,第一个变体也应该可以工作。根据文档:
如果在同一关系上定义了同一事件的多个触发器,则触发器将按触发器名称的字母顺序触发。对于 BEFORE 和 INSTEAD OF 触发器,每个触发器返回的可能被修改的行成为下一个触发器的输入。如果任何 BEFORE 或 INSTEAD OF 触发器返回 NULL,则该行的操作将被放弃,并且不会触发后续触发器(对于该行)。

很有趣的是,如果触发器返回NULL,操作将被放弃,我错过了这一部分。因此,如果我有多个触发器,就需要更多的工作来搜索可能存在的错误。我有点感觉多个触发器对于这个目的来说有点太多了。至于触发器的名称,你是对的,那样更好。我还在函数前缀中加入了fn_,所以你提醒我必须在触发器上也使用trg_。 - koala

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接