触发器必须建在订单明细表(order_items)上,因其含product_id、quantity、unit_price等关键字段,可实时轻量更新销售统计;建在主表需join且违背设计初衷。

触发器该建在订单表还是订单明细表
必须建在订单明细表(order_items)上。订单主表(orders)只存汇总信息,而销售统计需要按商品维度累加数量和金额,只有 order_items 包含 product_id、quantity、unit_price 这些关键字段。建在主表会导致无法定位具体商品,或被迫 JOIN 查询,违背触发器“实时+轻量”的设计初衷。
常见错误是监听 orders 的 INSERT,然后在触发器里查 order_items——这不仅引入额外 I/O,还可能因事务隔离级别导致查不到刚插入的明细行(尤其在 READ COMMITTED 下)。
INSERT/UPDATE/DELETE 三类操作怎么分别处理
销售统计表(如 product_sales_summary)需支持增删改同步,不能只写 INSERT 触发器。重点在于:每种 DML 操作对应统计值的不同修正逻辑,且必须用 IF EXISTS 或 MERGE 类语句避免主键冲突或空值计算错误。
-
INSERT:检查product_id是否已存在,存在则UPDATE quantity_total += NEW.quantity,不存在则INSERT新行 -
UPDATE:仅当NEW.quantity != OLD.quantity或NEW.unit_price != OLD.unit_price时才触发更新,否则跳过;修正公式为quantity_total = quantity_total - OLD.quantity + NEW.quantity,金额同理 -
DELETE:直接quantity_total -= OLD.quantity,并判断是否归零后删除该商品记录(可选)
触发器里调用 SELECT 会不会锁表或拖慢性能
会,而且很危险。MySQL 的 BEFORE INSERT 触发器中执行 SELECT ... FOR UPDATE 可能引发死锁;PostgreSQL 中在触发器里查本表(如先查当前统计再更新)会触发“tuple concurrently updated”错误。根本解法是:绕开 SELECT,全部用 INSERT ... ON CONFLICT DO UPDATE(PG)或 INSERT ... ON DUPLICATE KEY UPDATE(MySQL)。
示例(PostgreSQL):
INSERT INTO product_sales_summary (product_id, quantity_total, amount_total)
VALUES (NEW.product_id, NEW.quantity, NEW.quantity * NEW.unit_price)
ON CONFLICT (product_id) DO UPDATE
SET quantity_total = product_sales_summary.quantity_total + EXCLUDED.quantity_total,
amount_total = product_sales_summary.amount_total + EXCLUDED.amount_total;
这样一条语句完成“存在即更新、不存在即插入”,不查表、不锁全表、原子性强。
为什么不能用触发器维护库存,却能用于销售统计
库存更新要求强一致性(下单即扣减,不容许超卖),而销售统计本质是只读聚合,允许短暂延迟或少量误差(比如统计表滞后几毫秒不影响业务)。触发器在高并发下可能因执行顺序导致统计值暂时不一致,但只要最终一致即可接受;而库存一旦出错,直接导致超卖或负库存,必须靠应用层加锁或分布式事务兜底。
另一个关键差异:销售统计字段(quantity_total、amount_total)是纯数值累加,无业务约束;库存字段(stock_qty)常带 CHECK(stock_qty >= 0),触发器里更新它可能被 CHECK 约束打断,反向引发事务回滚连锁反应。
真正容易被忽略的是触发器对批量操作的兼容性——比如一次插入 1000 条订单明细,触发器会逐行执行 1000 次,而不是合并成一次聚合更新。如果统计表没有合适索引(product_id 必须有唯一索引或主键),性能会断崖式下跌。










