inserted和deleted是仅在触发器内有效的逻辑表,非真实表,不能在触发器外访问;直接SELECT * FROM inserted不报错但结果不可靠,因多行时未JOIN或WHERE会导致错位、漏数据或空集。

inserted 和 deleted 表不是普通表,不能直接 SELECT * 或 JOIN 就完事——必须结合触发器上下文、操作类型和字段匹配来取值,否则容易查空、报错或逻辑错位。
为什么直接 SELECT inserted 会报错?
因为 inserted 和 deleted 是逻辑表(pseudo-tables),只在触发器作用域内存在,且仅在对应 DML 操作发生时才被 SQL Server 自动填充。常见错误包括:
-
Msg 208, Level 16: Invalid object name 'inserted'—— 在触发器外执行SELECT * FROM inserted - 在
AFTER INSERT触发器中查deleted,结果永远为空(它只含 UPDATE/DELETE 前的旧值) - 字段名写错(比如用
id而实际主键是user_id),导致SELECT报列不存在
INSERT/UPDATE/DELETE 场景下怎么安全取数据?
核心原则:先判断表是否非空,再按需读取。不要假设某张表一定有数据。
-
INSERT:只读
inserted,deleted必为空 —— 可用IF EXISTS (SELECT 1 FROM inserted)判断 -
DELETE:只读
deleted,inserted必为空 —— 同样建议加EXISTS检查 -
UPDATE:两张表都可能有数据,且行数相同(每行更新前进
deleted,更新后进inserted)。必须用主键或唯一键JOIN对齐,例如:SELECT i.user_id, d.balance AS old_balance, i.balance AS new_balance FROM inserted i INNER JOIN deleted d ON i.user_id = d.user_id
多行操作时怎么避免只取到第一行?
触发器天然支持批量操作(比如 UPDATE users SET status=1 WHERE id IN (1,2,3) 会一次性触发一次触发器,inserted 和 deleted 都含 3 行)。常见误区是用标量变量赋值:
- ❌ 错误写法:
SELECT @id = id FROM inserted—— 若多行,@id只拿到最后一行值,不可靠 - ✅ 正确做法:用集合操作,比如直接
INSERT INTO log_table SELECT user_id, action, GETDATE() FROM inserted - 如需逐行处理逻辑(极少见),应改用游标或
WHILE+TOP 1,但性能差,优先走集合式
text/ntext/image 字段在 inserted/deleted 中不可用
SQL Server 2005+ 的 AFTER 触发器中,inserted 和 deleted 表对 text、ntext、image 类型字段返回 NULL(即使原表有值)。这是硬限制,不是配置问题。
- 替代方案:改用
varchar(max)、nvarchar(max)、varbinary(max) - 若无法改类型,只能在触发器外通过主键查原表获取这些字段(注意并发风险)
-
INSTEAD OF触发器不受此限制,但适用场景更窄(比如视图更新)
真正容易被忽略的是:UPDATE 操作中,inserted 和 deleted 表的行顺序不保证一致,也不能依赖物理顺序做隐式匹配——必须显式用主键或唯一约束字段 JOIN,否则两表错位会导致日志记录张冠李戴。

















