触发器必须写在被修改的表上,如orders表,类型为AFTER INSERT、UPDATE、DELETE;不能写在account_summary表上,否则破坏一致性;需覆盖三类操作并用COALESCE处理NULL,禁止在触发器中执行SELECT SUM()全量计算。
orders 表的 amount 字段时,要让 account_summary 表的 total_amount 保持一致」,那触发器就得建在 orders 上,且类型为 AFTER INSERT, UPDATE, DELETE。写在 account_summary 上毫无意义——它不会被业务代码直接改,改了反而破坏一致性。常见错误是只覆盖 UPDATE,漏掉 INSERT 和 DELETE。新订单插入、订单删除都会影响总和,缺一不可。
INSERT:累加新记录的 amount
UPDATE:先减去旧值,再加新值(不能只加差值,因为可能触发器里读不到 OLD.amount)DELETE:减去被删记录的 amount
OLD.amount 在 INSERT 里不存在,NEW.amount 在 DELETE 里不存在,直接引用会报错。必须用 IF 或 CASE 分支判断事件类型,且对可能为 NULL 的字段做显式处理。例如,不能写 SET @delta = NEW.amount - OLD.amount; —— 这在 INSERT 或 DELETE 里直接失败。正确做法是:
IF TG_OP = 'INSERT' THEN SET @delta = COALESCE(NEW.amount, 0);ELSEIF TG_OP = 'UPDATE' THEN SET @delta = COALESCE(NEW.amount, 0) - COALESCE(OLD.amount, 0);ELSE -- DELETE SET @delta = -COALESCE(OLD.amount, 0);END IF;
注意:COALESCE 是必须的,否则任意一个 amount 为 NULL,整个算术结果就是 NULL,后续更新会把 total_amount 设成 NULL。
UPDATE account_summary SET total_amount = (SELECT SUM(amount) FROM orders);,这在高并发下会引发严重问题:orders,数据量大时延迟明显orders 表有百万行,这个 SUM() 可能锁表数秒,阻塞其他操作正确的做法是只做增量更新:UPDATE account_summary SET total_amount = total_amount + @delta;。前提是 account_summary 表只有一行(或用 WHERE id = 1 精确限定),且初始值已由全量计算校准过。
最常踩的坑是:在 orders 的触发器里,又去 UPDATE orders —— MySQL 会直接报错 Can't update table 'orders' in stored function/trigger。同理,如果校验失败想抛异常,MySQL 5.7+ 要用 SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Amount mismatch';,而不是 RAISE ERROR(不支持)或 SELECT 模拟错误(无效)。
真正难的是边界情况:金额字段允许 NULL 吗?是否要检查负数?是否要兼容部分字段更新(比如只改订单状态,不改金额)?这些逻辑一旦漏判,触发器就会静默失效——表面没报错,数据却慢慢歪掉。