mysql触发器中不能用insert into ... select old.*,必须显式列出字段;update和delete需分别建触发器;备份表结构须随原表变更同步,且insert ... on duplicate key update不触发before update。

触发器里用 INSERT INTO ... SELECT OLD.* 会报错
MySQL 触发器中不能直接写 INSERT INTO backup_table SELECT OLD.*,因为 OLD.* 在 BEFORE UPDATE 或 BEFORE DELETE 中是只读的行引用,不支持在子查询中直接展开为字段列表。常见错误是:ERROR 1351 (HY000): View's SELECT contains a 'SELECT *' 或触发器编译失败。
正确做法是显式列出所有需要备份的字段,确保字段顺序、类型、NULL 性与原表一致:
CREATE TRIGGER user_update_backup BEFORE UPDATE ON users FOR EACH ROW INSERT INTO users_backup (id, name, email, updated_at, backup_time) VALUES (OLD.id, OLD.name, OLD.email, OLD.updated_at, NOW());
- 字段名必须和原表字段一一对应,不能漏掉主键或非空字段(否则插入失败)
-
backup_time这类审计字段建议用NOW()或SYSDATE(),避免用CURRENT_TIMESTAMP(在某些版本中可能被优化掉) - 如果原表有 BLOB/TEXT 字段,
OLD.xxx可正常引用,但注意备份表对应字段类型也要匹配
DELETE 和 UPDATE 都要建独立触发器
一个触发器只能响应一种事件类型,BEFORE UPDATE 不捕获 DELETE,反之亦然。想同时记录两种操作,必须分别创建:
CREATE TRIGGER user_delete_backup BEFORE DELETE ON users FOR EACH ROW INSERT INTO users_backup (id, name, email, updated_at, backup_time, op_type) VALUES (OLD.id, OLD.name, OLD.email, OLD.updated_at, NOW(), 'DELETE');
-
op_type字段强烈建议加上,否则无法区分某条备份记录来自更新前快照还是删除前快照 - 不要试图用一个触发器覆盖多个事件(MySQL 不支持
BEFORE UPDATE OR DELETE这种语法) - 若业务中存在批量
DELETE FROM ... LIMIT,触发器仍会逐行触发,不影响日志完整性,但要注意性能压力
备份表结构要兼容原表变更
当原表加字段、改类型、调默认值时,备份表不会自动同步——这是最常被忽略的维护点。一旦原表新增 phone VARCHAR(20),而触发器没更新、备份表也没加字段,后续 UPDATE 就会因字段数不匹配直接报错中断。
- 每次 ALTER TABLE 原表前,先检查所有相关触发器 SQL,并同步
ALTER TABLE users_backup - 备份表建议去掉外键、索引精简(只需给
id和backup_time加索引),避免拖慢主表 DML - 可考虑用
JSON字段存整行旧值(MySQL 5.7+):定义old_data JSON,再用JSON_OBJECT('id', OLD.id, 'name', OLD.name, ...)插入,降低结构耦合,但牺牲了字段级查询能力
触发器无法捕获 REPLACE、INSERT ... ON DUPLICATE KEY UPDATE 的 OLD 行
REPLACE INTO 实质是 DELETE + INSERT,会触发 BEFORE DELETE;但 INSERT ... ON DUPLICATE KEY UPDATE 属于 INSERT 语句,即使发生更新,也**不会**触发 BEFORE UPDATE,而是走 BEFORE INSERT —— 此时 OLD 不可用,NEW 是新值,你拿不到“更新前”的状态。
- 这类语句的日志需在应用层补全,或改用显式
SELECT ... FOR UPDATE+UPDATE组合 - 监控慢日志或 general_log 可发现这类绕过触发器的写法,建议 DBA 定期扫描
- MySQL 8.0 的
SET PERSIST log_bin_trust_function_creators = 1等配置不影响触发器行为,别误以为开了 binlog 就能替代行级日志
实际线上跑起来后,最容易出问题的不是语法,而是备份表字段漏同步、或者 INSERT ... ON DUPLICATE KEY UPDATE 悄悄绕过了你的日志逻辑。留个心眼,比写十行触发器都管用。











