【问题标题】:Generic solution to compare 2 rows比较 2 行的通用解决方案
【发布时间】:2014-05-06 16:37:55
【问题描述】:

TL;DR;
有没有一种方法可以比较同一个表的 2 行而不考虑表架构但忽略特定列?


我在系统维护中不知所措,并被要求在几个表中实施审计......准确地说是 72 个表......我决定在更新和删除时使用触发器。它工作正常并且表现符合预期。
我的问题是系统很白痴,并且在没有任何更改的情况下更新表上的行,类似于“将 X 更改为 X”,除了行有一个“rowversion”列,所以最后“rowversion”是更新。我的问题是:有没有办法比较行的“插入”版本和“删除”版本,而不考虑表架构并忽略特定列(因为如果 rowversion 进入操作,行将不一样...)。这里的主要目标是一个单脚本,我只需更改表名...

【问题讨论】:

  • 我认为您最好的选择是编写一个 sql 脚本,该脚本将获取一个表格并为您编写比较脚本。然后可以将该比较粘贴到表的触发器中。
  • 是的,你可以group by字段并使用count;添加设置为count(unique_field) >=2having 子句将显示重复项。但是,表模式很重要,因为您将无法获取相同的查询并在不同的表上使用它。您要求的是 SELECT ALL EXCEPT <fieldset> FROM ... 语法,多年来我一直要求 ISO-SQL 采用该语法。

标签: sql sql-server tsql database-schema


【解决方案1】:

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

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

我已经更新了这个答案十几次了,我想我已经解决了这些错误,但我不能保证。

这样的查询可能会影响性能,我不确定我是否真的建议实施它,但创建起来很有趣。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-03-04
    • 2023-02-08
    • 1970-01-01
    • 1970-01-01
    • 2022-12-16
    • 2016-06-28
    • 1970-01-01
    相关资源
    最近更新 更多