before insert触发器的核心作用是在insert语句落库前校验字段、自动填充默认值、重写new字段或抛出错误中断插入;它可修改new值,而after insert不可。

BEFORE INSERT触发器必须在数据写入表之前生效,才能修改 NEW 值或阻止插入;用 AFTER INSERT 就来不及改了。
BEFORE INSERT 触发器的核心作用是什么
它在 INSERT 语句真正落库前执行,允许你:校验字段值(比如拒绝负数)、自动填充默认值(如 create_time)、重写 NEW 字段(如转小写、截断超长字符串)、或抛出错误中断插入。
关键点在于:只有 BEFORE 能改 NEW,AFTER 只能读 NEW,不能改。
-
NEW是唯一可用的上下文变量,代表即将插入的那行数据 - 不能引用
OLD(因为还没“旧”数据) - 不能执行
SELECT ... INTO返回结果集,会报错ERROR 1415 (0A000): Not allowed to return a result set from a trigger
创建时必须处理分号冲突
MySQL 默认用分号 ; 当语句结束符,但触发器体里要写多条语句(比如 SET + IF),就得先换结束符,否则解析器会在第一处 ; 就切开语句,报语法错。
- 开头加
DELIMITER //(或其他非分号符号) - 触发器定义末尾用
//结束,而不是; - 最后再用
DELIMITER ;恢复默认
漏掉这一步,常见报错是 ERROR 1064 (42000): You have an error in your SQL syntax,且错误位置往往指向 BEGIN 后第一个分号。
修改字段值必须用 SET NEW.xxx = ...
想让插入时自动补时间、标准化邮箱、或强制设置状态,就得在 BEGIN ... END 里对 NEW 赋值。不写 SET,字段就按原始值入库。
示例:给 users 表的 email 字段自动转小写,并设默认 status:
DELIMITER // CREATE TRIGGER before_insert_users BEFORE INSERT ON users FOR EACH ROW BEGIN SET NEW.email = LOWER(NEW.email); SET NEW.status = 'active'; END// DELIMITER ;
-
NEW.email和NEW.status必须存在对应列名,拼错会报Unknown column 'NEW.xxx' in 'field list' - 如果
NEW.email是NULL,LOWER(NULL)还是NULL,不会报错 - 不能对主键或自增列(如
id)赋值,除非明确关闭自增:SET NEW.id = ...仅当该列未设AUTO_INCREMENT时才有效
校验失败要用 SIGNAL 主动报错
靠 IF 判断后不做什么,插入照样发生。要拦住非法数据,必须用 SIGNAL 抛异常。
示例:禁止插入年龄小于 0 或大于 150 的记录:
DELIMITER //
CREATE TRIGGER check_age_before_insert
BEFORE INSERT ON users
FOR EACH ROW
BEGIN
IF NEW.age 150 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Age must be between 0 and 150';
END IF;
END//
DELIMITER ;
-
SQLSTATE '45000'是通用错误码,也可用更具体的如'HY000' -
MESSAGE_TEXT是客户端看到的错误信息,别写空字符串,否则只显示模糊的 “Unknown error” - 没写
SIGNAL,哪怕IF条件成立,也不会中断插入——这点极易被忽略
真正麻烦的不是写法,而是触发器一旦生效,所有插入路径(应用层、命令行、迁移脚本)都会受约束;上线前务必在测试环境用真实业务数据跑一遍全链路插入场景,尤其注意批量 INSERT 会为每一行都触发一次,性能敏感时得权衡。











