UnPivot, безусловно, более производительный, но вот один из подходов, который будет динамически отключать практически любую таблицу, запрос или запись БЕЗ , фактически использующих динамический SQL
Пример
Declare @YourTable Table (Date date,cost1 int,cost2 int,cost3 int)
Insert Into @YourTable Values
('6/30/2017',3,4,5),
('6/24/2017',3,4,5),
('6/22/2017',3,4,5)
Select C.*
From @YourTable A
Cross Apply ( values (cast((Select A.* for XML RAW) as xml))) B(XMLData)
Cross Apply (
Select Item = a.value('local-name(.)','varchar(100)')
,Value = a.value('.','varchar(max)')
From B.XMLData.nodes('/row') as C1(n)
Cross Apply C1.n.nodes('./@*') as C2(a)
Where a.value('local-name(.)','varchar(100)') not in ('Column1','Column2')
) C
Возвращает
Item Value
Date 2017-06-30
cost1 3
cost2 4
cost3 5
Date 2017-06-24
cost1 3
cost2 4
cost3 5
Date 2017-06-22
cost1 3
cost2 4
cost3 5
Теперь, с небольшим поворотом
Select A.Date
,C.*
From @YourTable A
Cross Apply ( values (cast((Select A.* for XML RAW) as xml))) B(XMLData)
Cross Apply (
Select Item = a.value('local-name(.)','varchar(100)')
,Value = a.value('.','varchar(max)')
From B.XMLData.nodes('/row') as C1(n)
Cross Apply C1.n.nodes('./@*') as C2(a)
Where a.value('local-name(.)','varchar(100)') not in ('Date','Column2')
) C
Returns
Date Item Value
2017-06-30 cost1 3
2017-06-30 cost2 4
2017-06-30 cost3 5
2017-06-24 cost1 3
2017-06-24 cost2 4
2017-06-24 cost3 5
2017-06-22 cost1 3
2017-06-22 cost2 4
2017-06-22 cost3 5