比较2行的通用解决方案

时间:2014-05-06 16:37:55

标签: sql sql-server tsql database-schema

TL; DR;
有没有办法比较同一个表的两行,无论表模式如何,但忽略了一个特定的列?


我在一个系统维护中突然失控,被要求在几个表中实施审计......确切地说是72个表...我决定在更新和删除时使用触发器。它工作正常,表现得像预期的那样 我的问题是系统非常愚蠢,并且在没有任何更改的情况下更新表格上的行,并且为#X和#34;更改X,除了行具有" rowversion"列,所以,最后" rowversion"已更新。我的问题是:有没有办法比较"插入"行的版本和"删除"版本,无论表模式和忽略特定列(因为如果rowversion进入操作,行将不会相同...)。这里的主要目标是单脚本,我只需更改表名...

1 个答案:

答案 0 :(得分:2)

这是一个我刚刚编写的脚本,它将创建一个可以从触发器内部调用以获得比较查询的函数。然后,您可以运行该查询并将结果插入到临时表中,并根据行是否更改来执行您需要执行的操作。

create function GetChangedRowsQuery(
    @TableName              varchar(50), 
    @PrimaryKeyColumnName   varchar(50),
    @RowVersionColumnName   varchar(50) = ''
)
returns varchar(max)
as
begin

    declare 
        @ColumnName varchar(50),
        @GetChangedRowsQuery varchar(max)

    select @GetChangedRowsQuery = 
    'select isnull(a.' + @PrimaryKeyColumnName + ', b.' + + @PrimaryKeyColumnName + ') 
       from #inserted a
       full join #deleted b on a.' + @PrimaryKeyColumnName + ' = b.' + @PrimaryKeyColumnName + '
      where '

    declare ColumnCursor cursor Read_Only
    for select Name
          from Sys.columns
         where object_id = Object_Id('Member')

    open ColumnCursor
    fetch next from ColumnCursor into @ColumnName
    while @@FETCH_STATUS = 0
      begin

        if (@ColumnName != @PrimaryKeyColumnName and @ColumnName != @RowVersionColumnName)
          begin
            select @GetChangedRowsQuery = @GetChangedRowsQuery + '((a.' + @ColumnName + ' != b.' + @ColumnName + ' or a.' + @ColumnName + ' is null or b.' + @ColumnName + ' is null) and (a.' + @ColumnName + ' is not null or b.' + @ColumnName + ' is not null))' + char(13) + '      or ' 
          end
        fetch next from ColumnCursor into @ColumnName
      end

    close ColumnCursor
    deallocate ColumnCursor

    select @GetChangedRowsQuery = substring(@GetChangedRowsQuery, 0, len(@GetChangedRowsQuery) -7)

    return @GetChangedRowsQuery
end

然后你可以创建你的触发器来看起来像这样

create trigger TestTrigger on Member for Insert, Update, Delete
as
begin

    //We can't access the inserted or deleted tables from inside the exec statement
    //Dump that data into temp tables so you we can
    select *
      into #Inserted
      from Inserted

    select *
      into #Deleted
      from Deleted

    declare @GetChangedRowsQuery varchar(max)

    select @GetChangedRowsQuery = dbo.GetChangedRowsQuery('Member', 'MemberId', '')

    select @GetChangedRowsQuery

    create table #Temp (PrimaryKey int)

    insert into #Temp (PrimaryKey)
    exec (@GetChangedRowsQuery) 

    //Do what you need to do with the rows that have or haven't been updated

    drop table #Temp

end
go

我现在已经更新了这个答案了十几次,而且我认为我已经解决了这个问题,但我不保证。

像这样的查询可能会破坏性能,我不确定我是否真的建议实现它,但创建它很有趣。