使用基于集合的逻辑替换while循环的替代方法

时间:2018-09-19 14:25:54

标签: sql sql-server tsql

我有2个表-一个表具有最新记录(#Director表),另一个表(#DirectorAudit)具有记录发生更改的记录。 AuditId是完成的更改的顺序

需要具有以下输出-

https://i.stack.imgur.com/jN8aN.png

获得此输出的最佳方法是什么。

请在下面找到当前代码以获取输出,但我对此不满意。我们可以替换while循环(没有游标plz)或其他逻辑的任何替代解决方案。

我们非常感谢您的帮助。非常感谢。

SET NOCOUNT ON;

        IF OBJECT_id('tempdb..#a') IS NOT NULL                DROP TABLE #a
        IF OBJECT_id('tempdb..#b') IS NOT NULL                DROP TABLE #b
        IF OBJECT_id('tempdb..#c') IS NOT NULL                DROP TABLE #c
        IF OBJECT_id('tempdb..#d') IS NOT NULL                DROP TABLE #d

        IF OBJECT_id('tempdb..#DirectorAudit') IS NOT NULL DROP TABLE #DirectorAudit
        IF OBJECT_id('tempdb..#Director') IS NOT NULL DROP TABLE #Director

        Create table #DirectorAudit(AuditId TINYINT IDENTITY(1,1),DirectorID INT,[Name] varchar(200),Code varchar(5))
        INSERT INTO  #DirectorAudit(DirectorID,[Name],Code)
        SELECT  541,'Steven','A'
        UNION
        SELECT  541,'Roger','A'
        UNION
        SELECT  541,'Mathew','A'
        UNION
        SELECT  541,'Mathew','I'

        Create table #Director(DirectorID INT,[Name] varchar(200),Code varchar(5))
        INSERT INTO  #Director(DirectorID,[Name],Code)
        SELECT 541,'David','A'


        CREATE TABLE #d (DirectorID INT ,FieldName VARCHAR(100), OldValue VARCHAR(4000), NewValue VARCHAR(4000), AuditID int)
        CREATE TABLE #b (DirectorID INT,  Oldvalue VARCHAR(4000),oldCode VARCHAR(200),AuditId INT)
        CREATE TABLE #c (DirectorID INT,  NewValue VARCHAR(4000),oldCode VARCHAR(200),AuditId INT)


        DECLARE  @oldValue VARCHAR(200),
                 @Code VARCHAR(200),
                 @newValue VARCHAR(200),
                 @newCode     VARCHAR(200),    
                 @DirectorID INT = 541

        SELECT  Row_Number() over(order by AuditId asc) as ID,
                a.DirectorID,
                a.Name as NewName, 
                b.Name as OldName,
                a.Code as NewCode, 
                b.Code as oldCode,
                b.AuditID

        INTO     #a
        FROM     #Director a 
                 INNER JOIN #DirectorAudit  b ON a.DirectorID = b.DirectorID 

        WHERE     b.DirectorID = @DirectorID
        order by b.AuditId


         DECLARE @count INT =1 
         DECLARE @init INT = 1 
         DECLARE @tmp_AuditId INT 
         DECLARE @tmp_NextAuditID INT 

         SELECT @count = COUNT(*) FROM #a 

         WHILE @count >= @init
         BEGIN
            SELECT  @tmp_AuditId = AuditId from #a WHERE Id = @init
            select  @tmp_NextAuditID = AuditId from #a WHERE Id = @init+1

            IF @count =1 SET @tmp_NextAuditID = @tmp_AuditId

            SELECT @OldValue = OldName,
                 @Code = oldCode

            from #a 
            where AuditID = @tmp_AuditId

            select  @newValue = OldName,
                    @newCode = oldCode

            from #a 
            where AuditID = @tmp_NextAuditID

            INSERT INTO #b(DirectorID,  OldValue,oldCode, AuditID)
            select            DirectorID, @OldValue,@Code,  AuditID
            FROM    #a
            WHERE AuditId = @tmp_AuditId 

            IF @count <> @init 
            BEGIN
                INSERT INTO #c(DirectorID, NewValue,oldCode, AuditID)
                select            DirectorID, @newValue,@newCode, @tmp_AuditId
                FROM    #a
                WHERE AuditId = @tmp_NextAuditID 
            END
            ELSE
            BEGIN
                INSERT INTO #c(DirectorID, NewValue, oldCode,AuditID)
                select            E.DirectorID, e.Name,e.Code, a.AuditID
                FROM    #Director e 
                        INNER JOIN #a  a            ON    e.DirectorID = a.DirectorID
                WHERE AuditId = @tmp_NextAuditID 
            END    

            SET @init = @init + 1 
         END 

        /*
         select * from #a
         SELECT * FROM #b
         SELECT * FROM #c
        */

        INSERT INTO #d(DirectorID,FieldName, OldValue, NewValue, AuditID)
        SELECT DirectorID,FieldName, OldValue, NewValue, AuditID
        FROM (
                SELECT c.DirectorID,'Name' as FieldName, c.OldValue as OldValue, d.NewValue as NewValue, c.AuditID
                FROM    #b as c
                        inner join #c  as d ON  c.AuditId = d.AuditId
                UNION 
                SELECT c.DirectorID,'Code' as FieldName, c.oldCode as OldValue, d.oldCode as NewValue, c.AuditID
                FROM    #b as c
                        inner join #c  as d ON  c.AuditId = d.AuditId 


            )x 
        order by x.AuditID 

        select a.DirectorID,FieldName, OldValue, NewValue
        from #d a 
        where OLdValue <> NewValue 
        order by a.FieldName desc 

1 个答案:

答案 0 :(得分:3)

这里是您仅需提及关键字段的一种方法。在这种情况下,AutidID和DirectorID

交叉应用B会将记录转换为XML

Cross Apply C将动态取消透视数据(不包括指定的列)

示例

;with cte as (
Select A.AuditID
      ,A.DirectorID
      ,C.*
      ,NewValue      = lag(OldValue,1,OldValue) over (Partition By DirectorID,Field Order by case when AuditID is null then 0 else 1 end,AuditID desc)
 From (
        Select * from #DirectorAudit
        Union All
        Select null,* from #Director
      ) A
 Cross Apply ( values (cast((Select A.* for XML RAW) as xml))) B(XMLData)
 Cross Apply (
                Select Field = a.value('local-name(.)','varchar(100)')
                      ,OldValue = 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 ('AuditId','DirectorID','OtherColumnsToExclude')
             ) C
)
Select * 
 From  cte 
 Where OldValue<>NewValue
 Order by Field,AuditID

返回

AuditID DirectorID  Field   OldValue    NewValue
1       541         Code    A           I
2       541         Code    I           A
2       541         Name    Mathew      Roger
3       541         Name    Roger       Steven
4       541         Name    Steven      David
  

编辑-要求替代

;with cte as (
Select A.AuditID
      ,A.DirectorID
      ,B.*
      ,NewValue      = lag(OldValue,1,OldValue) over (Partition By DirectorID,Field Order by case when AuditID is null then 0 else 1 end,AuditID desc)
 From (
        Select * from #DirectorAudit
        Union All
        Select null,* from #Director
      ) A
 Cross Apply ( values ('Name',Name)
                     ,('Code',Code)
             ) B(Field,OldValue)
)
Select * 
 From  cte 
 Where OldValue<>NewValue
 Order by Field,AuditID