I have a temporary table like so
Id |Name |Status | Rate | Method |ModifiedTime |ModifiedBy
------------------------------------------
Ok, I adapted my previous answer but on Dynamic SQL. It's a little crazy but it works (using testTable as table name you can change that just replace 'testTable'):
DECLARE @query NVARCHAR(max)
SET @query = 'select item, case item';
SELECT @query = @query + Stuff(( SELECT
' when '''+a.NAME+''' then cast(prev'+a.NAME+' as varchar) '
FROM sys.all_columns a JOIN sys.tables t ON
t.object_id = a.object_id AND t.NAME = 'testTable' AND a.NAME
NOT IN ('id',
'Name', 'ModifiedTime', 'ModifiedBy') FOR xml path('') ), 1, 0,
'');
SET @query = @query + ' end as Before, case item ';
SELECT @query = @query + Stuff(( SELECT
' when '''+a.NAME+''' then cast('+a.NAME+' as varchar) '
FROM sys.all_columns a JOIN sys.tables t ON t.object_id =
a.object_id AND t.NAME = 'testTable' AND a.NAME NOT IN (
'id',
'Name',
'ModifiedTime', 'ModifiedBy') FOR xml path('') ), 1, 1,
'');
SET @query = @query
+ ' end as After, ModifiedTime, ModifiedBy from ( select ';
SELECT @query = @query + Stuff(( SELECT a.NAME +', lag('+ a.NAME +
') over (partition by Name order by id) prev'+a.NAME+', '
FROM
sys.all_columns a JOIN sys.tables t ON t.object_id =
a.object_id
AND t.NAME = 'testTable' AND a.NAME NOT IN ('id', 'Name',
'ModifiedTime', 'ModifiedBy') FOR xml path('') ), 1, 0,
'');
SET @query = @query
+ ' ModifiedBy, ModifiedTime from testTable ) as t1 cross join ('
;
SELECT @query = @query + Stuff(( SELECT ' select '''+ a.NAME +
'''as item union all '
FROM
sys.all_columns a JOIN sys.tables t ON t.object_id =
a.object_id
AND t.NAME =
'testTable' AND a.NAME NOT IN ('id', 'Name',
'ModifiedTime',
'ModifiedBy') FOR xml path('') ), 1, 1, '');
SET @query = LEFT(@query, Len(@query) - 10); --get rid of last union all
SET @query = @query + ' ) items where ';
SELECT @query = @query + Stuff(( SELECT ' or (item = '''+ a.NAME +''' and '+
a.NAME +
' != prev'+ a.NAME +')' FROM sys.all_columns a JOIN
sys.tables
t ON t.object_id = a.object_id AND t.NAME = 'testTable'
AND
a.NAME NOT IN ('id', 'Name', 'ModifiedTime', 'ModifiedBy'
) FOR
xml
path('') ), 1, 3, '');
SET @query = @query + ' order by ModifiedTime';
EXECUTE Sp_executesql
@query
Assuming NAME (Recipe1) is a key
Declare @Table table (Id int,Name varchar(50),Status int,Rate int,Method varchar(50),ModifiedTime DateTime,ModifiedBy varchar(50))
Insert Into @Table values
(1,'Recipe1',0,30,'xyz','2016-07-26 14:55:57.977','A'),
(2,'Recipe1',0,30,'abc','2016-07-26 14:56:18.123','A'),
(3,'Recipe1',1,30,'xyz','2016-07-26 14:57:50.180','b')
Declare @XML xml
Set @XML = (Select * from @Table for XML RAW)
;with cteBase as (
Select ID = r.value('@Id','int')
,Name = r.value('@Name','varchar(150)')
,ModifiedTime = r.value('@ModifiedTime','varchar(150)')
,ModifiedBy = r.value('@ModifiedBy','varchar(150)')
,Item = Attr.value('local-name(.)','varchar(max)')
,Value = Attr.value('.','varchar(max)')
From @XML.nodes('/row') AS A(r)
Cross Apply A.r.nodes('./@*[local-name(.)!="Id"]') AS B(Attr)
)
,cteExt as (Select *,LastValue =Lag(Value) over (Partition By Name,Item Order by ModifiedTime) From cteBase)
Select Name
,Item
,Before=LastValue
,After =Value
,ModifiedTime
,ModifiedBy
From cteExt
Where Value<>LastValue and LastValue is not null
and Item not in ('ModifiedTime','ModifiedBy')
Order By Name,ModifiedTime
Returns
Name Item Before After ModifiedTime ModifiedBy
Recipe1 Method xyz abc 2016-07-26T14:56:18.123 A
Recipe1 Method abc xyz 2016-07-26T14:57:50.180 b
Recipe1 Status 0 1 2016-07-26T14:57:50.180 b