【问题标题】:How to add username column into trigger in SQL Server for audit trail如何将用户名列添加到 SQL Server 的触发器中以进行审计跟踪
【发布时间】:2022-04-17 03:06:25
【问题描述】:

我的审核触发器(如下所示)效果很好,只是我需要记录谁进行了确切的更改。正如所写的那样,系统用户字段应该这样做,但这只有在直接在数据库中进行更改时才有效。如果通过网络应用程序完成,则系统用户是网络帐户而不是个人用户。

正在修改的每个表都有一个UserName 列,该列从.NET User.Identity.Name 中提取,我想将其添加到每一行中,但是当我尝试访问inserted.UserNamedeleted.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

【问题讨论】:

  • '遇到错误'....什么错误?
  • 您能否发布您实际尝试添加用户名的触发器版本?
  • 尝试从inserted.UserName分配时得到“无法绑定多部分标识符'inserted.UserName'”
  • 这看起来非常复杂的代码,每次插入/删除/更新行时都要执行。在我看来,最好创建一个解决方案,根据系统表为每个表创建自定义触发器,以便触发器已经具有所有列名等,以便触发器中没有动态部分或系统表访问
  • 如果你只需要一张桌子,那么这当然太复杂了

标签: sql-server triggers


【解决方案1】:

查看触发器,最后的动态SQL别名插入为I,所以在选择部分尝试I.Username插入,D.Username删除。

下面是触发器中发生的事情的简要说明。

选择到临时表中

SELECT * INTO #ins FROM inserted

SELECT * INTO #del FROM deleted

在 select 语句中为临时表起别名

 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)

【讨论】:

  • 无论我尝试从代码前面的 #ins 中选择还是稍后从 i 中选择,我都会收到“无法绑定多部分标识符”错误。添加代码以将inserted.UserName 的值分配给UserName 变量的最佳位置在哪里?
  • 查看我的编辑,我为 usernameI 添加了一个变量,并将其设置为插入的用户名和删除的用户名D
  • 感谢您的帮助,这对我有用。感谢您的时间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-02
  • 2011-02-16
  • 2021-06-22
  • 2011-03-15
  • 1970-01-01
  • 2020-11-07
  • 2013-10-29
相关资源
最近更新 更多