MySQL触发器对批量INSERT是逐行校验:每插入一行就执行一次BEFORE INSERT逻辑,NEW仅访问当前行数据,无法跨行判断或聚合,因MySQL仅支持强制的行级触发器(FOR EACH ROW),不支持语句级触发。
MySQL触发器对批量INSERT是逐行校验,不是一次性校验整批数据——每插入一行就执行一次BEFORE INSERT逻辑,用NEW访问当前行,无法跨行判断或聚合。
MySQL只支持行级触发器,FOR EACH ROW不是可选语法,而是强制要求。哪怕你写INSERT INTO t VALUES (1,'a'),(2,'b'),(3,'c'),也会触发3次BEFORE INSERT,每次NEW.id和NEW.name只对应其中一行。
NEW在每次触发时重置,上一行的值不可见INSERTED临时表,也无法在触发器里查刚插入的其他行(因为它们还没提交,且事务隔离限制)SIGNAL中断(如邮箱格式错误),整条批量语句回滚,其余行也不会插入校验逻辑必须基于当前行字段,不能依赖其他行。常见写法示例如下:
DELIMITER $$CREATE TRIGGER validate_user_before_insert BEFORE INSERT ON users FOR EACH ROW BEGIN IF NEW.email NOT LIKE '%_@__%.__%' THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Invalid email format'; END IF; IF NEW.status NOT IN ('active', 'inactive') THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Invalid status value'; END IF;END$$DELIMITER ;
NEW在BEFORE INSERT中可读可写,适合改写字段(如自动填充NEW.created_at = NOW())SELECT其他表(如查黑名单),否则批量插入性能急剧下降LIKE而非REGEXP,MySQL 8.0+虽支持正则,但开销更大触发器不适合做“本批内去重”(比如防止同一批里出现两个相同email),因为它看不到同批其他行。真正可靠的方案是靠数据库约束:
ALTER TABLE users ADD UNIQUE KEY uk_email (email);
INSERT IGNORE跳过冲突行,或INSERT ... ON DUPLICATE KEY UPDATE做合并SELECT COUNT(*) FROM users WHERE email = NEW.email——这只能查已有数据,查不到同批其他待插入行触发器不是万能拦截器,以下情况它根本不会运行:
LOAD DATA INFILE默认绕过触发器(除非显式启用LOCAL并配置sql_log_bin=ON)saveAll)可能生成INSERT ... SELECT,部分MySQL版本不触发TRIGGER权限时,创建成功但执行时报ERROR 1227 (42501)
slave_sql_verify_checksum=OFF等配置影响),主从行为不一致真正需要跨行逻辑(比如统计本次插入总金额、批量同步到汇总表),别硬塞进触发器——移到应用层或用存储过程统一控制事务边界和执行顺序。