mysql触发器中if必须用begin...end包裹,因默认仅支持单语句;update()仅检测字段是否出现在sql中而非值变化;old/new在insert/delete中分别不可用;避免查大表或自身表以防error 1442。

触发器里写IF,必须用BEGIN...END包住
MySQL触发器体默认只允许单条语句;想用IF做条件分支,必须显式声明复合语句块。漏掉BEGIN和END会直接报语法错误,比如ERROR 1064。
常见错误现象:CREATE TRIGGER ... AFTER UPDATE ON t FOR EACH ROW IF NEW.status != OLD.status THEN INSERT ...——这条语句在MySQL里非法,因为IF不能裸写在FOR EACH ROW后面。
- 正确写法:以
BEGIN开头、END结尾,中间放IF/ELSEIF/ELSE - 别忘了改
DELIMITER,否则分号会被MySQL提前解析为语句结束 -
IF支持嵌套,但超过3层易读性骤降,建议拆逻辑或用CASE
判断字段是否“真变化”,不能只靠UPDATE()
UPDATE('col')只检查SQL语句里有没有出现该字段,不关心值变没变。比如UPDATE t SET price = OLD.price,UPDATE('price')返回TRUE,但实际什么都没改。
真正检测值变更,得组合用:IF UPDATE('col') AND NEW.col OLD.col THEN
-
OLD.col和NEW.col比较时,必须用OLD.col NEW.col而非!=,否则NULL参与比较结果为NULL(即条件不成立) - 字符串字段要注意尾部空格和排序规则,必要时加
TRIM(NEW.name) TRIM(OLD.name)或BINARY NEW.name = BINARY OLD.name - 多个字段同时变化的条件,要分开调用
UPDATE():UPDATE('a') AND UPDATE('b'),不能写成UPDATE('a','b')
INSERT/UPDATE/DELETE触发器中OLD和NEW的可用性不同
OLD和NEW不是随时都能用:它们代表行变更前后的快照,所以语义上受限严格。
-
INSERT触发器:只有NEW可用(还没旧值),OLD访问会报错 -
DELETE触发器:只有OLD可用(数据已删,无新值),NEW访问报错 -
UPDATE触发器:OLD和NEW都可用,但注意OLD是变更前、NEW是变更后 - 误在
INSERT触发器里写IF OLD.id IS NOT NULL THEN,执行时直接失败
避免在触发器里查大表或写自身表
触发器本质是DML语句的附着逻辑,性能敏感且限制多。常见硬伤是忽略并发与锁行为。
- 在
AFTER UPDATE里再SELECT当前表(哪怕加LIMIT 1),可能触发ERROR 1442(表正在被修改) - 查配置表可以,但必须确保该表极小、有主键、不常更新;避免
SELECT * FROM huge_log_table WHERE ... - 不要试图用触发器实现“开关”逻辑,比如
SELECT @enable INTO @flag FROM config——用户变量在触发器中不可靠,MySQL 8.0+明确禁止赋值 - 真正需要动态控制,应把开关逻辑下沉到应用层,或用轻量配置表+主键查询+缓存
最常被忽略的一点:触发器里的IF不是“要不要触发”,而是“触发后要不要执行某段逻辑”。它的执行时机、事件类型、作用表,全由CREATE TRIGGER语句静态定义,运行时无法绕过或切换。想靠条件跳过整个触发动作?做不到。











