MySQL 僅支援行級觸發器,強制要求 FOR EACH ROW 子句。每影響一行資料,觸發器體就執行一次。這與 Oracle 支援語句級觸發器(可省略 FOR EACH ROW)形成鮮明對比。
CREATE TRIGGER trigger_name
{BEFORE | AFTER} {INSERT | UPDATE | DELETE}
ON table_name
FOR EACH ROW
BEGIN
-- 觸發器邏輯
SQL 语句;
END;
UPDATE t SET x=1 WHERE id IN (1,2,3) 影響 3 行 → 觸發器執行 3 次BEFORE(寫入前,可修改 NEW 值)vs AFTER(寫入後,不可修改)NEW:INSERT/UPDATE 的新行資料(NEW.col)OLD:UPDATE/DELETE 的舊行資料(OLD.col)CALL、START TRANSACTION、COMMIT、ROLLBACK(除非自主事務)CREATE TABLE goods (
goods_id INT PRIMARY KEY,
name VARCHAR(20),
num SMALLINT
) ENGINE=InnoDB;
CREATE TABLE `ord` (
oid INT PRIMARY KEY,
gid INT,
much INT
) ENGINE=InnoDB;
INSERT INTO goods VALUES (1,'cat',26),(2,'dog',26),(3,'pig',26);
DELIMITER $$
CREATE TRIGGER trg_ord_after_insert
AFTER INSERT ON `ord`
FOR EACH ROW
BEGIN
UPDATE goods SET num = num - NEW.much WHERE goods_id = NEW.gid;
END$$
DELIMITER ;
INSERT INTO `ord` VALUES (1, 2, 2);
SELECT * FROM goods;
-- 結果:dog (goods_id=2) num 從 26 變 24
CREATE TRIGGER trg_ord_after_delete
AFTER DELETE ON `ord`
FOR EACH ROW
BEGIN
UPDATE goods SET num = num + OLD.much WHERE goods_id = OLD.gid;
END$$
CREATE TRIGGER trg_ord_after_update
AFTER UPDATE ON `ord`
FOR EACH ROW
BEGIN
UPDATE goods SET num = num + OLD.much - NEW.much WHERE goods_id = NEW.gid;
END$$
-- 查看所有觸發器
SHOW TRIGGERS;
-- 刪除觸發器
DROP TRIGGER trg_ord_after_insert;
| 風險 | 影響 | 對策 |
|---|---|---|
| 大批量 DML 觸發器逐行執行 | 效能指數級下降,鎖爭用劇增 | 避免在高頻大表加觸發器;改用應用層批次處理或異步佇列 |
| 觸發器內再觸發其他表觸發器 | 級聯觸發、死鎖、難以除錯 | 嚴禁觸發器巢狀超過 1 層;業務邏輯下沉應用層 |
| 觸發器修改 NEW 值(BEFORE) | 可實現資料清洗/預設值填充 | 僅在 BEFORE 觸發器中使用 SET NEW.col = value; |
| 備份/遷移遺漏觸發器 | 業務邏輯缺失 | mysqldump --triggers 預設包含;遷移腳本驗證 SHOW TRIGGERS |
MySQL 觸發器是「資料庫層面的 AOP」,威力大但副作用強。現代架構傾向「業務邏輯上移應用層,資料庫專注儲存與索引」,觸發器使用率逐年下降。若必須用,請嚴守「單表、單語句、無巢狀、有文檔」四原則。