【问题标题】:Reading inserted column names and values in a TSQL trigger在 TSQL 触发器中读取插入的列名和值
【发布时间】:2010-07-09 14:35:53
【问题描述】:

我被要求为数据库中的每个表创建历史表。然后创建一个触发器,该触发器将在主表更新时写入历史表。

历史表的结构与主表相同,但多了几行('id' 和 'update type')

我以前从未对触发器做过任何事情,但我想做的是动态遍历“已插入”中的列并构造一个插入语句来填充历史记录表。

但是我不知道如何读取列的名称及其各自的值。

我的半成品触发器目前看起来像......

CREATE TRIGGER tr_address_history
ON address
FOR UPDATE
AS

DECLARE @colCount int
DECLARE @maxCols int
SET @colCount = 0
SET @maxCols = (SELECT COUNT(column_name) FROM INFORMATION_SCHEMA.columns WHERE TABLE_NAME = 'Inserted')

PRINT 'Number of columns = ' + CONVERT(varChar(10),@maxCols)
WHILE (@colCount <= @maxCols)
BEGIN
    DECLARE @name varchar(255)
    SELECT @name = column_name FROM INFORMATION_SCHEMA.columns WHERE TABLE_NAME = 'Inserted'
    DECLARE @value varchar(255)
    SELECT @value = @name FROM Inserted

    PRINT 'name = ' + @name + ' and value = ' + @value
    SET @colCount = @colCount + 1
END
PRINT 'Done';

当触发器运行时,它只会显示“列数 = 0”

谁能告诉我有什么问题:

SELECT COUNT(column_name) FROM INFORMATION_SCHEMA.columns WHERE TABLE_NAME = 'Inserted'

谢谢...

