ข้อเสนอการทำงานพร้อมด้วยข้อมูลตัวอย่างบางส่วนสามารถพบได้ที่ @ rextester: bigtable unpivot
ส่วนสำคัญของการดำเนินการ:
1 - ใช้syscolumnsและสำหรับ xmlเพื่อสร้างรายการคอลัมน์ของเราแบบไดนามิกสำหรับการดำเนินการยกเลิกการเปลี่ยนแปลง ค่าทั้งหมดจะถูกแปลงเป็น varchar (สูงสุด), w / NULL ที่ถูกแปลงเป็นสตริง 'NULL' (ปัญหานี้อยู่ด้วยการยกเลิกการข้ามค่า NULL)
2 - สร้างคิวรีแบบไดนามิกเพื่อยกเลิกการโอนข้อมูลลงในตาราง #columns temp
- ทำไมตารางเทมเพลตเทียบกับ CTE (ผ่านด้วยประโยค)? เกี่ยวข้องกับปัญหาประสิทธิภาพที่อาจเกิดขึ้นสำหรับข้อมูลจำนวนมากและการเข้าร่วม CTE ด้วยตนเองโดยไม่มีดัชนี / hashing ที่ใช้งานได้ ตาราง temp ช่วยให้สามารถสร้างดัชนีซึ่งควรปรับปรุงประสิทธิภาพในการเข้าร่วมด้วยตนเอง [ดูการเข้าร่วม CTE แบบช้าด้วยตนเอง ]
- ข้อมูลถูกเขียนไปยัง #columns ในลำดับ PK + ColName + UpdateDate ทำให้เราสามารถจัดเก็บค่า PK / Colname ในแถวที่อยู่ติดกัน คอลัมน์ข้อมูลประจำตัว ( กำจัด ) ช่วยให้เราสามารถเข้าร่วมแถวต่อเนื่องเหล่านี้ด้วยตนเองผ่าน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
;
และผลลัพธ์:
หมายเหตุ: ขออภัย ... ไม่สามารถหาวิธีที่ง่ายในการตัด -n-paste เอาต์พุต rextester ลงในบล็อกโค้ด ฉันเปิดรับข้อเสนอแนะ
ปัญหา / ข้อกังวลที่อาจเกิดขึ้น:
1 - การแปลงข้อมูลเป็น varchar ทั่วไป (สูงสุด) สามารถนำไปสู่การสูญเสียความแม่นยำของข้อมูลซึ่งหมายความว่าเราอาจพลาดการเปลี่ยนแปลงข้อมูลบางอย่าง พิจารณาวันที่และเวลาและคู่ลอยที่เมื่อแปลง / ส่งไปยัง 'varchar (สูงสุด)' ทั่วไปจะสูญเสียความแม่นยำ (เช่นค่าที่แปลงแล้วจะเท่ากัน):
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
ในขณะที่ความแม่นยำของข้อมูลสามารถรักษาได้นั้นจะต้องใช้การเข้ารหัสอีกเล็กน้อย (เช่นการคัดเลือกจากฐานข้อมูลคอลัมน์แหล่งข้อมูล) สำหรับตอนนี้ฉันเลือกที่จะใช้ varchar ทั่วไป (สูงสุด) ตามคำแนะนำของ OP (และสมมติว่า OP รู้ข้อมูลดีพอที่จะรู้ว่าเราจะไม่พบปัญหาการสูญเสียความแม่นยำของข้อมูล)
2 - สำหรับชุดข้อมูลขนาดใหญ่จริง ๆ เราเสี่ยงต่อการระเบิดทรัพยากรเซิร์ฟเวอร์ไม่ว่าจะเป็นพื้นที่ tempdb และ / หรือแคช / หน่วยความจำ ปัญหาหลักมาจากการระเบิดของข้อมูลที่เกิดขึ้นในช่วง unpivot (เช่นเราไปจาก 1 แถวและ 302 ชิ้นส่วนของข้อมูลเป็น 300 แถวและ 1200-1500 ชิ้นส่วนของข้อมูลรวมถึง 300 สำเนาของคอลัมน์ PK และ UpdateDate ชื่อคอลัมน์ 300 รายการ)