Просто еще один вариант через CROSS APPLY
Пример
Declare @YourTable Table ([ProductId] varchar(50),[OldDescription] varchar(50),[NewDescription] varchar(50),[OldTagId] int,[NewTagId] int)
Insert Into @YourTable Values
(12345,'description1','description2',1,5)
Select ProductID
,B.*
From @YourTable A
Cross Apply ( values (1,[OldDescription],[NewDescription])
,(2,left([OldTagId],25),left([NewTagId],25))
) B(ChangeID,OldVal,NewVal)
Возвращает
ProductID ChangeID OldVal NewVal
12345 1 description1 description2
12345 2 1 5
Просто для удовольствия:
Я видел комментарий из 30 столбцов. Если производительность НЕ важна, вот вариант, который будет динамически поворачивать ваши данные без фактического использования dynamici c SQL
Select *
From (
Select ProductID
,C.*
From @YourTable A
Cross Apply ( values (cast((Select A.* for XML RAW) as xml))) B(XMLData)
Cross Apply (
Select Item = left(xAttr.value('local-name(.)', 'varchar(100)'),3)+'Val'
,Value = xAttr.value('.','varchar(100)')
,ChangeID = ((row_number() over (order by (select null)) - 1 ) / 2)+1
From XMLData.nodes('//@*') xNode(xAttr)
Where xAttr.value('local-name(.)','varchar(100)') not in ('ProductID','Other','ColumnsToExclude')
) C
) src
Pivot ( max(Value) for Item in ([OldVal],[NewVal]) ) pvt