SQL Unpivot、交叉应用、动态查询?

Posted

技术标签:

【中文标题】SQL Unpivot、交叉应用、动态查询?【英文标题】:SQL Unpivot, Cross Apply, Dynamic Query? 【发布时间】:2018-04-24 17:43:04 【问题描述】:

我正处于十字路口。有人可以帮我...送我走正确的道路。

我想比较/呈现来自 2 个数据库表的数据,如下所示:

    应用程序数据库:许多表都有将更新/删除更改(审计)复制到另一个数据库的触发器。

    审核数据库:从应用程序数据库中的触发器复制的信息

我想要做的应该是相当简单的。下面是我想要比较数据以了解所做更改的可视化。

我有一个带有CROSS APPLYUNIONS 的工作版本(它很长,并且为列、表等手动输入很糟糕)。这些列不是动态的,这使得数百行代码变得粗暴且难以管理。必须有一个更优雅的设计。请有任何想法。

我只需要从两个表中返回一个特定的行 (ID),以进行比较。

APP DB
colA   colB   colC   colD
1      hello  foo    date

APP Audit DB
colA   colB   colC   colD
1      hi     bar    date

这是要输出的内容:

colA_data    ColumnName       oldData      newData
1            colB             hi           hello
1            colC             bar          foo
1            colD             date         date

我希望我已经理解了我想要完成的事情。

我想动态读取列名(不难),然后出于报告原因将结果并排放置。显然匹配列并将它们放入行中。

非常感谢示例代码。

【问题讨论】:

【参考方案1】:

可能最简单的方法是使用UNPIVOT

1。静态版

这里只是介绍UNPIVOT的使用,这是一个简单的静态版本,应该可以解决你的问题:

declare @appDB table( [colA] int, [colB] nvarchar(50),[colC] nvarchar(50),[colD] nvarchar(50)) 
declare @auditDB table( [colA] int, [colB] nvarchar(50),[colC] nvarchar(50),[colD] nvarchar(50)) 

insert into @appDB   select 1,'hello',  'foo',    'date'
insert into @auditDB select 1,'hi',     'bar',    'date'

select old.ColA_data, old.ColumnName, old.OldData, new.NewData 
from(
    select o.colA as ColA_data, o.ColumnName, o.OldData
    from @auditDB s 
    unpivot ([OldData] for [ColumnName] in ([colB], [colC], [colD])) o
) OLD
inner join
(
    select n.colA as ColA_data, n.ColumnName, n.NewData
    from @appDB t 
    unpivot ([NewData] for [ColumnName] in ([colB], [colC], [colD])) n
) NEW
on new.ColA_data = old.ColA_data and new.ColumnName = old.ColumnName

结果:

2。动态版

现在是完整版。您可以使用动态 SQL 更改从 SQL Server INFORMATION_SCHEMA 元数据中检索它们的列。 请注意,在此示例中,我添加了一个新列 (ColE)

if OBJECT_ID('appDB') is not null drop table appDB
if OBJECT_ID('auditDB') is not null drop table auditDB
create table appDB  (colA int, colB nvarchar(50),colC nvarchar(50),colD nvarchar(50),colE nvarchar(50)) 
create table auditDB(colA int, colB nvarchar(50),colC nvarchar(50),colD nvarchar(50),colE nvarchar(50)) 

insert into appDB   select 1,'hello', 'foo', 'date','time'
insert into auditDB select 1,'hi',    'bar', 'date','time'

declare @cols nvarchar(max)='' --this variable holds all the dates that will become column names
declare @sql  nvarchar(max)='' --this variable contains the TSQL dinamically generated

select @cols = @cols +  ', [' +COLUMN_NAME + ']' 
from INFORMATION_SCHEMA.COLUMNS 
where TABLE_NAME='appDB' 
    and COLUMN_NAME <>'colA'

set @cols = RIGHT(@cols, len(@cols)-2)

set @sql= @sql + ' select old.ColA_data, old.ColumnName, old.OldData, new.NewData '
set @sql= @sql + ' from('
set @sql= @sql + '  select o.colA as ColA_data, o.ColumnName, o.OldData'
set @sql= @sql + '  from auditDB s '
set @sql= @sql + '  unpivot ([OldData] for [ColumnName] in ('+@cols+')) o'
set @sql= @sql + ' ) OLD'
set @sql= @sql + ' inner join'
set @sql= @sql + ' ('
set @sql= @sql + '  select n.colA as ColA_data, n.ColumnName, n.NewData'
set @sql= @sql + '  from appDB t '
set @sql= @sql + '  unpivot ([NewData] for [ColumnName] in ('+@cols+')) n'
set @sql= @sql + ' ) NEW'
set @sql= @sql + ' on new.ColA_data = old.ColA_data and new.ColumnName = old.ColumnName'

exec(@sql)

结果:

【讨论】:

以上是关于SQL Unpivot、交叉应用、动态查询?的主要内容,如果未能解决你的问题,请参考以下文章

PL/SQL Oracle :- 在传递值时动态 UNPIVOT ORACLE TABLE

动态 SQL 使用多列交叉应用来反透视数据

UNPIVOT 表列

如何在SQL中产生交叉式数据表(枢纽分析表)Part 2(PIVOT,UNPIVOT)

交叉应用不高效,转换为 unpivot(或其他)?

交叉应用与 UNPIVOT