【问题讨论】:

    标签: tsql triggers


    【解决方案1】:

    Beenay25 提出的第一个解决方案很好,但您应该使用受影响的表而不是“插入”伪表。

    这是:

    SELECT @name = column_name FROM INFORMATION_SCHEMA.columns WHERE TABLE_NAME = 'AFFECTED_TABLE'
    

    而不是“插入”

    另外,您应该使用动态 SQL。

    这将是一个完整的工作解决方案:

    ALTER TRIGGER [dbo].[tr_address_history]
    ON [dbo].[address]
    AFTER Insert
    AS
    
    DECLARE @ColumnName nvarchar(500)
    DECLARE @TableName nvarchar(500)
    DECLARE @value nvarchar(500)
    DECLARE @Sql nvarchar(500)
    
    Set @TableName='address'
    
    DECLARE ColumnsCursor CURSOR FOR
    select column_name FROM INFORMATION_SCHEMA.columns WHERE TABLE_NAME = 'address'
    
    OPEN ColumnsCursor
    FETCH NEXT FROM ColumnsCursor into @ColumnName
    
    WHILE @@FETCH_STATUS=0
    BEGIN 
    
          select * into #tmp from inserted
          Set @Sql= 'SELECT @value =' + @ColumnName + ' FROM #tmp'
    
          EXEC sp_executesql @Sql, N'@Value nvarchar(500) OUTPUT', @Value OUTPUT
    
          DROP TABLE #TMP
    
          print '[' + @ColumnName +'='+ ltrim(rtrim(@Value))+']'
    
          FETCH NEXT FROM ColumnsCursor into @ColumnName
    END   
    
    CLOSE ColumnsCursor
    DEALLOCATE ColumnsCursor
    

    【讨论】:

      【解决方案2】:

      'inserted' 表是一个伪表;它没有出现在 INFORMATION_SCHEMA 中。

      UPDATE() 运算符用于触发器:

      CREATE TRIGGER trigger_name ON tablename
      FOR UPDATE
      AS
      SET NOCOUNT ON
      IF (UPDATE(Column1) OR UPDATE(Column2))
      BEGIN
        your sql here
      END
      

      COLUMNS_UPDATED

      UPDATE()

      【讨论】:

      • 嗨,Mitch,谢谢你,但我不知道在触发器运行之前列名是什么......这就是为什么我试图从 INFORMATION_SCHEMA 中获取信息的原因必须在六十张桌子上触发。我不想硬编码每一列的名称:(我只想重用相同的代码(好吧,显然有不同的表名)
      【解决方案3】:

      有一种方法可以满足提问者的要求:

      我在触发器中做了一些事情,测试特定表的所有列是否实际参与了对该表的插入。如果他们这样做了,我后来将它们复制到历史表中。如果没有,则回滚并仅打印完整的行可能会插入到报告表中。也许他们可以根据自己的需要进行调整:

      这里是:

          [
      
      if exists (select 1 from inserted) and not exists (select 1 from deleted) -- if an insert has been performed
      begin -- and we want to test whether all the columns in the report table were included in the insert
      declare @inserted_columncount int, @actual_num_of_columns int, @loop_columns int, @current_columnname nvarchar(300),
          @sql_test nvarchar(max), @params nvarchar(max), @is_there bit
      set @actual_num_of_columns = (
          select count(*) from (
          select COLUMN_NAME
          from INFORMATION_SCHEMA.COLUMNS
          where TABLE_NAME = 'renameFilesFromTable_report') as z)
      set @inserted_columncount = 0
      set @loop_columns = 1
      declare inserted_columnnames cursor scroll for -- these are not really the inserted ones, but we are going to test them 1 by 1
          select COLUMN_NAME
          from INFORMATION_SCHEMA.COLUMNS
          where TABLE_NAME = 'renameFilesFromTable_report'
      set @params = '@is_there_in bit output'
      open inserted_columnnames
      fetch next from inserted_columnnames into @current_columnname
      select * into #temp_for_dynamic_sql from inserted -- this is necessary because the scope of sp_executesql does not include inserted pseudo table
      while (@loop_columns <= @actual_num_of_columns) -- looping with independent integer arithmetic
      begin
      set @sql_test = '
      set @is_there_in = 0
      if exists (select ['+@current_columnname+'] from #temp_for_dynamic_sql where ['+@current_columnname+'] is not null)
      set @is_there_in = 1'
      exec sp_executesql @sql_test, @params, @is_there output
      if @is_there = 1
      begin
      fetch next from inserted_columnnames into @current_columnname
      set @inserted_columncount = @inserted_columncount + 1
      set @loop_columns = @loop_columns + 1
      end
      else if @is_there <> 1
      begin
      fetch next from inserted_columnnames into @current_columnname
      set @loop_columns = @loop_columns + 1
      end
      end 
      close inserted_columnnames
      deallocate inserted_columnnames
      -- at this point we hold in two int variables the number of columns participating in the insert and the total number of columns
      
          ]
      

      如果@inserted_columncount

      我这样做是因为我有一个 sp 每次运行时都会在报表表中插入 1 个完整的行。没关系,但我不希望其他人误触那张桌子。连我自己都没有。我也想保留历史。所以我做了这个触发器来保留历史记录,但也检查是否尝试插入而没有报告表中所有列的值,并进一步检查代码是否尝试更新或删除并回滚。

      我正在考虑扩展它以允许更新,但其中所有列都已设置。 这可以按如下方式完成:

      如果尝试更新,

      and exists (
      select possibly_excluded.COLUMN_NAME from (
      select COLUMN_NAME
      from INFORMATION_SCHEMA.COLUMNS
      where TABLE_NAME = 'renameFilesFromTable_report') as possibly_excluded
      group by possibly_excluded.COLUMN_NAME
      having COLUMN_NAME not in (
      select COLUMN_NAME
      from INFORMATION_SCHEMA.COLUMNS
      where TABLE_NAME = 'renameFilesFromTable_report' and
      sys.fn_IsBitSetInBitmask(@ColumnsUpdated, COLUMNPROPERTY(OBJECT_ID(TABLE_SCHEMA + '.' + TABLE_NAME), COLUMN_NAME, 'ColumnID')) <> 0)
      )
      begin
      rollback transaction
      print 'Only updates that set the values for a complete row are allowed on the report table..'
      end
      

      【讨论】:

        猜你喜欢
        • 2016-09-13
        • 1970-01-01
        • 1970-01-01
        • 2011-01-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-03-30
        • 1970-01-01
        相关资源
        最近更新 更多