CREATE TABLE goods(id INT PRIMARY KEY, name VARCHAR(20), num INT);
CREATE TABLE `order`(id INT AUTO_INCREMENT PRIMARY KEY, gid INT, much INT);
INSERT INTO goods VALUES (1, '電視機', 35), (2, '冰箱', 23);
CREATE TRIGGER trigger1
AFTER INSERT ON `order`
FOR EACH ROW
UPDATE goods SET num = num - NEW.much WHERE id = NEW.gid;
-- 測試
INSERT INTO `order`(gid, much) VALUES (1, 5); -- 庫存 35→30 ✓
INSERT INTO `order`(gid, much) VALUES (2, 100); -- 庫存 23→-77 ✗ 超賣!
原因:AFTER 觸發器在數據已寫入後執行,無法攔截、修改 NEW.much。
DROP TRIGGER trigger1;
CREATE TRIGGER trigger1
BEFORE INSERT ON `order`
FOR EACH ROW
BEGIN
DECLARE stock INT;
SELECT num INTO stock FROM goods WHERE id = NEW.gid;
IF NEW.much > stock THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '庫存不足';
END IF;
-- 或自動修正為最大可買
-- IF NEW.much > stock THEN SET NEW.much = stock; END IF;
UPDATE goods SET num = num - NEW.much WHERE id = NEW.gid;
END;
| 需求 | 觸發時機 | 理由 |
|---|---|---|
| 校驗/修正 NEW 值、阻止操作 | BEFORE | 可訪問並修改 NEW,拋異常回滾 |
| 需獲取自增 ID、外鍵級聯後續操作 | AFTER | INSERT 完成後才有生成值 |
| 異步通知、日志、統計聚合 | AFTER | 主業務已提交,減少鎖持有時間 |
| 同表再操作(避免遞歸) | AFTER | 配合 @@TRIGGER_NEST_LEVEL 判斷 |
CREATE TRIGGER trg_order_after
AFTER INSERT ON `order`
FOR EACH ROW
BEGIN
-- 庫存扣減
UPDATE goods SET num = num - NEW.much WHERE id = NEW.gid;
-- 記錄出庫流水
INSERT INTO stock_log(goods_id, qty, type) VALUES (NEW.gid, NEW.much, 'OUT');
-- 更新銷售統計
UPDATE daily_sales SET total = total + NEW.much WHERE goods_id = NEW.gid AND sale_date = CURDATE();
END;
注意:觸發器與觸發語句在同一事務,任一失敗全回滾,保證一致性。
UPDATE `order`...)→ 遞歸報錯COMMIT/ROLLBACK、START TRANSACTIONBEFORE 中修改 NEW 只對當前行生效,不影響其他行SIGNAL 拋出業務異常(MySQL 5.5+),應用層捕獲 SQLState 45000