必须为insert/update/delete分别创建独立触发器,因mysql和postgresql不支持单触发器内用if判断操作类型,且三类操作对汇总值影响方向不同:insert只加、update需先减后加并处理客户变更、delete只减,均须防null并确保主键与索引完备。

不能靠触发器“自动维护”汇总表——它只响应后续 DML,不补历史、不拦 TRUNCATE、不扛批量、并发下极易错漏。真要上,必须拆 INSERT/UPDATE/DELETE 三类触发器,主键对齐维度,NULL 防护到位,且初始化必须手动跑一次。
为什么 INSERT/UPDATE/DELETE 必须写三个独立触发器
混在一个触发器里无法区分操作语义,MySQL 和 PostgreSQL 都不支持触发器内写 IF 分支判断操作类型;SQLite 根本没 IF;SQL Server 虽支持但嵌套深度限制严。每类操作对汇总值的影响方向不同:
-
AFTER INSERT:只加新值,但必须检查NEW.amount IS NOT NULL,否则total = total + NULL整行变 NULL -
AFTER UPDATE:先减OLD.amount,再加NEW.amount,两个都要防 NULL;若OLD.customer_id != NEW.customer_id,还得双路更新 -
AFTER DELETE:只减OLD.amount,同样要OLD.amount IS NOT NULL判断
主键和索引怎么设才不丢数据
多维聚合(如按 date、category、region 分组)的汇总表,主键必须是这三列组合。否则:
- 主键缺失维度 → 多条逻辑不同的记录挤进同一行,互相覆盖
- 主键不唯一 →
INSERT OR REPLACE INTO或ON CONFLICT找不到冲突目标,静默插入重复行 - 没建联合索引 →
WHERE date = ? AND category = ?更新时全表扫描,高并发下锁等待甚至死锁
TRUNCATE 和历史数据必须人工兜底
TRUNCATE TABLE 在所有主流数据库中都不触发任何 DML 触发器——它直接清空页,连 AFTER DELETE 都收不到。后果是汇总表残留旧数据,彻底失准。
- 要么禁用
TRUNCATE权限,文档明确要求“清空请用DELETE FROM xxx” - 要么新建汇总表后立刻手动初始化:
INSERT INTO summary SELECT ... GROUP BY ... ON CONFLICT DO UPDATE - 触发器从不回溯历史数据——哪怕源表已有三年记录,刚建好的汇总表就是空的,必须手动补
哪些写法会直接让触发器失效或拖垮性能
以下操作在任意数据库触发器中都应禁止:
- 在触发器里写
SELECT SUM(amount) FROM orders WHERE customer_id = NEW.customer_id—— MySQL 报 ERROR 1442,PostgreSQL 全表扫描,SQL Server 锁升级 - 用
INSERT INTO ... SELECT ...或REPLACE INTO做 upsert —— 高并发下必现重复键或死锁,必须改用INSERT ... ON DUPLICATE KEY UPDATE(MySQL)或MERGE(SQL Server) - 在触发器里调用存储过程更新其他表 —— MySQL 禁止事务控制,SQL Server 可能触发嵌套超限(Msg 217)
- 假设
UPDATE时字段一定变化,不加IF OLD.amount != NEW.amount判断 —— 无意义更新导致 binlog 膨胀、锁竞争加剧
最常被忽略的一点:触发器函数一旦上线,就很难热更新。字段名变更、条件调整、新增关联维度,都得停写、改触发器、重测。真正可控的路径是把修正逻辑抽到存储过程里,触发器只做简单调用——否则维护成本远高于收益。











