web*_*orm 13 trigger sql-server audit sql-server-2012
在设置审计跟踪时,跟踪谁在表中更新或插入记录没有问题,但是,跟踪谁删除记录似乎更成问题。
我可以通过在插入/更新字段中包含“UpdatedBy”来跟踪插入/更新。这允许 INSERT/UPDATE 触发器通过inserted.UpdatedBy
. 但是,使用删除触发器不会插入/更新数据。有没有办法将信息传递给删除触发器,以便它知道谁删除了记录?
这是一个插入/更新触发器
ALTER TRIGGER [dbo].[trg_MyTable_InsertUpdate]
ON [dbo].[MyTable]
FOR INSERT, UPDATE
AS
INSERT INTO AuditTable (IdOfRecordedAffected, UserWhoMadeChanges)
VALUES (inserted.ID, inserted.LastUpdatedBy)
FROM inserted
Run Code Online (Sandbox Code Playgroud)
使用 SQL Server 2012
Sol*_*zky 13
有没有办法将信息传递给删除触发器,以便它知道谁删除了记录?
是的:通过使用一个非常酷的(并且未被充分利用的功能)称为CONTEXT_INFO
. 它本质上是存在于所有作用域中并且不受事务约束的会话内存。它可用于将信息(任何信息——好吧,任何适合有限空间的信息)传递给触发器以及在 sub-proc/EXEC 调用之间来回传递。我以前在这种完全相同的情况下使用过它。
上下文信息是一个 VARBINARY(128)
通过设置:SET CONTEXT_INFO
通过:CONTEXT_INFO()获取
用下面的测试看看它是如何工作的。请注意,我转换成CHAR(128)
前CONVERT(VARBINARY(128), ..
。这是力空白填充,使其更容易转换回VARCHAR
得到它的时候CONTEXT_INFO()
,因为VARBINARY(128)
是右侧填充0x00
秒。
SELECT CONTEXT_INFO();
-- Initially = NULL
DECLARE @EncodedUser VARBINARY(128);
SET @EncodedUser = CONVERT(VARBINARY(128),
CONVERT(CHAR(128), 'I deleted ALL your records! HA HA!')
);
SET CONTEXT_INFO @EncodedUser;
SELECT CONTEXT_INFO() AS [RawContextInfo],
RTRIM(CONVERT(VARCHAR(128), CONTEXT_INFO())) AS [DecodedUser];
Run Code Online (Sandbox Code Playgroud)
结果:
0x492064656C6574656420414C4C20796F7572207265636F7264732120484120484121202020202020...
I deleted ALL your records! HA HA!
Run Code Online (Sandbox Code Playgroud)
把它放在一起:
该应用程序应该调用一个“删除”存储过程,该过程传入正在删除记录的用户名(或其他任何内容)。我假设这已经是正在使用的模型,因为听起来您已经在跟踪插入和更新操作。
“删除”存储过程执行以下操作:
DECLARE @EncodedUser VARBINARY(128);
SET @EncodedUser = CONVERT(VARBINARY(128),
CONVERT(CHAR(128), @UserName)
);
SET CONTEXT_INFO @EncodedUser;
-- DELETE STUFF HERE
Run Code Online (Sandbox Code Playgroud)审计触发器执行以下操作:
-- Set the INT value in LEFT (currently 50) to the max size of [UserWhoMadeChanges]
INSERT INTO AuditTable (IdOfRecordedAffected, UserWhoMadeChanges)
SELECT del.ID, COALESCE(
LEFT(RTRIM(CONVERT(VARCHAR(128), CONTEXT_INFO())), 50),
'<unknown>')
FROM DELETED del;
Run Code Online (Sandbox Code Playgroud)请注意,正如@SeanGallardy 在评论中指出的那样,由于其他程序和/或临时查询从该表中删除记录,有可能:
CONTEXT_INFO
尚未设置,仍然是NULL
:
出于这个原因,我更新了上面的内容INSERT INTO AuditTable
以使用 aCOALESCE
来默认值。或者,如果您不想要默认值并需要一个名称,那么您可以执行类似以下操作:
DECLARE @UserName VARCHAR(50); -- set to the size of AuditTable.[UserWhoMadeChanges]
SET @UserName = LEFT(RTRIM(CONVERT(VARCHAR(128), CONTEXT_INFO())), 50);
IF (@UserName IS NULL)
BEGIN
ROLLBACK TRAN; -- cancel the DELETE operation
RAISERROR('Please set UserName via "SET CONTEXT_INFO.." and try again.', 16 ,1);
END;
-- use @UserName in the INSERT...SELECT
Run Code Online (Sandbox Code Playgroud)CONTEXT_INFO
已设定为一个值不是一个有效的用户名,因此可能会超过大小AuditTable.[UserWhoMadeChanges]
字段:
出于这个原因,我添加了一个LEFT
功能来确保从里面抓取的任何东西CONTEXT_INFO
都不会破坏INSERT
. 如代码中所述,您只需将 设置50
为UserWhoMadeChanges
字段的实际大小。
SQL SERVER 2016 及更新版本的更新
SQL Server 2016 添加了此每会话内存的改进版本:会话上下文。新的会话上下文本质上是一个键值对的哈希表,其中“键”是类型sysname
(即NVARCHAR(128)
),“值”是SQL_VARIANT
。意义:
CONTEXT_INFO()
(有关详细信息,请参阅我的帖子:为什么 CONTEXT_INFO() 不返回由 SET CONTEXT_INFO 设置的确切值?)CONTEXT_INFO
)有关详细信息,请参阅以下文档页面:
您不能那样做,除非您希望记录 SQL 服务器用户 ID 而不是应用程序级别的用户 ID。
您可以通过一个名为 DeletedBy 的列并根据需要进行设置来进行软删除,然后您的更新触发器可以进行真正的删除(或存档记录,我通常会在可能且合法的情况下避免硬删除)以及更新您的审计跟踪. 要以这种方式强制删除,请定义一个on delete
引发错误的触发器。如果您不想在物理表中添加列,您可以定义一个视图来添加列并定义instead of
触发器来处理更新基表,但这可能有点矫枉过正。
归档时间: |
|
查看次数: |
17826 次 |
最近记录: |