delete触发器必须用before而非after,因after中old不可访问会导致归档静默失败;mysql需显式列出old字段插入归档表,postgresql则须在before delete触发器函数中return old且函数返回类型为trigger。

DELETE 触发器必须用 BEFORE 而不是 AFTER
触发器要在记录真正被删掉之前捕获它,否则 OLD 行数据在 AFTER DELETE 里已不可访问——MySQL 和 PostgreSQL 都如此。用 AFTER 会导致归档失败,且不报错,只静默丢数据。
实操建议:
- MySQL 中必须声明为
BEFORE DELETE ON 表名 - PostgreSQL 同样要求
BEFORE DELETE,且函数需返回OLD(否则触发器会阻止删除) - 触发器里不能对原表做 INSERT/UPDATE/DELETE(会引发“表正在被修改”的递归错误)
归档表结构必须严格兼容源表字段
如果源表有自增主键、默认值、生成列或 JSON 字段,归档表字段类型、是否允许 NULL、时区设置稍有偏差,INSERT INTO archive_table SELECT ... FROM OLD 就会失败。
实操建议:
- 用
SHOW CREATE TABLE 源表查结构,复制建表语句后删掉AUTO_INCREMENT、PRIMARY KEY、DEFAULT等约束,保留字段名和类型 - 特别注意:
TIMESTAMP字段在 MySQL 5.6+ 默认带ON UPDATE CURRENT_TIMESTAMP,归档表应去掉该属性 - PostgreSQL 中若源表有
GENERATED ALWAYS AS (...) STORED列,归档表需改为普通列并手动赋值
MySQL 触发器中不能直接 INSERT SELECT OLD.*
MySQL 不支持 INSERT INTO archive_table SELECT * FROM OLD 这种写法,会报错 ERROR 1351 (HY000): VIEW 'OLD' has no creation context。
实操建议:
- 必须显式列出所有字段:
INSERT INTO archive_table (id, name, created_at) VALUES (OLD.id, OLD.name, OLD.created_at) - 字段顺序、数量、类型必须与
INSERT目标列完全一致,否则报错或截断 - 遇到
TEXT或JSON字段,确保归档表对应字段类型匹配(例如 MySQL 8.0 的JSON类型不能插入到TEXT字段)
PostgreSQL 需显式 RETURN OLD 且函数必须是 ROW 类型
PostgreSQL 的 BEFORE DELETE 触发器函数如果不返回 OLD,删除操作会被取消——这常被忽略,导致“删不掉记录”却查不到错误。
实操示例(函数定义):
CREATE OR REPLACE FUNCTION move_to_archive() RETURNS TRIGGER AS $$ BEGIN INSERT INTO orders_archive SELECT OLD.*; RETURN OLD; -- 关键!缺这行就删不掉 END; $$ LANGUAGE plpgsql;
注意事项:
- 函数返回类型必须是
TRIGGER,且RETURN OLD是硬性要求 -
SELECT OLD.*在 PostgreSQL 中合法,但前提是归档表字段顺序、类型、数量与源表完全一致 - 如果归档表多了一个
archived_at时间戳字段,就得写成INSERT INTO orders_archive (...) VALUES (OLD.*, NOW()),不能用*
触发器逻辑看着简单,但字段隐式转换、时区、权限、事务隔离级别都会让归档行为变得不可靠;最稳妥的做法是先在测试库用单条 DELETE 手动验证触发器日志和归档结果,再上线。











