1. PostgreSQL触发器基础概念解析
PostgreSQL触发器是一种强大的数据库功能,它允许我们在特定数据库事件发生时自动执行预定义的函数。触发器本质上是一个与表相关联的回调函数,当表发生指定事件(如INSERT、UPDATE或DELETE)时,这个函数就会被自动调用。
触发器最常见的应用场景包括:
- 数据验证和完整性检查
- 审计日志记录
- 自动计算派生数据
- 跨表数据同步
- 业务规则强制执行
在PostgreSQL中,触发器可以分为几种不同类型:
- 行级触发器:针对受影响的每一行数据都会触发一次
- 语句级触发器:无论影响多少行数据,每个SQL语句只触发一次
- BEFORE触发器:在操作执行前触发
- AFTER触发器:在操作执行后触发
- INSTEAD OF触发器:主要用于视图,替代原始操作
重要提示:触发器函数必须在创建触发器之前定义好,且必须声明为返回trigger类型,不接受任何参数。
2. 创建触发器的完整步骤
2.1 定义触发器函数
首先我们需要创建一个触发器函数,这是触发器的核心逻辑所在。下面是一个简单的示例:
sql复制CREATE OR REPLACE FUNCTION update_modified_timestamp()
RETURNS TRIGGER AS $$
BEGIN
NEW.modified_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
这个函数会在每次更新记录时自动设置modified_at字段为当前时间。注意几点:
- 函数必须返回TRIGGER类型
- 对于BEFORE INSERT/UPDATE触发器,可以通过修改NEW记录来改变将要插入/更新的数据
- 必须返回NEW或NULL(NULL表示跳过当前操作)
2.2 创建触发器
定义好函数后,我们就可以创建触发器了:
sql复制CREATE TRIGGER update_user_timestamp
BEFORE UPDATE ON users
FOR EACH ROW
EXECUTE FUNCTION update_modified_timestamp();
这个触发器会在users表的每次更新操作前触发,调用我们刚才定义的函数。
触发器定义的关键部分:
BEFORE UPDATE:指定触发时机和事件ON users:指定关联的表FOR EACH ROW:声明为行级触发器EXECUTE FUNCTION:指定要执行的函数
2.3 触发器的高级选项
PostgreSQL触发器还支持一些高级特性:
条件触发:使用WHEN子句可以指定触发条件
sql复制CREATE TRIGGER check_salary
BEFORE INSERT OR UPDATE ON employees
FOR EACH ROW
WHEN (NEW.salary < 0)
EXECUTE FUNCTION reject_negative_salary();
多事件触发:一个触发器可以响应多种事件
sql复制CREATE TRIGGER log_changes
AFTER INSERT OR UPDATE OR DELETE ON products
FOR EACH ROW
EXECUTE FUNCTION log_product_changes();
触发器顺序控制:通过指定约束条件控制多个触发器的执行顺序
sql复制CREATE TRIGGER validate_data
BEFORE INSERT ON orders
FOR EACH ROW
EXECUTE FUNCTION validate_order_data()
FOLLOWS some_other_trigger;
3. 常见业务场景应用实例
3.1 审计日志记录
审计日志是触发器的经典应用场景。我们可以创建一个触发器来自动记录所有对敏感数据的修改:
sql复制CREATE TABLE audit_log (
id SERIAL PRIMARY KEY,
table_name TEXT NOT NULL,
operation TEXT NOT NULL,
old_data JSONB,
new_data JSONB,
changed_by TEXT,
changed_at TIMESTAMP DEFAULT NOW()
);
CREATE OR REPLACE FUNCTION log_audit_trail()
RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'INSERT' THEN
INSERT INTO audit_log(table_name, operation, new_data, changed_by)
VALUES (TG_TABLE_NAME, TG_OP, to_jsonb(NEW), current_user);
EL
