Dynamically select distinct current and previous columns from a sql table

自作多情 提交于 2019-12-01 08:29:16

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 
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!