PostgreSQL 触发器(Trigger)详解
1. 什么是触发器?
触发器(Trigger) 是 PostgreSQL 中的一种 自动执行 的数据库对象,当 表上的某些事件发生时(如 INSERT、UPDATE、DELETE),触发器会自动执行 定义好的触发器函数。
常见用途:
- 自动审计(记录数据变更)
- 强制业务逻辑约束
- 触发级联操作
- 实现复杂的验证规则
2. 触发器的基本语法
PostgreSQL 触发器由两部分组成:
- 触发器函数(Trigger Function):定义触发时要执行的逻辑。
- 触发器(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_id | first_name | last_name | salary |
|---|---|---|---|
| 1 | ALICE | Smith | 5000 |
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_id | emp_id | old_salary | new_salary | change_date |
|---|---|---|---|---|
| 1 | 1 | 5000 | 6000 | 2025-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 INSERT 或 AFTER UPDATE 触发器记录数据变更。
更多详细内容请关注其他相关文章!