我使用以下代码(由Pop Rivett的SQL博客提供)在SQL Server表上为审计跟踪创建触发器。
除了一个例外,它效果很好。我需要记录究竟是谁做出了改变。如上所述,系统用户字段应该这样做,但只有在数据库中直接进行更改时才有效。如果通过网络应用程序完成,则系统用户是网络帐户而不是单个用户。
每个要修改的表都有一个UserName
列,它从.NET User.Identity.Name
开始,我想将其添加到每一行,但是当我尝试访问inserted.UserName
时或deleted.UserName
我遇到了错误。
如何添加列以捕获此信息?
CREATE TRIGGER [dbo].[tr_Grading]
ON [dbo].[Grading]
FOR INSERT, UPDATE, DELETE
AS
DECLARE @bit INT ,
@field INT ,
@maxfield INT ,
@char INT ,
@fieldname VARCHAR(128) ,
@TableName VARCHAR(128) ,
@PKCols VARCHAR(1000) ,
@sql VARCHAR(2000),
@UpdateDate VARCHAR(21) ,
@SystemName VARCHAR(128) ,
@Type CHAR(1) ,
@PKSelect VARCHAR(1000)
-- change @TableName to match the table to be audited
SELECT @TableName = 'Grading'
-- date and user
SELECT @SystemName = SYSTEM_USER,
@UpdateDate = CONVERT(VARCHAR(8), GETDATE(), 112)
+ ' ' + CONVERT(VARCHAR(12), GETDATE(), 114)
-- Action
IF EXISTS (SELECT * FROM inserted)
IF EXISTS (SELECT * FROM deleted)
SELECT @Type = 'U'
ELSE
SELECT @Type = 'I'
ELSE
SELECT @Type = 'D'
-- get list of columns
SELECT * INTO #ins FROM inserted
SELECT * INTO #del FROM deleted
-- Get primary key columns for full outer join
SELECT @PKCols = COALESCE(@PKCols + ' and', ' on')
+ ' i.' + c.COLUMN_NAME + ' = d.' + c.COLUMN_NAME
FROM
INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk ,
INFORMATION_SCHEMA.KEY_COLUMN_USAGE c
WHERE
pk.TABLE_NAME = @TableName
AND CONSTRAINT_TYPE = 'PRIMARY KEY'
AND c.TABLE_NAME = pk.TABLE_NAME
AND c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME
-- Get primary key select for insert
SELECT @PKSelect = COALESCE(@PKSelect+'+','')
+ '''<' + COLUMN_NAME
+ '=''+convert(varchar(100), coalesce(i.' + COLUMN_NAME +',d.' + COLUMN_NAME + '))+''>'''
FROM
INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk ,
INFORMATION_SCHEMA.KEY_COLUMN_USAGE c
WHERE
pk.TABLE_NAME = @TableName
AND CONSTRAINT_TYPE = 'PRIMARY KEY'
AND c.TABLE_NAME = pk.TABLE_NAME
AND c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME
IF @PKCols IS NULL
BEGIN
RAISERROR('no PK on table %s', 16, -1, @TableName)
RETURN
END
SELECT
@field = 0,
@maxfield = MAX(ORDINAL_POSITION)
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = @TableName
WHILE @field < @maxfield
BEGIN
SELECT @field = MIN(ORDINAL_POSITION)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = @TableName
AND ORDINAL_POSITION > @field
SELECT @bit = (@field - 1 )% 8 + 1
SELECT @bit = POWER(2,@bit - 1)
SELECT @char = ((@field - 1) / 8) + 1
IF SUBSTRING(COLUMNS_UPDATED(),@char, 1) & @bit > 0 OR @Type IN ('I','D')
BEGIN
SELECT @fieldname = COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = @TableName
AND ORDINAL_POSITION = @field
SELECT @sql = '
INSERT Audit(Type, TableName, PK, FieldName, OldValue, NewValue,
UpdateDate, SystemName)
SELECT
''' + @Type + ''',
''' + @TableName + ''',
' + @PKSelect + ',''' + @fieldname + ''''
+ ',convert(varchar(1000),d.' + @fieldname + ')'
+ ',convert(varchar(1000),i.' + @fieldname + ')'
+ ',''' + @UpdateDate + ''''
+ ',''' + @SystemName + ''''
+ ' from #ins i full outer join #del d'
+ @PKCols
+ ' where i.' + @fieldname + ' <> d.' + @fieldname
+ ' or (i.' + @fieldname + ' is null and d.' + @fieldname + ' is not null)'
+ ' or (i.' + @fieldname + ' is not null and d.' + @fieldname + ' is null)'
EXEC (@sql)
END
END
GO
答案 0 :(得分:0)
查看触发器,最后将动态SQL别名作为I插入,因此请尝试插入I.Username
,并在选择部分中删除D.Username
。
以下是对触发器中发生的事情的快速解释。
选择临时表格
SELECT * INTO #ins FROM inserted
SELECT * INTO #del FROM deleted
选择语句中的别名临时表
from #ins i full outer join #del d
编辑 - 如何将用户名设置为变量
我喜欢在过程/触发器的顶部设置变量,如下所示:
CREATE TRIGGER [dbo].[tr_Grading]
ON [dbo].[Grading]
FOR INSERT, UPDATE, DELETE
AS
DECLARE @bit INT ,
@field INT ,
@maxfield INT ,
@char INT ,
@fieldname VARCHAR(128) ,
@TableName VARCHAR(128) ,
@PKCols VARCHAR(1000) ,
@sql VARCHAR(2000),
@UpdateDate VARCHAR(21) ,
@SystemName VARCHAR(128) ,
@Type CHAR(1) ,
@PKSelect VARCHAR(1000),
@UsernameI VARCHAR(1000),
@UsernameD Varchar(1000)
-- Set Username
SET @UsernameI = (SELECT Top 1 Username FROM Inserted)
SET @UsernameD = (SELECT Top 1 Username FROM Deleted)