Compare two sets of XML data using XQuery in SQL S

2019-02-09 04:56发布

问题:

Suppose I store employee data in a xml column in my log table. Sometimes data is also updated in the xml column from a stored procedure.

Here is the sample example

DECLARE @XML1 XML
DECLARE @XML2 XML

SET @XML1 = 
'<NewDataSet> 
<Employee>
<EmpID>1005</EmpID>
<Name> keith </Name>
<DOB>12/02/1981</DOB>
<DeptID>ACC001</DeptID>
<Salary>10,500</Salary>
</Employee>
</NewDataSet>'

SET @XML2 = 
'<NewDataSet> 
<Employee>
<EmpID>1006</EmpID>
<Name> keith </Name>
<DOB>05/02/1981</DOB>
<DeptID>ACC002</DeptID>
<Salary>10,900</Salary>
</Employee>
</NewDataSet>'

There is some difference in two the xml data which I need to show like old value & new value as a output of sql

Old Value             New Value
---------             ---------
1005                  1006
12/02/1981            05/02/1981
ACC001                ACC002
10,500                10,900

I just need to show the difference like above. So please guide me how to compare two xml data using XQuery and show the difference only in the above fashion in SQL Server. Please guide me with code snippet. thanks

回答1:

;with XML1 as
(
  select T.N.value('local-name(.)', 'nvarchar(100)') as NodeName,
         T.N.value('.', 'nvarchar(100)') as Value
  from @XML1.nodes('/NewDataSet/Employee/*') as T(N)
),
XML2 as
(
  select T.N.value('local-name(.)', 'nvarchar(100)') as NodeName,
         T.N.value('.', 'nvarchar(100)') as Value
  from @XML2.nodes('/NewDataSet/Employee/*') as T(N)
)
select coalesce(XML1.NodeName, XML2.NodeName) as NodeName, 
       XML1.Value as Value1, 
       XML2.Value as Value2
from XML1
  full outer join XML2
    on XML1.NodeName = XML2.NodeName
where coalesce(XML1.Value, '') <> coalesce(XML2.Value, '')    

Result:

NodeName             Value1               Value2
-------------------- -------------------- --------------------
EmpID                1005                 1006
DOB                  12/02/1981           05/02/1981
DeptID               ACC001               ACC002
Salary               10,500               10,900


回答2:

I don't have the exact output you wanted - but at least you get a good comparison of old and new values:

;WITH OldData AS
(
SELECT 
    @XML1.value('(/NewDataSet/Employee/EmpID)[1]', 'int') AS 'EmpID',
    @XML1.value('(/NewDataSet/Employee/Name)[1]', 'varchar(50)') AS 'Name',
    @XML1.value('(/NewDataSet/Employee/DOB)[1]', 'datetime') AS 'DOB',
    @XML1.value('(/NewDataSet/Employee/DeptID)[1]', 'varchar(50)') AS 'DeptID',
    @XML1.value('(/NewDataSet/Employee/Salary)[1]', 'varchar(25)') AS 'Salary'
),
NewData AS
(
SELECT 
    @XML2.value('(/NewDataSet/Employee/EmpID)[1]', 'int') AS 'EmpID',
    @XML2.value('(/NewDataSet/Employee/Name)[1]', 'varchar(50)') AS 'Name',
    @XML2.value('(/NewDataSet/Employee/DOB)[1]', 'datetime') AS 'DOB',
    @XML2.value('(/NewDataSet/Employee/DeptID)[1]', 'varchar(50)') AS 'DeptID',
    @XML2.value('(/NewDataSet/Employee/Salary)[1]', 'varchar(25)') AS 'Salary'
)
SELECT
    'Old values', od.*
FROM OldData od
UNION
SELECT 'New values', nd.*
FROM NewData nd

Gives you an output of:

            EmpID  Name   DOB                       DeptID   Salary
Old values  1005   keith  1981-12-02 00:00:00.000   ACC001   10,500
New values  1006   keith  1981-05-02 00:00:00.000   ACC002   10,900

SQL Server is great for storing and manipulating data - but presentation like this should be done in a front-end application (like an ASP.NET application) - not in T-SQL....



回答3:

I am too late here !!! However I found that if employees XML as shown above has multiple records then the JOIN query with CTE returns incorrect results.

I have below XML input

DECLARE @XML1 XML
DECLARE @XML2 XML

SET @XML1 = 
'<NewDataSet> 
<Employees>
    <Employee>
        <Name> keith </Name>
        <EmpID> 1005 </EmpID>
        <DOB>12/02/1981</DOB>
        <DeptID>ACC001</DeptID>
        <Salary>10,500</Salary>
    </Employee>
    <Employee>
        <Name> keith </Name>
        <EmpID> 1004 </EmpID>
        <DOB>12/02/1981</DOB>
        <DeptID>ACC001</DeptID>
        <Salary>10,500</Salary>
    </Employee>
</Employees>
</NewDataSet>'

    SET @XML2 = 
    '<NewDataSet> 
    <Employees>
        <Employee>
            <Name> keith </Name>
            <EmpID> 1005 </EmpID>
            <DOB>12/02/1981</DOB>
            <DeptID>ACC001</DeptID>
            <Salary>10,500</Salary>
        </Employee>
        <Employee>
            <Name> keith </Name>
            <EmpID> 1004 </EmpID>
            <DOB>12/02/1981</DOB>
            <DeptID>ACC001</DeptID>
            <Salary>10,501</Salary>
        </Employee>
        <Employee>
            <Name> keith1 </Name>
            <EmpID> 10040 </EmpID>
            <DOB>12/02/1981</DOB>
            <DeptID>ACC001</DeptID>
            <Salary>10,501</Salary>
        </Employee>
    </Employees>
    </NewDataSet>'

I will use below query to find the difference

select  T.N.value('local-name(.)', 'nvarchar(100)') as NodeName,
T.N.value('.', 'nvarchar(100)') as Value
from @XML2.nodes('/NewDataSet/Employees/Employee/*') as T(N)

EXCEPT

select  T.N.value('local-name(.)', 'nvarchar(100)') as NodeName,
T.N.value('.', 'nvarchar(100)') as Value
from @XML1.nodes('/NewDataSet/Employees/Employee/*') as T(N)

Hope this helps !!!