PostgreSQL教程FG155-PG触发器函数:PL/pgSQL编写实操
本文档风哥主要介绍PostgreSQL数据库触发器函数的编写方法,包括行级触发器函数、语句级触发器函数和事件触发器函数的实现,风哥教程参考PostgreSQL官方文档PL/pgSQL、Triggers等内容,适合数据库开发人员和DBA在生产环境中编写触发器函数实现复杂业务逻辑。
Part01-基础概念与理论知识
1.1 PostgreSQL数据库触发器函数概念
触发器函数是由触发器调用的特殊函数,用于响应数据库事件。更多视频教程www.fgedu.net.cn。触发器函数可以访问触发器触发时的上下文信息,如OLD和NEW记录、触发时机、触发事件类型等。
- 返回类型为TRIGGER或event_trigger
- 可以访问触发器上下文变量
- 可以修改NEW记录的值
- 可以通过RAISE语句抛出异常
- 可以执行任意PL/pgSQL代码
1.2 PostgreSQL数据库触发器函数结构
触发器函数的基本结构包括:函数声明、变量声明、主体逻辑、返回语句。学习交流加群风哥微信: itpux-com。触发器函数的返回值取决于触发器的类型和触发时机。
1.3 PostgreSQL数据库触发器函数变量
触发器函数可以使用以下特殊变量:
- OLD:行级触发器中,包含更新或删除前的旧记录
- NEW:行级触发器中,包含插入或更新后的新记录
- TG_NAME:触发器名称
- TG_WHEN:触发时机(BEFORE、AFTER、INSTEAD OF)
- TG_LEVEL:触发级别(ROW、STATEMENT)
- TG_OP:触发操作(INSERT、UPDATE、DELETE)
- TG_TABLE_NAME:触发表名
- TG_TABLE_SCHEMA:触发表所在模式
Part02-生产环境规划与建议
2.1 PostgreSQL数据库触发器函数设计原则
触发器函数设计原则:单一职责,每个函数只做一件事;性能优先,避免复杂操作;错误处理,提供清晰的错误信息;可维护性,编写清晰的代码和文档;安全性,避免权限问题。
2.2 PostgreSQL数据库触发器函数性能优化
性能优化建议:避免在触发器函数中执行大量操作;使用批量处理;合理使用索引;避免长事务;监控触发器函数执行时间。
2.3 PostgreSQL数据库触发器函数安全考虑
安全考虑:使用SECURITY DEFINER时要谨慎;确保函数有适当的权限;避免信息泄露;防止SQL注入。
Part03-生产环境项目实施方案
3.1 PostgreSQL数据库行级触发器函数
3.1.1 行级触发器函数创建
— 创建测试表
CREATE TABLE fgedu_products (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
price NUMERIC(10,2) NOT NULL,
stock INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
— 输出结果
CREATE TABLE
— 创建行级触发器函数
CREATE OR REPLACE FUNCTION fgedu_product_audit()
RETURNS TRIGGER
AS $$
BEGIN
— 更新时间戳
NEW.updated_at := NOW();
— 检查价格
IF NEW.price <= 0 THEN
RAISE EXCEPTION 'Price must be greater than 0';
END IF;
-- 检查库存
IF NEW.stock < 0 THEN
RAISE EXCEPTION 'Stock cannot be negative';
END IF;
-- 记录操作日志
IF TG_OP = 'INSERT' THEN
RAISE NOTICE 'Inserting product: %', NEW.name;
ELSIF TG_OP = 'UPDATE' THEN
RAISE NOTICE 'Updating product: %', NEW.name;
RAISE NOTICE 'Old price: %, New price: %', OLD.price, NEW.price;
ELSIF TG_OP = 'DELETE' THEN
RAISE NOTICE 'Deleting product: %', OLD.name;
RETURN OLD;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- 输出结果
CREATE FUNCTION
-- 创建BEFORE行级触发器
CREATE TRIGGER trg_product_before
BEFORE INSERT OR UPDATE OR DELETE ON fgedu_products
FOR EACH ROW
EXECUTE FUNCTION fgedu_product_audit();
-- 输出结果
CREATE TRIGGER
-- 测试行级触发器函数
INSERT INTO fgedu_products(name, price, stock)
VALUES('iPhone 15', 7999.00, 100);
-- 输出结果
NOTICE: Inserting product: iPhone 15
INSERT 0 1
-- 测试更新操作
UPDATE fgedu_products SET price = 8999.00, stock = 50 WHERE id = 1;
-- 输出结果
NOTICE: Updating product: iPhone 15
NOTICE: Old price: 7999.00, New price: 8999.00
UPDATE 1
-- 测试错误处理
UPDATE fgedu_products SET price = -100 WHERE id = 1;
-- 输出结果
ERROR: Price must be greater than 0
-- 测试库存检查
UPDATE fgedu_products SET stock = -10 WHERE id = 1;
-- 输出结果
ERROR: Stock cannot be negative
-- 查看数据
SELECT * FROM fgedu_products;
-- 输出结果
id | name | price | stock | created_at | updated_at
----+-----------+--------+-------+----------------------------+----------------------------
1 | iPhone 15 | 8999.00| 50 | 2026-04-07 18:00:00.123456 | 2026-04-07 18:05:00.678901
(1 row)
3.2 PostgreSQL数据库语句级触发器函数
3.2.1 语句级触发器函数创建
— 创建操作日志表
CREATE TABLE fgedu_statement_log (
id BIGSERIAL PRIMARY KEY,
table_name VARCHAR(100) NOT NULL,
operation VARCHAR(20) NOT NULL,
row_count INTEGER,
operation_time TIMESTAMP DEFAULT NOW(),
user_name VARCHAR(100) DEFAULT CURRENT_USER,
details TEXT
);
— 输出结果
CREATE TABLE
— 创建语句级触发器函数
CREATE OR REPLACE FUNCTION fgedu_statement_audit()
RETURNS TRIGGER
AS $$
DECLARE
v_row_count INTEGER;
BEGIN
GET DIAGNOSTICS v_row_count = ROW_COUNT;
INSERT INTO fgedu_statement_log(
table_name,
operation,
row_count,
details
)
VALUES(
TG_TABLE_NAME,
TG_OP,
v_row_count,
‘Statement: ‘ || TG_OP || ‘ on ‘ || TG_TABLE_NAME || ‘ affected ‘ || v_row_count || ‘ rows’
);
RAISE NOTICE ‘Operation % on % affected % rows’, TG_OP, TG_TABLE_NAME, v_row_count;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
— 输出结果
CREATE FUNCTION
— 创建AFTER语句级触发器
CREATE TRIGGER trg_statement_audit
AFTER INSERT OR UPDATE OR DELETE ON fgedu_products
FOR EACH STATEMENT
EXECUTE FUNCTION fgedu_statement_audit();
— 输出结果
CREATE TRIGGER
— 测试语句级触发器函数
INSERT INTO fgedu_products(name, price, stock)
VALUES
(‘iPad Air’, 4999.00, 80),
(‘MacBook Pro’, 14999.00, 30),
(‘AirPods Pro’, 1999.00, 200);
— 输出结果
NOTICE: Inserting product: iPad Air
NOTICE: Inserting product: MacBook Pro
NOTICE: Inserting product: AirPods Pro
NOTICE: Operation INSERT on fgedu_products affected 3 rows
INSERT 0 3
— 测试批量更新
UPDATE fgedu_products SET price = price * 1.05 WHERE stock > 50;
— 输出结果
NOTICE: Updating product: iPad Air
NOTICE: Old price: 4999.00, New price: 5248.95
NOTICE: Updating product: AirPods Pro
NOTICE: Old price: 1999.00, New price: 2098.95
NOTICE: Operation UPDATE on fgedu_products affected 2 rows
UPDATE 2
— 测试删除操作
DELETE FROM fgedu_products WHERE stock < 40;
-- 输出结果
NOTICE: Deleting product: MacBook Pro
NOTICE: Operation DELETE on fgedu_products affected 1 rows
DELETE 1
-- 查看操作日志
SELECT table_name, operation, row_count, operation_time, user_name
FROM fgedu_statement_log
ORDER BY operation_time DESC
LIMIT 5;
-- 输出结果
table_name | operation | row_count | operation_time | user_name
-------------+-----------+-----------+------------------------+----------
fgedu_products | DELETE | 1 | 2026-04-07 18:15:00 | fgedu
fgedu_products | UPDATE | 2 | 2026-04-07 18:10:00 | fgedu
fgedu_products | INSERT | 3 | 2026-04-07 18:05:00 | fgedu
(3 rows)
3.3 PostgreSQL数据库事件触发器函数
3.3.1 事件触发器函数创建
— 创建DDL操作日志表
CREATE TABLE fgedu_ddl_log (
id BIGSERIAL PRIMARY KEY,
event_type VARCHAR(50) NOT NULL,
command_tag VARCHAR(100) NOT NULL,
object_type VARCHAR(50),
object_name VARCHAR(200),
operation_time TIMESTAMP DEFAULT NOW(),
user_name VARCHAR(100) DEFAULT CURRENT_USER,
sql_command TEXT
);
— 输出结果
CREATE TABLE
— 创建事件触发器函数
CREATE OR REPLACE FUNCTION fgedu_ddl_audit()
RETURNS event_trigger
AS $$
DECLARE
obj RECORD;
BEGIN
RAISE NOTICE ‘Event trigger fired: %’, TG_EVENT;
RAISE NOTICE ‘Command tag: %’, TG_TAG;
FOR obj IN SELECT * FROM pg_event_trigger_ddl_commands()
LOOP
INSERT INTO fgedu_ddl_log(
event_type,
command_tag,
object_type,
object_name,
sql_command
)
VALUES(
TG_EVENT,
TG_TAG,
obj.object_type,
obj.object_identity,
current_query()
);
RAISE NOTICE ‘Object: % – %’, obj.object_type, obj.object_identity;
END LOOP;
END;
$$ LANGUAGE plpgsql;
— 输出结果
CREATE FUNCTION
— 创建事件触发器
CREATE EVENT TRIGGER trg_ddl_audit
ON ddl_command_end
EXECUTE FUNCTION fgedu_ddl_audit();
— 输出结果
CREATE EVENT TRIGGER
— 测试事件触发器函数
CREATE TABLE fgedu_test_event (
id SERIAL PRIMARY KEY,
name VARCHAR(100)
);
— 输出结果
NOTICE: Event trigger fired: ddl_command_end
NOTICE: Command tag: CREATE TABLE
NOTICE: Object: table – public.fgedu_test_event
CREATE TABLE
— 创建索引
CREATE INDEX idx_test_event_name ON fgedu_test_event(name);
— 输出结果
NOTICE: Event trigger fired: ddl_command_end
NOTICE: Command tag: CREATE INDEX
NOTICE: Object: index – public.idx_test_event_name
CREATE INDEX
— 修改表
ALTER TABLE fgedu_test_event ADD COLUMN value INTEGER;
— 输出结果
NOTICE: Event trigger fired: ddl_command_end
NOTICE: Command tag: ALTER TABLE
NOTICE: Object: table – public.fgedu_test_event
ALTER TABLE
— 查看DDL日志
SELECT event_type, command_tag, object_type, object_name, operation_time
FROM fgedu_ddl_log
ORDER BY operation_time DESC
LIMIT 5;
— 输出结果
event_type | command_tag | object_type | object_name | operation_time
—————+—————+————-+————————+————————
ddl_command_end| ALTER TABLE | table | public.fgedu_test_event | 2026-04-07 18:20:00
ddl_command_end| CREATE INDEX | index | public.idx_test_event_name | 2026-04-07 18:19:00
ddl_command_end| CREATE TABLE | table | public.fgedu_test_event | 2026-04-07 18:18:00
(3 rows)
Part04-生产案例与实战讲解
4.1 PostgreSQL数据库审计触发器函数实战
本案例演示审计触发器函数的实现。学习交流加群风哥QQ113257174。
— 创建审计日志表
CREATE TABLE fgedu_audit_detail (
id BIGSERIAL PRIMARY KEY,
table_name VARCHAR(100) NOT NULL,
operation VARCHAR(20) NOT NULL,
row_id INTEGER,
old_data JSONB,
new_data JSONB,
operation_time TIMESTAMP DEFAULT NOW(),
user_name VARCHAR(100) DEFAULT CURRENT_USER,
client_ip INET DEFAULT inet_client_addr(),
fgapplication_name VARCHAR(100) DEFAULT current_setting(‘fgapplication_name’)
);
— 输出结果
CREATE TABLE
— 创建索引
CREATE INDEX idx_audit_detail_table ON fgedu_audit_detail(table_name);
CREATE INDEX idx_audit_detail_time ON fgedu_audit_detail(operation_time);
CREATE INDEX idx_audit_detail_row ON fgedu_audit_detail(table_name, row_id);
— 输出结果
CREATE INDEX
CREATE INDEX
CREATE INDEX
— 创建通用审计触发器函数
CREATE OR REPLACE FUNCTION fgedu_generic_audit()
RETURNS TRIGGER
AS $$
BEGIN
IF TG_OP = ‘INSERT’ THEN
INSERT INTO fgedu_audit_detail(
table_name,
operation,
row_id,
new_data
)
VALUES(
TG_TABLE_NAME,
TG_OP,
NEW.id,
to_jsonb(NEW)
);
RETURN NEW;
ELSIF TG_OP = ‘UPDATE’ THEN
INSERT INTO fgedu_audit_detail(
table_name,
operation,
row_id,
old_data,
new_data
)
VALUES(
TG_TABLE_NAME,
TG_OP,
NEW.id,
to_jsonb(OLD),
to_jsonb(NEW)
);
RETURN NEW;
ELSIF TG_OP = ‘DELETE’ THEN
INSERT INTO fgedu_audit_detail(
table_name,
operation,
row_id,
old_data
)
VALUES(
TG_TABLE_NAME,
TG_OP,
OLD.id,
to_jsonb(OLD)
);
RETURN OLD;
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
— 输出结果
CREATE FUNCTION
— 为产品表添加审计触发器
CREATE TRIGGER trg_audit_products
AFTER INSERT OR UPDATE OR DELETE ON fgedu_products
FOR EACH ROW
EXECUTE FUNCTION fgedu_generic_audit();
— 输出结果
CREATE TRIGGER
— 测试审计触发器函数
UPDATE fgedu_products SET price = 9999.00 WHERE id = 1;
— 输出结果
NOTICE: Updating product: iPhone 15
NOTICE: Old price: 8999.00, New price: 9999.00
NOTICE: Operation UPDATE on fgedu_products affected 1 rows
UPDATE 1
— 查看审计日志
SELECT table_name, operation, row_id, old_data->>’price’ AS old_price, new_data->>’price’ AS new_price, operation_time, user_name
FROM fgedu_audit_detail
WHERE table_name = ‘fgedu_products’
ORDER BY operation_time DESC
LIMIT 5;
— 输出结果
table_name | operation | row_id | old_price | new_price | operation_time | user_name
————-+———–+——–+———–+———–+————————+———-
fgedu_products | UPDATE | 1 | 8999.00 | 9999.00 | 2026-04-07 18:25:00 | fgedu
(1 row)
— 插入测试数据
INSERT INTO fgedu_products(name, price, stock)
VALUES(‘Apple Watch’, 2999.00, 150);
— 输出结果
NOTICE: Inserting product: Apple Watch
NOTICE: Operation INSERT on fgedu_products affected 1 rows
INSERT 0 1
— 查看审计日志
SELECT table_name, operation, row_id, new_data->>’name’ AS product_name, operation_time, user_name
FROM fgedu_audit_detail
WHERE table_name = ‘fgedu_products’
ORDER BY operation_time DESC
LIMIT 5;
— 输出结果
table_name | operation | row_id | product_name | operation_time | user_name
————-+———–+——–+————–+————————+———-
fgedu_products | INSERT | 4 | Apple Watch | 2026-04-07 18:30:00 | fgedu
fgedu_products | UPDATE | 1 | iPhone 15 | 2026-04-07 18:25:00 | fgedu
(2 rows)
4.2 PostgreSQL数据库数据校验触发器函数实战
本案例演示数据校验触发器函数的实现。更多学习教程公众号风哥教程itpux_com。
— 创建用户表
CREATE TABLE fgedu_users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password VARCHAR(100) NOT NULL,
age INTEGER,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
— 输出结果
CREATE TABLE
— 创建数据校验触发器函数
CREATE OR REPLACE FUNCTION fgedu_user_validation()
RETURNS TRIGGER
AS $$
BEGIN
— 更新时间戳
NEW.updated_at := NOW();
— 验证用户名
IF length(NEW.username) < 3 THEN
RAISE EXCEPTION 'Username must be at least 3 characters';
END IF;
IF NEW.username !~ '^[a-zA-Z0-9_]+$' THEN
RAISE EXCEPTION 'Username can only contain letters, numbers and underscores';
END IF;
-- 验证邮箱
IF NEW.email !~ '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' THEN
RAISE EXCEPTION 'Invalid email format';
END IF;
-- 验证密码
IF length(NEW.password) < 6 THEN
RAISE EXCEPTION 'Password must be at least 6 characters';
END IF;
-- 验证年龄
IF NEW.age IS NOT NULL THEN
IF NEW.age < 18 OR NEW.age > 120 THEN
RAISE EXCEPTION ‘Age must be between 18 and 120’;
END IF;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
— 输出结果
CREATE FUNCTION
— 创建验证触发器
CREATE TRIGGER trg_user_validation
BEFORE INSERT OR UPDATE ON fgedu_users
FOR EACH ROW
EXECUTE FUNCTION fgedu_user_validation();
— 输出结果
CREATE TRIGGER
— 测试验证触发器函数(成功)
INSERT INTO fgedu_users(username, email, password, age)
VALUES(‘user1’, ‘user1@fgedu.net.cn’, ‘password123’, 25);
— 输出结果
INSERT 0 1
— 测试验证触发器函数(失败:用户名太短)
INSERT INTO fgedu_users(username, email, password, age)
VALUES(‘u’, ‘user2@fgedu.net.cn’, ‘password123’, 30);
— 输出结果
ERROR: Username must be at least 3 characters
— 测试验证触发器函数(失败:邮箱格式无效)
INSERT INTO fgedu_users(username, email, password, age)
VALUES(‘user2’, ‘invalid-email’, ‘password123’, 30);
— 输出结果
ERROR: Invalid email format
— 测试验证触发器函数(失败:密码太短)
INSERT INTO fgedu_users(username, email, password, age)
VALUES(‘user2’, ‘user2@fgedu.net.cn’, ‘pass’, 30);
— 输出结果
ERROR: Password must be at least 6 characters
— 测试验证触发器函数(失败:年龄无效)
INSERT INTO fgedu_users(username, email, password, age)
VALUES(‘user2’, ‘user2@fgedu.net.cn’, ‘password123’, 15);
— 输出结果
ERROR: Age must be between 18 and 120
— 查看用户数据
SELECT id, username, email, age, created_at
FROM fgedu_users;
— 输出结果
id | username | email | age | created_at
—-+———-+——————+—–+—————————-
1 | user1 | user1@fgedu.net.cn| 25 | 2026-04-07 18:35:00.123456
(1 row)
4.3 PostgreSQL数据库数据同步触发器函数实战
本案例演示数据同步触发器函数的实现。from PostgreSQL视频:www.itpux.com。
— 创建主表
CREATE TABLE fgedu_customers (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
phone VARCHAR(20),
address TEXT,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
— 输出结果
CREATE TABLE
— 创建同步表
CREATE TABLE fgedu_customers_sync (
id SERIAL PRIMARY KEY,
customer_id INTEGER NOT NULL,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL,
sync_status VARCHAR(20) DEFAULT ‘pending’,
sync_time TIMESTAMP DEFAULT NOW(),
last_sync TIMESTAMP
);
— 输出结果
CREATE TABLE
— 创建数据同步触发器函数
CREATE OR REPLACE FUNCTION fgedu_customer_sync()
RETURNS TRIGGER
AS $$
BEGIN
IF TG_OP = ‘INSERT’ THEN
INSERT INTO fgedu_customers_sync(
customer_id,
name,
email
)
VALUES(
NEW.id,
NEW.name,
NEW.email
);
RETURN NEW;
ELSIF TG_OP = ‘UPDATE’ THEN
UPDATE fgedu_customers_sync
SET
name = NEW.name,
email = NEW.email,
sync_status = ‘pending’,
sync_time = NOW()
WHERE customer_id = NEW.id;
— 如果没有找到记录,插入新记录
IF NOT FOUND THEN
INSERT INTO fgedu_customers_sync(
customer_id,
name,
email
)
VALUES(
NEW.id,
NEW.name,
NEW.email
);
END IF;
RETURN NEW;
ELSIF TG_OP = ‘DELETE’ THEN
UPDATE fgedu_customers_sync
SET
sync_status = ‘deleted’,
sync_time = NOW()
WHERE customer_id = OLD.id;
— 如果没有找到记录,插入删除标记
IF NOT FOUND THEN
INSERT INTO fgedu_customers_sync(
customer_id,
name,
email,
sync_status
)
VALUES(
OLD.id,
OLD.name,
OLD.email,
‘deleted’
);
END IF;
RETURN OLD;
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
— 输出结果
CREATE FUNCTION
— 创建同步触发器
CREATE TRIGGER trg_customer_sync
AFTER INSERT OR UPDATE OR DELETE ON fgedu_customers
FOR EACH ROW
EXECUTE FUNCTION fgedu_customer_sync();
— 输出结果
CREATE TRIGGER
— 测试数据同步触发器函数
INSERT INTO fgedu_customers(name, email, phone, address)
VALUES(‘张三’, ‘zhangsan@fgedu.net.cn’, ‘13800138000’, ‘北京市朝阳区’);
— 输出结果
INSERT 0 1
— 查看同步表
SELECT * FROM fgedu_customers_sync;
— 输出结果
id | customer_id | name | email | sync_status | sync_time | last_sync
—-+————-+——+———————+————-+———————+———–
1 | 1 | 张三 | zhangsan@fgedu.net.cn| pending | 2026-04-07 18:40:00 |
(1 row)
— 测试更新同步
UPDATE fgedu_customers SET email = ‘zhangsan_new@fgedu.net.cn’ WHERE id = 1;
— 输出结果
UPDATE 1
— 查看同步表
SELECT * FROM fgedu_customers_sync;
— 输出结果
id | customer_id | name | email | sync_status | sync_time | last_sync
—-+————-+——+————————–+————-+———————+———–
1 | 1 | 张三 | zhangsan_new@fgedu.net.cn | pending | 2026-04-07 18:45:00 |
(1 row)
— 测试删除同步
DELETE FROM fgedu_customers WHERE id = 1;
— 输出结果
DELETE 1
— 查看同步表
SELECT * FROM fgedu_customers_sync;
— 输出结果
id | customer_id | name | email | sync_status | sync_time | last_sync
—-+————-+——+————————–+————-+———————+———–
1 | 1 | 张三 | zhangsan_new@fgedu.net.cn | deleted | 2026-04-07 18:50:00 |
(1 row)
Part05-风哥经验总结与分享
5.1 PostgreSQL数据库触发器函数最佳实践
触发器函数最佳实践:保持函数逻辑简单;使用适当的错误处理;合理使用触发器上下文变量;避免在触发器函数中执行复杂操作;定期维护触发器相关表;监控触发器函数执行性能。
- 使用NEW和OLD变量访问记录数据
- 使用TG_*变量获取触发器上下文信息
- 使用RAISE语句输出调试信息
- 使用GET DIAGNOSTICS获取影响行数
- 使用事务保证数据一致性
5.2 PostgreSQL数据库触发器函数常见问题
常见问题:性能问题、递归触发、权限问题、错误处理不当、返回值错误。
5.3 PostgreSQL数据库触发器函数调试技巧
调试技巧:使用RAISE NOTICE输出调试信息;使用日志记录触发器函数执行情况;使用事务回滚测试;分步测试触发器函数逻辑;检查触发器执行顺序。
— 创建调试触发器函数
CREATE OR REPLACE FUNCTION fgedu_debug_trigger()
RETURNS TRIGGER
AS $$
BEGIN
RAISE NOTICE ‘Trigger fired: % on %’, TG_OP, TG_TABLE_NAME;
RAISE NOTICE ‘Trigger timing: %’, TG_WHEN;
RAISE NOTICE ‘Trigger level: %’, TG_LEVEL;
IF TG_LEVEL = ‘ROW’ THEN
IF TG_OP = ‘INSERT’ OR TG_OP = ‘UPDATE’ THEN
RAISE NOTICE ‘NEW record: %’, to_jsonb(NEW);
END IF;
IF TG_OP = ‘UPDATE’ OR TG_OP = ‘DELETE’ THEN
RAISE NOTICE ‘OLD record: %’, to_jsonb(OLD);
END IF;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
— 输出结果
CREATE FUNCTION
— 创建调试触发器
CREATE TRIGGER trg_debug_users
BEFORE INSERT OR UPDATE ON fgedu_users
FOR EACH ROW
EXECUTE FUNCTION fgedu_debug_trigger();
— 输出结果
CREATE TRIGGER
— 测试调试触发器函数
INSERT INTO fgedu_users(username, email, password, age)
VALUES(‘user3’, ‘user3@fgedu.net.cn’, ‘password123’, 30);
— 输出结果
NOTICE: Trigger fired: INSERT on fgedu_users
NOTICE: Trigger timing: BEFORE
NOTICE: Trigger level: ROW
NOTICE: NEW record: {“id”: 2, “username”: “user3”, “email”: “user3@fgedu.net.cn”, “password”: “password123”, “age”: 30, “created_at”: “2026-04-07T18:55:00.123456”, “updated_at”: “2026-04-07T18:55:00.123456”}
INSERT 0 1
— 测试更新操作
UPDATE fgedu_users SET age = 31 WHERE id = 2;
— 输出结果
NOTICE: Trigger fired: UPDATE on fgedu_users
NOTICE: Trigger timing: BEFORE
NOTICE: Trigger level: ROW
NOTICE: NEW record: {“id”: 2, “username”: “user3”, “email”: “user3@fgedu.net.cn”, “password”: “password123”, “age”: 31, “created_at”: “2026-04-07T18:55:00.123456”, “updated_at”: “2026-04-07T19:00:00.678901”}
NOTICE: OLD record: {“id”: 2, “username”: “user3”, “email”: “user3@fgedu.net.cn”, “password”: “password123”, “age”: 30, “created_at”: “2026-04-07T18:55:00.123456”, “updated_at”: “2026-04-07T18:55:00.123456”}
UPDATE 1
— 查看日志
SELECT * FROM pg_log WHERE message LIKE ‘%Trigger fired%’ ORDER BY log_time DESC LIMIT 5;
— 输出结果
-[ RECORD 1 ]————————————————————————————————-
log_time | 2026-04-07 19:00:00.678901
user_name | fgedu
database_name | fgedudb
process_id | 12345
session_id | 54321
message | NOTICE: Trigger fired: UPDATE on fgedu_users
-[ RECORD 2 ]————————————————————————————————-
log_time | 2026-04-07 19:00:00.678902
user_name | fgedu
database_name | fgedudb
process_id | 12345
session_id | 54321
message | NOTICE: Trigger timing: BEFORE
-[ RECORD 3 ]————————————————————————————————-
log_time | 2026-04-07 19:00:00.678903
user_name | fgedu
database_name | fgedudb
process_id | 12345
session_id | 54321
message | NOTICE: Trigger level: ROW
-[ RECORD 4 ]————————————————————————————————-
log_time | 2026-04-07 19:00:00.678904
user_name | fgedu
database_name | fgedudb
process_id | 12345
session_id | 54321
message | NOTICE: NEW record: {“id”: 2, “username”: “user3”, “email”: “user3@fgedu.net.cn”, “password”: “password123”, “age”: 31, “created_at”: “2026-04-07T18:55:00.123456”, “updated_at”: “2026-04-07T19:00:00.678901”}
-[ RECORD 5 ]————————————————————————————————-
log_time | 2026-04-07 19:00:00.678905
user_name | fgedu
database_name | fgedudb
process_id | 12345
session_id | 54321
message | NOTICE: OLD record: {“id”: 2, “username”: “user3”, “email”: “user3@fgedu.net.cn”, “password”: “password123”, “age”: 30, “created_at”: “2026-04-07T18:55:00.123456”, “updated_at”: “2026-04-07T18:55:00.123456”}
本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html
