触发器在批量导入时默认不生效,load data infile、bulk insert、copy等命令为性能绕过触发器;仅标准insert/update语句才触发,清洗应前置到导入前或通过set子句、check约束实现。

不能。触发器在批量导入时默认不生效,所谓“自动清洗”是常见误解。
LOAD DATA INFILE 和 BULK INSERT 会跳过触发器
MySQL 的 LOAD DATA INFILE、SQL Server 的 BULK INSERT、PostgreSQL 的 COPY 命令,设计初衷就是绕过约束和触发器以换取性能。它们不走标准 SQL 插入路径,因此 BEFORE INSERT 触发器根本不会被调用。
- 即使你写了完美的
TRIM(LOWER(NEW.email)),CSV 里带空格的邮箱也会原样入库 -
FIRE_TRIGGERS是 SQL Server 的可选参数,但仅对BULK INSERT有效,且会显著降低吞吐量(实测下降 3–5 倍) - PostgreSQL 的
COPY没有等效开关;想触发,必须改用INSERT INTO ... SELECT ... FROM ...拆解,但这就不是“批量导入”了
触发器只对逐行 INSERT/UPDATE 生效
只有当数据通过标准 DML 语句写入时,触发器才起作用。典型场景包括:
- Web 表单提交调用的
INSERT INTO users (...) VALUES (...) - API 层执行的单条或小批量
INSERT ... SELECT(注意:MySQL 对这种语句仍逐行触发) - 应用层 ORM 的 save() 或 create() 方法(前提是未启用 bulk_create)
此时可安全使用:SET NEW.phone = REGEXP_REPLACE(NEW.phone, '[^0-9]', ''),但需确认 MySQL 版本 ≥ 8.0;5.7 只能嵌套多次 REPLACE()。
真正该做的:把清洗移到导入前或约束层
依赖触发器做批量清洗,本质是把问题留到入库时刻——既难调试,又掩盖源头缺陷。正确路径是:
- 用 Python/Pandas 或
awk/sed预处理 CSV:去除 BOM、统一换行符、正则清理字段,再导入 - 建表时加
CHECK约束,例如:CONSTRAINT chk_email CHECK (email REGEXP '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$'),让非法数据直接报错拒入 - 对已入库脏数据,用 CTE 分步清洗并验证,例如先
WITH clean AS (...) SELECT * FROM clean WHERE cleaned_phone IS NOT NULL预览结果,再UPDATE
最易被忽略的一点:LOAD DATA INFILE 支持 SET 子句,可在导入瞬间做简单转换,比如 SET email = TRIM(LOWER(@email)) —— 这比触发器更轻量、更可控,且不牺牲性能。










