必须由应用层或存储过程显式传入删除原因,触发器无法自动获取;MySQL用@session变量、PostgreSQL用SET LOCAL配合current_setting、SQL Server依赖CONTEXT_INFO();日志字段需设为TEXT/NVARCHAR(MAX)并允许NULL,触发器内操作须轻量同步且兜底处理空值。

触发器里拿不到 DELETE 原因,得靠外部传入
SQL 触发器本身无法知道“为什么删”,DELETE 语句不带原因字段。想记录删除原因,必须让应用层或存储过程把原因显式传进来——常见做法是加一个临时上下文变量或额外参数。MySQL 支持 @session.delete_reason 这类用户变量;PostgreSQL 可用 current_setting('app.delete_reason', true) 配合 SET LOCAL;SQL Server 则依赖 CONTEXT_INFO() 或临时表。
- 别指望在
BEFORE DELETE触发器里靠SELECT查日志或会话信息反推原因——多数数据库不保证会话上下文跨语句一致 - MySQL 示例:应用执行前先设变量
SET @delete_reason = '用户主动注销';,触发器里读@delete_reason写入日志表 - PostgreSQL 要提前
ALTER DATABASE ... SET app.delete_reason = '...';不够灵活,推荐在事务内用SET LOCAL app.delete_reason = '...';
日志表设计要兼容 NULL 和长文本
删除原因不是必填,且长度不可控,日志表的对应字段不能设 NOT NULL 或过短的 VARCHAR(50)。否则触发器一写就报错中断删除,反而破坏业务。
-
reason字段类型建议用TEXT(PostgreSQL/MySQL)或NVARCHAR(MAX)(SQL Server) - 加个
deleted_at时间戳和deleted_by(如用户ID),比单靠原因更有追溯价值 - 避免在日志表上建过多索引——高频删除+写日志容易拖慢主表操作
触发器里别做复杂逻辑或远程调用
触发器运行在事务上下文中,任何阻塞、异常或超时都会导致原 DELETE 失败。记录原因这事必须轻量、同步、无副作用。
- 禁止在触发器里调用 HTTP 接口、发消息、查其他库表(除非只读且极快)
- 别用
INSERT INTO ... SELECT ... FROM another_table拼原因——来源表可能被锁或不存在 - 最稳写法:直接
INSERT INTO audit_log (...) VALUES (OLD.id, COALESCE(@delete_reason, 'unknown'), NOW());
应用层不传原因时,触发器要兜底但不报错
生产环境总有漏传场景。触发器不能因为 @delete_reason 是 NULL 就拒绝删除——业务优先级高于日志完整性。
- MySQL 中用
COALESCE(@delete_reason, 'no_reason_provided') - PostgreSQL 用
NULLIF(current_setting('app.delete_reason', true), '')防空字符串污染 - SQL Server 记得先
CONVERT(VARBINARY(128), @reason)再存到CONTEXT_INFO,读取时再转回字符串,否则易乱码

















