I have a temporary table like so
Id |Name |Status | Rate | Method |ModifiedTime |ModifiedBy
-----------------------------------------------------------------------------
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
I would like to select only the changes and wanted to show what the value was previously and what it is currently accompanied by who changed it. The final outcome will be as follows. I am using SQL Server 2014.
Item | Before | After |ModifiedTime | ModifiedBy
-----------------------------------------------------------------------------
Method | xyz | Abc | 2016-07-26 14:56:18.123 | A
-------------------------------------------------------------------------------
Status | 0 | 1 | 2016-07-26 14:57:50.180 | b
--------------------------------------------------------------------------------
Method | Abc | xyz | 2016-07-26 14:57:50.180 | b
I would like to do this dynamically instead of specifying each column name individually as shown in this link
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
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
来源:https://stackoverflow.com/questions/38617690/dynamically-select-distinct-current-and-previous-columns-from-a-sql-table