
MySQL 创建触发器前必须确认的权限和模式
没有 TRIGGER 权限,CREATE TRIGGER 会直接报错 ERROR 1227 (42501): Access denied; you need the TRIGGER privilege for this operation。普通开发账号常被忽略这点,DBA 通常只给 SELECT/INSERT/UPDATE/DELETE,忘了开触发器权限。
另外,MySQL 8.0+ 默认开启 sql_mode 中的 STRICT_TRANS_TABLES,如果触发器里插入的数据违反约束(比如往非空字段插 NULL),整个语句会失败并回滚——这不是触发器写错了,是模式在“较真”。
- 用
SHOW GRANTS FOR CURRENT_USER;检查是否有TRIGGER权限 - 临时关闭严格模式调试可用:
SET sql_mode = '';(仅会话级,别在生产执行) - 触发器只能建在「永久表」上,不能用于临时表或视图
INSERT 触发器里读取新数据要用 NEW,不是 INSERTED
SQL Server 用 INSERTED,PostgreSQL 用 NEW,MySQL 只认 NEW——写成 INSERTED.id 或 new.id(小写)都会语法报错 ERROR 1327 (42000): Undeclared variable: new。
常见场景:记录日志、自动生成 UUID、校验字段逻辑。注意 NEW 在 BEFORE INSERT 中可修改,在 AFTER INSERT 中只读。
-
BEFORE INSERT:能改NEW.column_name,比如SET NEW.created_at = NOW(); -
AFTER INSERT:只能读,不能赋值,否则报错ERROR 1362 (HY000): Updating of NEW row is not allowed in after trigger - 对自增主键,
NEW.id在BEFORE里是0或NULL,到AFTER才有真实值
UPDATE 触发器要同时处理 OLD 和 NEW,且不能误判 NULL
想“只在 price 改变时更新 updated_at”,写成 IF OLD.price != NEW.price THEN 看似合理,但只要任一端是 NULL,整个表达式结果就是 UNKNOWN,条件不成立——这是 MySQL 三值逻辑的坑,不是 bug。
正确写法必须显式处理 NULL:用 IS NOT DISTINCT FROM(MySQL 8.0.2+)或拆开判断。
- 兼容旧版写法:
IF (OLD.price NEW.price) = 0 THEN(是安全等于,NULL 也返回布尔值) - 别在触发器里调
SELECT ... FOR UPDATE或写其他表,容易引发死锁 - 触发器里不能用事务控制语句(
COMMIT/ROLLBACK),会报错ERROR 1305 (42000): SAVEPOINT does not exist
删除或修改触发器只能 DROP,没有 ALTER TRIGGER
MySQL 不支持 ALTER TRIGGER,改逻辑必须先 DROP TRIGGER 再 CREATE。但直接删可能被其他人在执行中调用,导致短暂缺失逻辑。
更稳妥的做法是:先创建新名字的触发器,验证无误后,再删旧的。注意触发器名全局唯一,不能跨库重名(哪怕不同数据库)。
- 查已有触发器:
SHOW TRIGGERS LIKE 'orders';或查information_schema.TRIGGERS - 删触发器:
DROP TRIGGER IF EXISTS before_insert_orders;(加IF EXISTS避免不存在时报错) - 触发器定义不存于建表语句里,
SHOW CREATE TABLE不显示它,容易漏掉
AFTER 触发器调外部存储或慢查询时的延迟放大效应。










