很多人第一次在 SQL Server 触发器里见到 deleted,都会把它当成一张“系统自动生成的表”来理解,结果一到批量更新、软删除或数据恢复场景就容易踩坑。本文按触发器生命周期、联表方式和归档恢复三个层面拆开讲,帮助你判断 deleted 到底能做什么、不能做什么,以及哪些写法在生产环境里更稳。
deleted 表什么时候存在,什么时候一定不要查
deleted 并不是真实存在的表,而是 SQL Server 在特定触发器执行期间提供的一份内存临时快照。它不能被 CREATE 或 DROP,也不能跨触发器访问;一旦当前事务结束,这份数据就会被销毁。

只要触发器类型是 AFTER DELETE、AFTER UPDATE 或者 INSTEAD OF DELETE/UPDATE,SQL Server 就会创建这个临时结构,其中保存的是操作发生前的原始行。
有一个非常常见的误区:在 AFTER INSERT 触发器里查询 deleted。这时结果为空,因为 INSERT 并不涉及旧数据,deleted 自然不会有内容。
两类操作里,deleted 存的内容并不一样
- DELETE 操作:
deleted包含全部待删行,可直接用于归档、审计或删除前校验。 - UPDATE 操作:
deleted只保存更新前的旧值,通常要和inserted联查才能判断字段变更。 - 触发器外部:不要在普通查询环境里直接
SELECTdeleted,否则会报“对象名 'deleted' 无效”。
为什么 deleted 和 inserted 联查必须靠主键
在 UPDATE 触发器中,最常见的需求是对比更新前后的字段值。这时很多人会想当然地认为 deleted 和 inserted 行序一致,于是写出类似 SELECT TOP 1 FROM deleted 配合 ORDER BY (SELECT NULL) 的代码,试图“取同一行”的旧值和新值。

这类写法不可靠。SQL Server 不保证两张伪表的行序天然一致,尤其是在批量操作时,依赖顺序几乎等于埋雷。
正确做法是始终使用主键或唯一键进行 INNER JOIN:
SELECT d.Id, d.Email AS old_email, i.Email AS new_email FROM deleted d INNER JOIN inserted i ON d.Id = i.Id;
联表时需要注意的三个细节
- 如果主键是
(OrderId, LineNo)这样的复合键,ON条件必须写全:ON d.OrderId = i.OrderId AND d.LineNo = i.LineNo。 - 避免直接写
SELECT *。宽表遇到大批量更新时,读取全部列会明显拖慢触发器执行。 - 不要用
LEFT JOIN deleted去“判断是否删除”。在AFTER UPDATE触发器里,deleted本来就一定非空。
做软删除时,为什么要用 INSTEAD OF DELETE
如果你的目标不是把数据真的删掉,而是实现逻辑删除与后续恢复,那么关键点并不是“能不能读到 deleted”,而是“必须阻止物理删除真正发生”。

因此,这类设计应优先使用 INSTEAD OF DELETE。如果仍然使用普通删除后触发的逻辑,触发器执行时原表行已经进入物理删除流程,虽然 deleted 里还能看到旧值,但它不能代替稳定的软删方案,碰到并发事务时风险更高。
软删触发器的配套设计
- 原表需要有
IsDeleted BIT DEFAULT 0字段,用来承载逻辑删除状态。 - 归档表建议放在独立数据库,至少也要放在独立文件组,避免主库故障时连归档一起丢失。
- 触发器开头必须写
SET NOCOUNT ON,否则某些 ORM(如 Entity Framework)可能把归档INSERT的影响行数误判成主语句结果并抛异常。 - 不要在触发器里调用另一个可能再次触发当前逻辑的存储过程,否则很容易出现嵌套调用甚至死循环。
deleted 不能直接恢复数据,真正能恢复的是归档
deleted 的生命周期只存在于当前触发器作用域内,事务一结束就会销毁。所以“从 deleted 恢复数据”这个说法本身并不准确,真正可恢复的是你提前写入归档表的数据,例如 Products_Archive。
恢复操作本身也不建议再绕回触发器,而应直接对原表执行更新:
UPDATE Products SET IsDeleted = 0 WHERE Id IN (SELECT Id FROM Products_Archive WHERE DeletedAt > '2026-08-01');
恢复和归档阶段的性能建议
- 多条恢复不要长期依赖
WHERE Id IN (子查询)配合大归档表,容易带来内存压力;更稳妥的方式是使用临时表加JOIN。 - 归档表至少应包含
DeletedAt和DeletedBy,否则恢复时很难判断是谁删的、什么时候删的。 - 归档表索引不要堆得过多。写入频繁的场景下,索引维护成本会直接拖慢软删除链路。
一组更稳的使用原则
- 把
deleted当成触发器作用域内的临时快照,而不是可持久访问的系统表。 - 在
UPDATE场景中,用主键或唯一键把deleted与inserted成对联查。 - 涉及归档、审计和恢复时,核心资产不是
deleted本身,而是你写入归档库的数据结构。 - 涉及软删除时,先决定是否要阻止物理删除,再选择
INSTEAD OF DELETE这样的触发方式。







