いくつかのサンプルデータを含むワーキングプロポーザルは、rextesterで見つけることができます:bigtable unpivot
操作の要点:
1- syscolumnsおよびxmlを使用して、ピボット解除操作用の列リストを動的に生成します。すべての値はvarchar(max)に変換され、w / nullは文字列 'NULL'に変換されます(これはNULL値をスキップするアンピボットの問題に対処します)
2-#columns一時テーブルにデータをピボット解除する動的クエリを生成します
- 一時テーブルとCTEの組み合わせ(with with句を使用)が必要な理由 大量のデータの潜在的なパフォーマンスの問題と、使用可能なインデックス/ハッシュスキームのないCTEの自己結合に関する問題。一時テーブルは、自己結合のパフォーマンスを改善するインデックスの作成を可能にします[ 遅いCTE自己結合を参照 ]
- データはPK + ColName + UpdateDateの順序で#columnsに書き込まれるため、PK / Colname値を隣接する行に格納できます。ID列(rid)を使用すると、rid = rid + 1を介してこれらの連続した行を自己結合できます。
3-#tempテーブルの自己結合を実行して、目的の出力を生成します
rextesterからカットアンドペースト...
サンプルデータと#columnsテーブルを作成します。
CREATE TABLE dbo.bigtable
(UpdateDate datetime not null
,PK varchar(12) not null
,col1 varchar(100) null
,col2 int null
,col3 varchar(20) null
,col4 datetime null
,col5 char(20) null
,PRIMARY KEY (PK)
);
CREATE TABLE dbo.bigtable_archive
(UpdateDate datetime not null
,PK varchar(12) not null
,col1 varchar(100) null
,col2 int null
,col3 varchar(20) null
,col4 datetime null
,col5 char(20) null
,PRIMARY KEY (PK, UpdateDate)
);
insert into dbo.bigtable values ('20170512', 'ABC', NULL, 6, 'C1', '20161223', 'closed')
insert into dbo.bigtable_archive values ('20170427', 'ABC', NULL, 6, 'C1', '20160820', 'open')
insert into dbo.bigtable_archive values ('20170315', 'ABC', NULL, 5, 'C1', '20160820', 'open')
insert into dbo.bigtable_archive values ('20170212', 'ABC', 'C1', 1, 'C1', '20160820', 'open')
insert into dbo.bigtable_archive values ('20170109', 'ABC', 'C1', 1, 'C1', '20160513', 'open')
insert into dbo.bigtable values ('20170526', 'XYZ', 'sue', 23, 'C1', '20161223', 're-open')
insert into dbo.bigtable_archive values ('20170401', 'XYZ', 'max', 12, 'C1', '20160825', 'cancel')
insert into dbo.bigtable_archive values ('20170307', 'XYZ', 'bob', 12, 'C1', '20160825', 'cancel')
insert into dbo.bigtable_archive values ('20170223', 'XYZ', 'bob', 12, 'C1', '20160820', 'open')
insert into dbo.bigtable_archive values ('20170214', 'XYZ', 'bob', 12, 'C1', '20160513', 'open')
;
create table #columns
(rid int identity(1,1)
,PK varchar(12) not null
,UpdateDate datetime not null
,ColName varchar(128) not null
,ColValue varchar(max) null
,PRIMARY KEY (rid, PK, UpdateDate, ColName)
);
ソリューションの根幹:
declare @columns_max varchar(max),
@columns_raw varchar(max),
@cmd varchar(max)
select @columns_max = stuff((select ',isnull(convert(varchar(max),'+name+'),''NULL'') as '+name
from syscolumns
where id = object_id('dbo.bigtable')
and name not in ('PK','UpdateDate')
order by name
for xml path(''))
,1,1,''),
@columns_raw = stuff((select ','+name
from syscolumns
where id = object_id('dbo.bigtable')
and name not in ('PK','UpdateDate')
order by name
for xml path(''))
,1,1,'')
select @cmd = '
insert #columns (PK, UpdateDate, ColName, ColValue)
select PK,UpdateDate,ColName,ColValue
from
(select PK,UpdateDate,'+@columns_max+' from bigtable
union all
select PK,UpdateDate,'+@columns_max+' from bigtable_archive
) p
unpivot
(ColValue for ColName in ('+@columns_raw+')
) as unpvt
order by PK, ColName, UpdateDate'
--select @cmd
execute(@cmd)
--select * from #columns order by rid
;
select c2.PK, c2.UpdateDate, c2.ColName as ColumnName, c1.ColValue as 'Old Value', c2.ColValue as 'New Value'
from #columns c1,
#columns c2
where c2.rid = c1.rid + 1
and c2.PK = c1.PK
and c2.ColName = c1.ColName
and isnull(c2.ColValue,'xxx') != isnull(c1.ColValue,'xxx')
order by c2.UpdateDate, c2.PK, c2.ColName
;
そして結果:
注:おpびします... rextesterの出力をコードブロックにカットアンドペーストする簡単な方法がわかりませんでした。私は提案を受け入れます。
潜在的な問題/懸念:
1-データを一般的なvarchar(max)に変換すると、データの精度が失われる可能性があり、これにより一部のデータ変更が失われる可能性があります。ジェネリックの 'varchar(max)'に変換/キャストされたときに精度が失われる(つまり、変換された値が同じである)次の日時と浮動小数点のペアを考慮してください。
original value varchar(max)
------------------- -------------------
06/10/2017 10:27:15 Jun 10 2017 10:27AM
06/10/2017 10:27:18 Jun 10 2017 10:27AM
234.23844444 234.238
234.23855555 234.238
29333488.888 2.93335e+007
29333499.999 2.93335e+007
データの精度は維持できますが、もう少しコーディングが必要になります(たとえば、ソース列のデータ型に基づいたキャスト)。今のところ、OPの推奨事項に従って、ジェネリックvarchar(max)に固執することを選択しました(そして、OPはデータ精度の損失の問題に遭遇しないことを知るのに十分にデータを知っているという仮定)。
2-大量のデータセットの場合、tempdbスペースやキャッシュ/メモリなど、サーバーリソースを使い果たすリスクがあります。主な問題は、アンピボット中に発生するデータの爆発によるものです(たとえば、1行と302個のデータから300行と1200-1500個のデータに移動します(PKおよびUpdateDate列の300コピー、300列名を含む)