触发器不适合做聚合统计,因其非原子性、易重复执行、禁止查改源表、异常静默等问题;应改用幂等SQL、物化视图、增量标记+定时对账等方案。

触发器里没做幂等处理,重复更新导致统计值漂移
触发器执行不是原子性的“一次只跑一遍”,尤其在批量操作(如 INSERT INTO t SELECT ...)或主从复制重放时,可能被多次调用。比如一个 AFTER INSERT 触发器里写了 UPDATE stats SET count = count + 1 WHERE user_id = NEW.user_id,但如果同一条记录因 binlog 重放、应用重试或触发链被调用两次,count 就会多加一次。
实操建议:
- 优先用
MERGE(SQL Server)或INSERT ... ON DUPLICATE KEY UPDATE(MySQL)替代单纯UPDATE,确保写入幂等 - 统计字段设为
GENERATED ALWAYS AS (subquery) STORED(MySQL)或用物化视图(PostgreSQL),绕过触发器手动维护 - 在触发器开头加去重判断:例如先查目标行是否存在,不存在才
INSERT,存在再UPDATE,避免无条件+或-
跨行聚合逻辑写在触发器里,却只处理单行 NEW/OLD
你写了个“用户订单总金额”统计,但在触发器里只用 NEW.amount 做加减,没考虑同一用户可能有多个并发插入——这会导致最终值只反映最后一条的变更,而非真实累计值。更糟的是,如果触发器基于 AFTER INSERT,但业务实际走的是 REPLACE INTO 或 INSERT ... ON DUPLICATE KEY UPDATE,它甚至可能根本没触发。
实操建议:
- 聚合类统计绝不能依赖单行
NEW推算全局值;正确做法是:触发器只记“变更量”,再由外部定期任务或物化视图全量校准 - 确认你的 DML 操作真能触发对应事件:运行
SHOW TRIGGERS LIKE 'orders';,检查Event列是否匹配(如INSERT不会响应REPLACE) - 测试必须覆盖批量场景:用
INSERT INTO orders (user_id, amount) VALUES (123, 100), (123, 200);看统计表是否变成 300,而不是 200
触发器内查源表再更新统计表,触发 MySQL ERROR 1442
常见写法是在 AFTER UPDATE 里补查一遍订单表算最新总额:SELECT SUM(amount) FROM orders WHERE user_id = NEW.user_id,然后写回用户表。这在 MySQL 中直接报错 ERROR 1442 (HY000),且整个事务回滚——但很多客户端不显示该错误,只返回 “Query OK”,让你误以为成功了。
实操建议:
- 绝对禁止在触发器中
SELECT或UPDATE触发它的同一张表;要用NEW/OLD字段推导,或把聚合逻辑移到应用层 - 查错误日志:搜
ERROR 1442,别只看SHOW WARNINGS—— 它通常为空 - 改用事件驱动:触发器只往通知表插一条记录,由后台作业异步刷新统计,避开引擎限制
没捕获异常,统计失败静默吞掉
触发器里 UPDATE stats SET total = ... WHERE id = NEW.user_id 如果目标行不存在,MySQL 默认不报错,ROW_COUNT() 返回 0,但后续语句照常执行。你看到统计表没变,却不知道是 WHERE 没匹配上,还是约束冲突被吞了。
实操建议:
- 在关键语句后加校验:例如
IF ROW_COUNT() = 0 THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'stats row missing for user'; END IF; - 所有多语句触发器必须包在
BEGIN ... END内,并用DECLARE EXIT HANDLER FOR SQLEXCEPTION记日志 - 建专用调试表,每条触发器入口都写一行日志:
INSERT INTO trigger_debug (trigger_name, op, user_id, ts) VALUES ('update_stats', 'AFTER INSERT', NEW.user_id, NOW());

















