일부 샘플 데이터가 포함 된 작업 제안서는 다음 위치에서 찾을 수 있습니다. 에서 Bigtable UNPIVOT
작업의 요지 :
1- syscolumns 및 XML 사용 에 대해 피벗 해제 작업에 대한 열 목록을 동적으로 생성하십시오. 모든 값은 varchar (max)로 변환되고 NULL은 문자열 'NULL'로 변환됩니다 (이 값은 피벗되지 않은 NULL 값을 건너 뛰는 문제를 해결 함)
2-#columns 임시 테이블로 데이터를 피벗 해제하기위한 동적 쿼리 생성
- 왜 (를 통해 CTE 대 임시 테이블 에 절)? 사용 가능한 인덱스 / 해싱 방식이없는 대량의 데이터 및 CTE 자체 조인에 대한 잠재적 인 성능 문제와 관련이 있습니다. 임시 테이블을 사용하면 자체 조인의 성능을 향상시켜야하는 인덱스를 만들 수 있습니다. [ 느린 CTE 자체 조인 참조 ].
- PK + ColName + UpdateDate 순서로 # 열에 데이터가 기록되므로 PK / Colname 값을 인접한 행에 저장할 수 있습니다. 식별 열 ( 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
;
그리고 결과 :

참고 : 사과 ...는 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의 권장 사항 (및 OP가 데이터 정밀도 손실 문제에 빠지지 않을 정도로 데이터를 잘 알고 있다고 가정)에 따라 일반적인 varchar (max)를 고수하기로 결정했습니다.
2-실제로 큰 데이터 집합의 경우 tempdb 공간 및 / 또는 캐시 / 메모리 여부에 관계없이 일부 서버 리소스가 폭발 할 위험이 있습니다. 주요 문제는 피벗 해제 중 발생하는 데이터 폭발 (예 : PK 및 UpdateDate 열 300 개, 열 이름 300 개를 포함하여 1 행 302 개 데이터에서 300 행 1, 1200-1500 개 데이터로 이동)에서 발생합니다.