Reputation: 23
I have 2 tables - one having the latest records (#Director table) and other table ( #DirectorAudit ) which has records where changes has taken place. The AuditId is the sequence of changes done
Need to have its output as follows -
Whats the best possible way to get this output.
Please find below the current code to get output but i am not happy with it. Any alternative solution where we can replace while loop (no cursors plz) or some other logic.
Any help is highly appreciated. Thank you very much.
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
Upvotes: 1
Views: 208
Reputation: 82010
Here is one approach where you only have to mention the key fields. In this case AutidID and DirectorID
Cross Apply B will convert the record into XML
Cross Apply C will dynamically unpivot your data (excluding specified columns)
Example
;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
Returns
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
EDIT - REQUESTED Alternate
;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
Upvotes: 3