PostgreSQL 触发器(Trigger)详解
                           
天天向上
发布: 2025-03-12 23:38:34

原创
765 人浏览过

1. 什么是触发器?

触发器(Trigger) 是 PostgreSQL 中的一种 自动执行 的数据库对象,当 表上的某些事件发生时(如 INSERT、UPDATE、DELETE),触发器会自动执行 定义好的触发器函数

常见用途:

  • 自动审计(记录数据变更)
  • 强制业务逻辑约束
  • 触发级联操作
  • 实现复杂的验证规则

2. 触发器的基本语法

PostgreSQL 触发器由两部分组成:

  1. 触发器函数(Trigger Function):定义触发时要执行的逻辑。
  2. 触发器(Trigger):定义触发的事件和作用范围。

创建触发器函数

触发器函数的 返回类型必须是 TRIGGER,且必须用 RETURN NEW;RETURN OLD; 结束。

CREATE OR REPLACE FUNCTION trigger_function_name()
RETURNS TRIGGER AS $$
BEGIN
    -- 触发器执行的逻辑
    RETURN NEW;  -- INSERT 或 UPDATE 需要返回 NEW,DELETE 需要返回 OLD
END;
$$ LANGUAGE plpgsql;

创建触发器

CREATE TRIGGER trigger_name
{ BEFORE | AFTER | INSTEAD OF } { INSERT | UPDATE | DELETE }
ON table_name
[ FOR EACH { ROW | STATEMENT } ]
EXECUTE FUNCTION trigger_function_name();

3. 触发器的类型

根据执行时机分类

触发器类型说明
BEFORE在数据操作(INSERT/UPDATE/DELETE)发生之前触发
AFTER在数据操作发生之后触发
INSTEAD OF用于 视图(VIEW),替代 INSERT/UPDATE/DELETE

根据触发粒度分类

触发器类型说明
FOR EACH ROW针对 每一行 数据执行触发器逻辑
FOR EACH STATEMENT只执行 一次,无论受影响的行数是多少

4. 触发器示例

4.1 插入前触发(BEFORE INSERT)

需求:

  • employees 表插入数据时,自动大写 first_name
CREATE TABLE employees (
    emp_id SERIAL PRIMARY KEY,
    first_name VARCHAR(50),
    last_name VARCHAR(50),
    salary DECIMAL(10,2)
);

创建触发器函数

CREATE OR REPLACE FUNCTION uppercase_first_name()
RETURNS TRIGGER AS $$
BEGIN
    NEW.first_name := UPPER(NEW.first_name);
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

创建触发器

CREATE TRIGGER before_insert_employee
BEFORE INSERT ON employees
FOR EACH ROW
EXECUTE FUNCTION uppercase_first_name();

测试

INSERT INTO employees (first_name, last_name, salary) 
VALUES ('alice', 'Smith', 5000);
SELECT * FROM employees;

结果

emp_idfirst_namelast_namesalary
1ALICESmith5000

4.2 更新后触发(AFTER UPDATE)

需求

  • 每当 employees 表的 salary 发生变更时,自动记录变更信息到 salary_changes 表。
CREATE TABLE salary_changes (
    change_id SERIAL PRIMARY KEY,
    emp_id INT,
    old_salary DECIMAL(10,2),
    new_salary DECIMAL(10,2),
    change_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

创建触发器函数

CREATE OR REPLACE FUNCTION log_salary_changes()
RETURNS TRIGGER AS $$
BEGIN
    INSERT INTO salary_changes (emp_id, old_salary, new_salary)
    VALUES (OLD.emp_id, OLD.salary, NEW.salary);
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

创建触发器

CREATE TRIGGER after_salary_update
AFTER UPDATE ON employees
FOR EACH ROW
WHEN (OLD.salary IS DISTINCT FROM NEW.salary)  -- 只有 salary 变化时触发
EXECUTE FUNCTION log_salary_changes();

测试

UPDATE employees SET salary = 6000 WHERE emp_id = 1;
SELECT * FROM salary_changes;

结果

change_idemp_idold_salarynew_salarychange_date
11500060002025-03-12 14:00:00

4.3 删除前触发(BEFORE DELETE)

需求

  • 阻止 employees 表中 salary > 10000 的员工被删除。

创建触发器函数

CREATE OR REPLACE FUNCTION prevent_high_salary_delete()
RETURNS TRIGGER AS $$
BEGIN
    IF OLD.salary > 10000 THEN
        RAISE EXCEPTION 'Cannot delete employee with high salary!';
    END IF;
    RETURN OLD;
END;
$$ LANGUAGE plpgsql;

创建触发器

CREATE TRIGGER before_delete_employee
BEFORE DELETE ON employees
FOR EACH ROW
EXECUTE FUNCTION prevent_high_salary_delete();

测试

DELETE FROM employees WHERE salary > 10000;

错误提示

ERROR:  Cannot delete employee with high salary!

5. 删除触发器

如果不再需要触发器,可以删除:

DROP TRIGGER trigger_name ON table_name;

示例:

DROP TRIGGER before_insert_employee ON employees;

如果要删除触发器函数:

DROP FUNCTION trigger_function_name();

6. PostgreSQL 触发器 vs 约束

特性触发器(Trigger)约束(Constraint)
作用范围复杂业务逻辑基本数据完整性
执行时机INSERT/UPDATE/DELETE 事件仅当数据违反规则时
可执行操作记录日志、修改数据、阻止操作仅能阻止不合法数据
是否支持复杂逻辑

如果只是 强制数据完整性(如唯一约束、非空约束),推荐使用 约束(Constraint),但如果涉及 审计、日志记录、自动填充等复杂逻辑,就要用 触发器(Trigger)


7. 触发器的最佳实践

尽量使用 BEFORE 触发器:可以减少 SQL 执行时间,因为 BEFORE 触发器可以在数据写入之前调整数据,减少事务回滚的开销。
避免使用 FOR EACH ROW 触发器处理大数据量:当表数据量较大时,建议使用 FOR EACH STATEMENT 触发器,减少性能开销。
谨慎修改或删除触发器:触发器可能影响数据完整性,修改或删除前应仔细评估。
配合日志表记录变更:对于审计日志,推荐 AFTER INSERTAFTER UPDATE 触发器记录数据变更。


更多详细内容请关注其他相关文章!

发表回复 0

Your email address will not be published. Required fields are marked *