AFTER DELETE触发器必须显式列出deleted字段或用FOR JSON AUTO序列化,避免SELECT *、单变量赋值及原表回查;需结合sys.dm_exec_connections获取IP、ORIGINAL_LOGIN()获取用户,并确保权限与字段同步。
直接 SELECT * FROM deleted 是高危操作,多行删除时字段错位、计算列为空、LOB 字段报错都可能发生。触发器必须适配真实表结构变化,不能靠“猜”。
id, name, email, created_at,就写 SELECT id, name, email, created_at FROM deleted,别省事INSERT INTO otherdb.dbo.audit_table SELECT ... FROM deleted 可能因权限或四部分命名失败,优先用同库 + 链接服务器或应用层中转DECLARE @id INT; SELECT @id = id FROM deleted 在删 10 行时只保留其中一行的值,SQL Server 不保证哪一行被赋值需要完整快照又不想每改一次表就手动同步触发器字段?用 FOR JSON AUTO 是目前最稳的方案,它不依赖列名顺序、自动跳过计算列、兼容稀疏列和 LOB。
SELECT (SELECT * FROM deleted FOR JSON AUTO) AS DeletedData,返回一个 NVARCHAR(MAX) 字符串,可直接插入日志表的 DeletedData 字段CONVERT(NVARCHAR(MAX), ...) 拼接:datetime 会变成无时区字符串,uniqueidentifier 缺少大括号,后续解析困难仅存数据不够,审计要求知道“谁、何时、从哪删的”。SQL Server 提供系统视图和函数,但调用时机和权限要卡准。
SELECT TOP 1 client_net_address FROM sys.dm_exec_connections WHERE session_id = @@SPID,必须加 TOP 1,否则多行结果会导致赋值失败ORIGINAL_LOGIN() 比 SUSER_NAME() 更可靠,后者可能被上下文切换影响GETDATE() 即可,不要用 SYSDATETIMEOFFSET() 除非你明确需要时区信息且日志表字段类型匹配sys.dm_exec_connections 需要 VIEW SERVER STATE 权限,部署前确认执行触发器的账号有该权限常见误区是写 IF EXISTS (SELECT 1 FROM Orders WHERE id IN (SELECT id FROM deleted)) 来“确认是否真删了”,这逻辑冗余且危险。
BEFORE DELETE 阶段做,而不是在 AFTER 里回头查触发器本身不保存上下文,所有字段映射、序列化方式、权限检查都得人工对齐——哪怕只加一列,忘了改触发器,日志就断。最易忽略的是 deleted 表在级联删除中只含直删行,子表变动不会出现其中。