用AFTER INSERT/DELETE触发器配合NEW/OLD动态取值可安全实现库存自动加减,须避免硬编码ID、确保大小写一致、利用事务原子性,并对软删除等场景做适配。

直接用 AFTER INSERT 和 AFTER DELETE 触发器就能实现库存自动加减,但必须搭配 NEW 或 OLD 引用行数据,且不能在触发器里写死商品 ID 或数量——否则所有订单都只扣同一款商品。
触发器必须用 NEW 和 OLD 动态取值
常见错误是把库存更新语句写成 UPDATE products SET stock = stock - 3 WHERE id = 1,这样无论插入哪条订单,都只扣商品 ID=1 的库存。正确做法是用 NEW.product_id 和 NEW.quantity 获取刚插入订单里的字段值。
-
INSERT场景只能用NEW(代表新插入的那行) -
DELETE场景只能用OLD(代表被删的那行) -
UPDATE场景两者都能用:OLD.quantity是旧数量,NEW.quantity是新数量,差值才是要调整的库存量 - 如果订单表字段名是
product_id和quantity,触发器里就得严格写成NEW.product_id和NEW.quantity,大小写和下划线必须完全一致
AFTER 比 BEFORE 更适合库存扣减
扣库存这类操作必须等主记录插入成功后再执行,否则可能因外键约束、唯一索引等问题导致主操作失败,而触发器已提前改了库存,造成数据不一致。所以一律用 AFTER INSERT,而不是 BEFORE INSERT。
-
BEFORE INSERT适合做数据校验或默认值填充(比如自动生成order_no),不适合修改其他表 -
AFTER INSERT确保订单已落库,再扣库存,失败时整个事务会回滚(前提是引擎支持事务,如 InnoDB) - 注意:MySQL 触发器本身不开启新事务,它和主 SQL 共享同一个事务上下文,这是保证原子性的关键
删除订单时记得用 AFTER DELETE 回补库存
用户取消订单或管理员删单,库存必须加回去。这时候触发器要读 OLD.product_id 和 OLD.quantity,然后执行 UPDATE products SET stock = stock + OLD.quantity WHERE id = OLD.product_id。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 别漏掉
DELETE场景——很多方案只做扣减,没做回补,导致“删单后库存变少”这种反直觉问题 - 如果订单表有软删除(比如加
is_deleted字段),触发器不会自动响应,得改用UPDATE触发器并判断字段变化 - 回补逻辑要和扣减逻辑对称:扣是
- NEW.quantity,补就是+ OLD.quantity,符号别写反
触发器里不能调用存储函数或外部服务
MySQL 触发器内禁止执行 CALL、SELECT ... INTO(除非赋给变量)、GET_LOCK,也不支持 HTTP 请求或日志打印。它只允许 DML(INSERT/UPDATE/DELETE)和简单赋值。
- 想记录操作日志?只能往另一张日志表
INSERT,不能写文件或发消息 - 想校验库存是否充足?可以在
BEFORE INSERT里用IF (SELECT stock FROM products WHERE id = NEW.product_id) - 跨库操作受限:触发器默认只能操作同库表,跨库需显式写成
db_name.table_name,且用户权限必须覆盖目标库
最易被忽略的是事务边界——触发器不是独立事务,它依附于原始 SQL 所在事务。一旦触发器里的 UPDATE 报错(比如违反外键或唯一约束),整个插入/删除操作都会失败,这点和应用层手动执行完全不同。所以写之前务必确认库存表结构、索引和约束是否匹配业务语义。










