UPDATE()函数仅在UPDATE触发器中有效,用于检测SQL语句是否包含某字段,而非值是否变化;需配合NEW.col OLD.col判断实际变更,并注意NULL安全与字符串比较细节。

UPDATE() 函数只在 UPDATE 语句中有效,INSERT/DELETE 里用会报错
MySQL 触发器里没有 OLD.col IS DISTINCT FROM NEW.col 这种标准写法,得靠 UPDATE() 函数配合字段名判断。但它不是万能的——它只在 UPDATE 触发器里返回布尔值,在 INSERT 或 DELETE 触发器中调用会直接报错:FUNCTION xxx.UPDATE does not exist。
实操建议:
- 只在
BEFORE UPDATE或AFTER UPDATE触发器里用UPDATE('col_name') - 别在
INSERT触发器里写IF UPDATE('id')—— 即使语法没标红,执行时必挂 - 如果想统一处理“某字段变了就触发逻辑”,得把 INSERT 和 UPDATE 的逻辑拆开:INSERT 默认视为“变化”,UPDATE 再用
UPDATE()判定
UPDATE() 判断的是 SQL 语句是否包含该字段,不是实际值是否改变
这是最容易踩的坑:UPDATE('price') 返回 TRUE,只说明你写的 UPDATE ... SET price = 100 这条语句里出现了 price 字段,哪怕新旧值完全一样(比如 SET price = OLD.price),它也返回 TRUE。
所以真要检测“值变了”,必须显式比较:
IF UPDATE('price') AND NEW.price != OLD.price THEN ...- 注意 NULL 安全比较:用
NEW.price OLD.price替代!=,否则NULL != NULL返回NULL(即 false) - 字符串字段慎用
=,要考虑 collation 和尾部空格;必要时加TRIM()或用BINARY强制字节比较
多个字段组合判断时,UPDATE() 要逐个写,不能传数组或逗号分隔
你不能写 UPDATE('name', 'email') 或 UPDATE(['name','email']) —— 语法错误。每个字段都得单独调用 UPDATE(),再用 AND/OR 拼接。
常见场景:只有当 status 和 updated_at 同时被更新时才记录日志:
IF UPDATE('status') AND UPDATE('updated_at') THEN
INSERT INTO log_table (...) VALUES (...);
END IF;
但注意:这只能保证这两个字段出现在 SET 子句里,不保证它们的值真的变了。如果业务要求“两个值都变才动作”,就得补上 NEW.status != OLD.status AND NEW.updated_at != OLD.updated_at。
UPDATE() 在低版本 MySQL 中行为一致,但高版本不解决本质缺陷
从 MySQL 5.7 到 8.0,UPDATE() 的行为没变过——它始终是语法层面的检查。有人误以为升级到 8.0 就能用 JSON_CONTAINS() 或窗口函数绕过,其实不行:触发器里不支持大多数 JSON 函数,也不能用子查询查自身表(会报 Can't update table 't' in stored function/trigger)。
真正可靠的替代方案极少,目前最稳的还是:
- 坚持用
UPDATE('col') AND (NEW.col OLD.col) = FALSE - 对大字段(如
TEXT)做变更检测前先加长度判断:IF UPDATE('content') AND LENGTH(NEW.content) != LENGTH(OLD.content),避免全量比对拖慢触发器 - 如果逻辑太重,考虑把判断逻辑移到应用层,触发器只做轻量标记(比如设
is_dirty = 1),后续由定时任务或应用兜底处理
触发器里的字段变更判断,从来就不是“有没有函数”的问题,而是“你愿不愿意为每次 UPDATE 都多跑一次值比较”的权衡。漏掉 或默认忽略 NULL,线上就可能静默跳过关键逻辑。


















