Đề xuất làm việc, với một số dữ liệu mẫu, có thể được tìm thấy @ rextester: untable unpOLL
Ý chính của hoạt động:
1 - Sử dụng syscolumn và cho xml để tự động tạo danh sách cột của chúng tôi cho hoạt động hủy trục; tất cả các giá trị sẽ được chuyển đổi thành varchar (max), w / NULL được chuyển đổi thành chuỗi 'NULL' (địa chỉ này có vấn đề với việc bỏ qua các giá trị NULL)
2 - Tạo truy vấn động để hủy dữ liệu vào bảng tạm thời #columns
- Tại sao một bảng temp vs CTE (thông qua với khoản)? liên quan đến vấn đề hiệu suất tiềm năng đối với một khối lượng lớn dữ liệu và tự tham gia CTE mà không có sơ đồ băm / chỉ mục có thể sử dụng được; một bảng tạm thời cho phép tạo ra một chỉ mục giúp cải thiện hiệu suất khi tự tham gia [xem tự tham gia CTE chậm ]
- Dữ liệu được ghi vào #column theo thứ tự PK + ColName + UpdateDate, cho phép chúng tôi lưu trữ các giá trị PK / Colname trong các hàng liền kề; một cột danh tính ( thoát ) cho phép chúng ta tự tham gia các hàng liên tiếp này thông qua Rid = Rid + 1
3 - Thực hiện tự tham gia bảng #temp để tạo đầu ra mong muốn
Cắt-n-dán từ rextester ...
Tạo một số dữ liệu mẫu và bảng #columns của chúng tôi:
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)
);
Ruột của giải pháp:
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
;
Và kết quả:

Lưu ý: xin lỗi ... không thể tìm ra một cách dễ dàng để cắt-n-dán đầu ra rextester vào một khối mã. Tôi đang mở để đề xuất.
Các vấn đề / mối quan tâm tiềm năng:
1 - chuyển đổi dữ liệu thành một varchar chung (tối đa) có thể dẫn đến mất độ chính xác của dữ liệu, điều này có thể có nghĩa là chúng ta bỏ lỡ một số thay đổi dữ liệu; hãy xem xét các cặp datetime và float sau đây, khi được chuyển đổi / chuyển thành 'varchar (max)' chung, sẽ mất độ chính xác (nghĩa là các giá trị được chuyển đổi là như nhau):
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
Mặc dù độ chính xác của dữ liệu có thể được duy trì, nó sẽ yêu cầu mã hóa nhiều hơn một chút (ví dụ: truyền dựa trên kiểu dữ liệu cột nguồn); hiện tại tôi đã chọn gắn bó với phương sai chung (tối đa) theo khuyến nghị của OP (và giả định rằng OP biết rõ dữ liệu đủ để biết rằng chúng tôi sẽ không gặp phải bất kỳ vấn đề nào về mất độ chính xác dữ liệu).
2 - đối với các tập dữ liệu thực sự lớn, chúng tôi có nguy cơ thổi bay một số tài nguyên máy chủ, cho dù đó là không gian tempdb và / hoặc bộ nhớ cache / bộ nhớ; vấn đề chính xuất phát từ vụ nổ dữ liệu xảy ra trong một lần không xoay vòng (ví dụ: chúng tôi chuyển từ 1 hàng và 302 mẩu dữ liệu sang 300 hàng và 1200-1500 mẩu dữ liệu, bao gồm 300 bản sao của cột PK và UpdateDate, 300 tên cột)