sql server和mysql的dml触发器均为语句级,非标准行级;批量操作时inserted/deleted含多行,误当单行处理导致逻辑错误,应使用集合操作而非变量赋值或游标。

SQL Server 和 MySQL 的 DML 触发器全是语句级的
所谓“失效”,90% 是触发器确实执行了,但只处理了 INSERTED 或 DELETED 里的某一行——因为误把它当单行结果集用了。SQL Server 和 MySQL 都不支持标准的 FOR EACH ROW 行级触发器(MySQL 5.7+ 支持语法,但仍是语句级语义;Oracle、PostgreSQL 才真有行级)。一次 UPDATE t SET x = 1 WHERE id IN (1,2,3,...,5000),触发器只跑 1 次,INSERTED 里却有 5000 行新值,DELETED 里对应 5000 行旧值。
- 错误写法:
DECLARE @id INT; SELECT @id = id FROM INSERTED;—— 多行时 @id 取哪行?SQL Server 不报错,但结果不可控 - 正确思路:把
INSERTED当成真实临时表,用JOIN、GROUP BY、EXISTS等集合操作处理 - 验证是否真多行:在触发器里加日志
INSERT INTO debug_log SELECT 'update', COUNT(*) FROM INSERTED;,看记录数是不是你预期的批量数
MySQL 中 REPLACE 和 ON DUPLICATE KEY UPDATE 不触发 BEFORE INSERT
这是大批量导入场景下最隐蔽的“失效”:你以为写了 BEFORE INSERT 做校验,结果 REPLACE INTO 或 INSERT ... ON DUPLICATE KEY UPDATE 根本不走它。MySQL 8.0.19 之前,这类语句只可能触发 BEFORE UPDATE(如果键冲突),BEFORE INSERT 完全跳过。
- 典型现象:插入重复主键数据,校验逻辑没生效,脏数据进库
- 确认方式:
SHOW TRIGGERS LIKE 'your_table';查Timing和Event是否匹配你实际执行的语句类型 - 规避方案:
INSERT ... SELECT替代REPLACE;或改用MERGE(SQL Server)、INSERT ON CONFLICT(PostgreSQL);MySQL 下可先DELETE再INSERT,确保触发时机可控
别用游标遍历 INSERTED,那是性能毒药
看到“只处理了一行”,第一反应是加游标逐条处理 INSERTED,这在 SQL Server 里很常见,但属于反模式:游标强制序列化,锁住整张表更久,日志膨胀翻倍,10 万行批量可能从秒级拖到分钟级。
手动 Telegram 斜杠命令,用于查看 Codex 状态及使用情况。用户发送 /codex_usage、/codex_usage default、/codex_usage all 等时触发。
- 错误示范:
DECLARE cur CURSOR FOR SELECT id, val FROM INSERTED;+FETCH循环 - 替代做法:用
UPDATE t2 SET x = i.val FROM t2 INNER JOIN INSERTED i ON t2.id = i.id;,让优化器自动并行 - 关键点:目标表关联字段(如
t2.id)必须有索引,否则JOIN退化为嵌套循环,性能断崖下跌 - 如果真要逐行逻辑(比如调外部 API),说明不该放触发器里——该挪到应用层异步队列或定时批任务
WHERE IN (SELECT ...) 在触发器里大概率变慢
UPDATE t2 SET status = 'done' WHERE t2.id IN (SELECT id FROM INSERTED); 看着干净,实则高危。因为 INSERTED 是内存临时表,没统计信息,SQL Server 容易选错执行计划;MySQL 8.0 前还不支持对 NEW/OLD 加索引提示。
- 慢的根源常不在触发器本身,而在主表
t2.id缺索引,或优化器误判IN子查询为低效嵌套 - 推荐写法:
UPDATE t2 SET status = 'done' WHERE EXISTS (SELECT 1 FROM INSERTED i WHERE i.id = t2.id);,SQL Server 更倾向用哈希匹配 - 极端情况可显式建临时表:
SELECT id INTO #tmp_ids FROM INSERTED; CREATE INDEX ix_id ON #tmp_ids(id);,再拿它去JOIN
真正难的不是让触发器“跑起来”,而是戒掉“这一行刚进来,我立刻处理它”的直觉。只要代码里还出现 @var =、DECLARE CURSOR、或任何试图把 INSERTED 当单行容器的地方,它在批量场景下就不值得信任。










