bulk insert 默认不触发触发器,是sql server的设计行为而非bug;它绕过约束检查、日志记录和触发器执行,仅insert/update/delete等显式dml语句才激活触发器。

SQL Server 的 BULK INSERT 默认不触发触发器
直接结论:BULK INSERT 命令默认绕过所有触发器(包括 AFTER INSERT),这不是 bug,是设计行为。它走的是高吞吐路径,跳过约束检查、日志记录和触发器执行——所以你看到“数据进去了,但触发器没动”,不是失效,是压根没被调用。
验证方式很简单:执行一次 BULK INSERT 后查 sys.triggers 的执行日志(如果你有日志表)或加 PRINT 语句,会发现什么都没输出。而用普通 INSERT 就能立刻看到触发效果。
-
BULK INSERT不受ENABLE TRIGGER控制,启用也没用 - 即使触发器状态是
is_disabled = 0,它依然不会运行 - 该行为在 SQL Server 所有主流版本(2016–2022)中一致
如何让批量导入触发触发器?用 INSERT ... SELECT 替代 BULK INSERT
想保留触发器逻辑,就别用 BULK INSERT。换成带触发器支持的等价操作:INSERT INTO target_table SELECT * FROM OPENROWSET(...) 或先 BULK INSERT 到临时表,再 INSERT INTO target_table SELECT * FROM #staging。
关键点在于:只有显式 INSERT、UPDATE、DELETE 语句才会激活触发器;BULK INSERT、BCP、SSIS 的“快速加载”模式默认都不走这条路径。
- 临时表方案最稳妥:建
#staging,BULK INSERT进去,再INSERT INTO real_table SELECT ... - 如果源是文件,可用
OPENROWSET(BULK ...)配合INSERT ... SELECT,它会触发触发器 - 注意:
SELECT INTO创建新表时也不会触发目标表的触发器(因为表还不存在)
MySQL 的 LOAD DATA INFILE 同样跳过触发器
MySQL 用户容易踩的坑:用 LOAD DATA INFILE 导入 CSV 时,BEFORE INSERT 和 AFTER INSERT 全部静默失效——和 SQL Server 的 BULK INSERT 是同一类机制。
官方文档明确写:“LOAD DATA INFILE does not activate triggers.” 它绕过触发器、外键检查、甚至部分约束(取决于 sql_mode)。所以你在触发器里写的日志插入、字段修正、权限校验,全都不会发生。
- 替代方案:用
INSERT ... VALUES循环或客户端分批提交(控制每批 ≤ 1000 行) - 或者先
LOAD DATA INFILE到中间表,再INSERT INTO main_table SELECT ... - 若必须用
LOAD DATA,所有业务逻辑得提前挪到 ETL 层或应用层做
触发器是否生效,第一步永远是确认操作类型是否被支持
很多人花几小时 debug 触发器逻辑,最后发现只是用了 REPLACE INTO(MySQL)、BULK INSERT(SQL Server)或 LOAD DATA(MySQL)——这些命令根本不在触发器的监听名单里。
不要一上来就怀疑 IF 条件、NEW 值或事务隔离级别。先问自己:我当前执行的 SQL 命令,在对应数据库的文档里,是否明确写了 “triggers are activated”?如果没有,那就不是你的触发器有问题,是你的导入方式选错了。
尤其注意那些看起来像 DML 实际不是的语法:比如 MySQL 的 INSERT ON DUPLICATE KEY UPDATE 只触发 BEFORE UPDATE(8.0.19 前),SQL Server 的 MERGE 在某些条件下也跳过触发器——这些细节不查文档很容易忽略。










