MySQL的BEFORE UPDATE触发器中,OLD.字段名获取修改前值,NEW.字段名获取修改后值,两者均可用于精准比对;PostgreSQL用OLD/NEW记录变量,SQL Server依赖deleted/inserted临时表;需显式比较并处理NULL安全问题。

触发器里怎么捕获 UPDATE 时的旧值和新值
MySQL 的 BEFORE UPDATE 触发器能读取 OLD.字段名 和 NEW.字段名,这是做历史备份的前提。PostgreSQL 则用 OLD 和 NEW 记录变量,语法稍不同;SQL Server 依赖 deleted 和 inserted 临时表。关键不是“能不能”,而是“要不要在触发器里做判断”——直接无条件插入所有字段变更会浪费空间,建议只对敏感字段(如 id_card、phone、email)做差异比对。
常见错误是写成:IF OLD.phone != NEW.phone THEN INSERT ...,但没处理 NULL 比较问题。MySQL 中 NULL != NULL 返回 NULL(即 false),必须改用 NOT (OLD.phone NEW.phone);PostgreSQL 用 IS DISTINCT FROM 更安全。
备份表结构怎么设计才不拖慢主表更新
历史备份表不能简单复制主表结构。字段要精简:只存 record_id(关联原记录)、field_name(字符串标识字段名)、old_value 和 new_value(统一用 TEXT 或 JSON 类型)、updated_at、updated_by(如果业务能传用户 ID)。避免冗余索引,但必须在 (record_id, field_name, updated_at) 上建联合索引——否则查某条记录的手机号修改轨迹会变全表扫描。
性能隐患常出在大字段上:比如主表有个 TEXT 类型的 address 字段,每次改它都往备份表插几 KB 数据,很快撑爆 I/O。对策有两个:只备份前 200 字符(用 SUBSTR(NEW.address, 1, 200)),或 对值做哈希后存(如 MD5(NEW.address)),查变更时靠哈希比对,不还原原文。
如何让触发器知道是谁改的(绕过应用层缺失)
多数业务系统没把操作人信息透传到 SQL 层,触发器拿不到 updated_by。硬编码 'system' 不解决问题。可行方案有三个:
- MySQL 用
USER()或CURRENT_USER()获取数据库连接账号(适合按角色区分场景) - PostgreSQL 用
current_setting('app.user_id', true),前提是应用在事务开始前执行SET app.user_id = '123' - SQL Server 可读
CONTEXT_INFO(),但需要应用层调用SET CONTEXT_INFO预置字节数组
注意:USER() 在连接池场景下可能返回中间件账号,不是真实操作人;current_setting 若未设默认值会报错,得加 DEFAULT 参数兜底。
跨库或分表时触发器还管用吗
触发器只作用于单表、单库。一旦主表做了分库分表(比如用 ShardingSphere 或 MyCat),触发器就失效了——因为路由后的 SQL 可能根本没走到原库的触发器上。此时必须把备份逻辑提到应用层:在 ORM 更新敏感字段前,手动写一条历史记录。或者改用数据库日志解析(如 MySQL binlog + Canal),但成本高、延迟不可控。
另一个容易被忽略的点:某些云数据库(如阿里云 RDS MySQL 5.7)默认禁用触发器,需在控制台开启 log_bin_trust_function_creators 参数并授权 TRIGGER 权限,否则建触发器会报错 ERROR 1419。

















