使用触发器时检测哪些列已更新的最佳方法

时间:2010-10-29 12:41:12

标签: sql sql-server triggers audit

我有一个用户表,其中包含| Firstname | Surname | DateOfBirth | ......以及其他一些专栏。在该特定表上,我有一个更新触发器,如果​​有人更改任何用户值,则会创建审核。

我的问题是,管理层希望以下列方式显示视觉审核:

DateChanged |Change                        |Details
--------------------------------------------------------------------------------
2010/01/01  |FirstName Changed             |FirstName Changed from "John" to "Joe"  
2010/01/01  |FirstName And Lastname Changed|FirstName Changed from "Smith" to "White "And Lastname changed from "els" to "brown"
2010/01/01  |User Deleted                  |"ark Anrdrews" was deleted

我相信解决这个问题的最佳方法是修改存储过程,但是如何指示触发器记录这些事情呢?

2 个答案:

答案 0 :(得分:5)

您可以在名为COLUMS_UPDATED()的触发器中调用一个函数。这将返回触发器表中列的位图,其中1表示表中的相应列已被修改。它本身就不那么精彩,因为你最终会得到一个列索引列表,并且仍然需要编写一个CASE语句来确定哪些列被修改。如果你没有很多列可能不那么糟糕,但如果你想要更通用的东西,你可以加入information_schema.columns将这些列索引转换为列名

这是一个小样本,展示你可以做的事情:

CREATE TABLE test (
ID Int NOT null IDENTITY(1,1),
NAME VARCHAR(50),
Age INT
)

 GO
---------------------------------------
CREATE TRIGGER tr_test ON test FOR UPDATE 

AS

DECLARE @cols varbinary= COLUMNS_UPDATED()
DECLARE @altered TABLE(colid int)
DECLARE @index int=1

WHILE @cols>0 BEGIN
    IF @cols & 1 <> 0 INSERT @altered(colid) VALUES(@index)
    SET @index=@index+1
    SET @cols=@cols/2
END 

DECLARE @text varchar(MAX)=''
SELECT @text=@text+C.COLUMN_NAME+', '
FROM information_schema.COLUMNS C JOIN @altered A ON c.ordinal_position=A.colid
WHERE table_name='test'

PRINT @text

GO
------------------------------------------------------
INSERT test(NAME, age) VALUES('test',12)
UPDATE test SET age=15
UPDATE test SET NAME='fred'
UPDATE test SET age=18,NAME='bert'
DROP TABLE test

答案 1 :(得分:1)

MS SQL Server具有UPDATED()函数。它允许您指定字段名称以告知是否有任何记录已更改该字段。

然而,我的经验是,所需的功能性通常超过这种通用功能的限制。我通常最终只是简单地将INSERTED伪表连接到DELETED伪表并自己进行比较。

*这样的比较假定表具有唯一标识,无论是单个字段还是某个字段的复合键。