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
;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
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....
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 !!!
Related
Im trying to load an XML file to SQL Server, but i m not getting anything
Here is my XML File:
<?xml version="1.0" encoding="UTF-8"?>
<catalog xmlns="http://www.demandware.com/xml/impex/catalog/2006-10-31" catalog-id="master-catalog-sso-us">
<header>
<image-settings>
<internal-location base-path="/"/>
<view-types>
<view-type>large</view-type>
<view-type>medium</view-type>
<view-type>small</view-type>
<view-type>swatch</view-type>
</view-types>
<alt-pattern>${productname}</alt-pattern>
<title-pattern>${productname}</title-pattern>
</image-settings>
</header>
<category category-id="root">
<display-name xml:lang="x-default">root Category</display-name>
<description xml:lang="x-default">root Category</description>
<online-flag>true</online-flag>
<template/>
<page-attributes/>
<refinement-definitions>
<refinement-definition type="attribute" bucket-type="none" attribute-id="shops" system="false">
<display-name xml:lang="x-default">Shops</display-name>
<value-set>search-result</value-set>
<sort-mode>value-name</sort-mode>
<sort-direction>ascending</sort-direction>
<cutoff-threshold>5</cutoff-threshold>
</refinement-definition>
</refinement-definitions>
</category>
<category category-id="default">
<display-name xml:lang="x-default">default Category</display-name>
<description xml:lang="x-default">default Category</description>
<online-flag>true</online-flag>
<parent>root</parent>
<template/>
<page-attributes/>
</category>
<product product-id="0217328320-sso-us">
<ean/>
<upc/>
<unit/>
<min-order-quantity>1</min-order-quantity>
<step-quantity>1</step-quantity>
<display-name xml:lang="x-default">Magnetibook - 4 Seasons</display-name>
<long-description xml:lang="x-default">48 magnets that stick to the metallic "canvas" where your child can choose between any of the four season "back drops" and then pick out the appropriate outfits and dress the family for a day outside. Comes in a magnetic closing "book" for safe keeping and easy storage.</long-description>
<store-force-price-flag>false</store-force-price-flag>
<store-non-inventory-flag>false</store-non-inventory-flag>
<store-non-revenue-flag>false</store-non-revenue-flag>
<store-non-discountable-flag>false</store-non-discountable-flag>
<online-flag>true</online-flag>
<online-from>2017-01-01T05:00:00.000Z</online-from>
<available-flag>true</available-flag>
<searchable-flag>false</searchable-flag>
<tax-class-id>standard</tax-class-id>
<brand>Janod</brand>
<manufacturer-name>Juratoys Company</manufacturer-name>
<sitemap-included-flag site-id="rco-us">true</sitemap-included-flag>
<sitemap-changefrequency site-id="rco-us">weekly</sitemap-changefrequency>
<sitemap-priority site-id="rco-us">1.0</sitemap-priority>
<page-attributes/>
<custom-attributes>
<custom-attribute attribute-id="ID">6339080</custom-attribute>
How do i load that ID field into SQL? I tried this:
But i believe my cross apply c.nodes is wrong! What Markups should i use?
Thanks
SELECT a.id.query('id').value('.','varchar(50)') as id FROM
( SELECT CAST(C AS XML) FROM OPENROWSET (BULK '\\test\master.xml', SINGLE_BLOB ) as T(c) ) AS S(c)
cross apply c.nodes('product/id') as A(id)
You ca try this:
SELECT c.id.query('id').value('.','varchar(50)') as id FROM
( SELECT CAST(C AS XML) FROM OPENROWSET (BULK '\\test\master.xml', SINGLE_BLOB ) as T(c) ) AS T(c)
cross apply c.nodes('product/id') as c(id)
But in your shared xml couldn't find an id tag within product tag.
Here I am sharing an Example:
XML File content:
<?xml version="1.0" encoding="utf-8"?>
<Customers>
<Customer>
<Document>000 000 000</Document>
<Name>Mary Angel</Name>
<Address>Your City, YC 1212</Address>
<Profession>Systems Analyst</Profession>
</Customer>
<Customer>
<Document>000 000 001</Document>
<Name>John Lenon</Name>
<Address>Your City, YC 1212</Address>
<Profession>Driver</Profession>
</Customer>
<Customer>
<Document>000 000 002</Document>
<Name>Alice Freeman</Name>
<Address>Your City, YC 1212</Address>
<Profession>Architect</Profession>
</Customer>
<Customer>
<Document>000 000 003</Document>
<Name>George Sands</Name>
<Address>Your City, YC 1212</Address>
<Profession>Doctor</Profession>
</Customer>
<Customer>
<Document>000 000 004</Document>
<Name>Mark Oliver</Name>
<Address>Your City, YC 1212</Address>
<Profession>Writer</Profession>
</Customer>
</Customers>
Copy above content and save as a xml file in d drive named testxmlfile.xml
Then try below query:
SELECT
MY_XML.Customer.query('Document').value('.', 'VARCHAR(20)') Document,
MY_XML.Customer.query('Name').value('.', 'VARCHAR(50)') Name,
MY_XML.Customer.query('Address').value('.', 'VARCHAR(50)')Address,
MY_XML.Customer.query('Profession').value('.', 'VARCHAR(50)' )Profession
FROM (SELECT CAST(MY_XML AS xml)
FROM OPENROWSET(BULK 'd:\testxmlfile.xml', SINGLE_BLOB) AS T(MY_XML)) AS T(MY_XML)
CROSS APPLY MY_XML.nodes('Customers/Customer') AS MY_XML (Customer);
Output:
Document Name Address Profession
000 000 000 Mary Angel Your City, YC 1212 Systems Analyst
000 000 001 John Lenon Your City, YC 1212 Driver
000 000 002 Alice Freeman Your City, YC 1212 Architect
000 000 003 George Sands Your City, YC 1212 Doctor
000 000 004 Mark Oliver Your City, YC 1212 Writer
You haven't been clear about what exactly you want, but it looks like you want the attribute product-id from here:
<product product-id="0217328320-sso-us">
You haven't taken into account namespaces, you need to add that.
It's also unclear if you have multiple product nodes. If you do, then you need this
WITH XMLNAMESPACES (DEFAULT 'http://www.demandware.com/xml/impex/catalog/2006-10-31')
SELECT A.product.value('#product-id','varchar(50)') as id
FROM
( SELECT CAST(C AS XML)
FROM OPENROWSET (BULK '\\test\master.xml', SINGLE_BLOB ) as T(c)
) AS S(c)
cross apply S.c.nodes('/catalog/product') as A(product)
If you have only one product node then you don't need .values at all:
WITH XMLNAMESPACES (DEFAULT 'http://www.demandware.com/xml/impex/catalog/2006-10-31')
SELECT S.c.value('/catalog/product/#product-id','varchar(50)') as id FROM
FROM
( SELECT CAST(C AS XML)
FROM OPENROWSET (BULK '\\test\master.xml', SINGLE_BLOB ) as T(c)
) AS S(c)
XML without record element need to convert to a table using T-SQL in SQL Server.
DECLARE #X XML = '<ROOT>
<RECORD>
<ID>1</ID>
<AppCode>Code 1</AppCode>
<ID>2</ID>
<AppCode>Code 2</AppCode>
<ID>3</ID>
<AppCode>Code 3</AppCode>
<ID>4</ID>
<AppCode>Code 4</AppCode>
<ID>5</ID>
<AppCode>Code 5</AppCode>
</RECORD>
</ROOT>'
I tried like this
SELECT
CASE WHEN items.item.value('local-name(.)', 'varchar(300)') = 'ID'
THEN items.item.value('text()[1]', 'varchar(300)')
END AS ID,
CASE WHEN items.item.value('local-name(.)', 'varchar(300)') = 'AppCode'
THEN items.item.value('text()[1]', 'varchar(300)')
END AS AppCode
FROM
#X.nodes('/ROOT/record/*') AS items(item)
Result was not correct -
ID AppCode
-----------------
1 NULL
NULL Code 1
2 NULL
NULL Code 2
I need a result like this::
ID Appcode
---------------
1 Code 1
2 Code 2
3 Code 3
4 Code 4
5 Code 5
UPDATE 2: This will be the best approach
Try it out
WITH Numbers AS
(SELECT TOP(#X.value('count(//ID)','int')) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS Nmbr FROM master..spt_values)
SELECT Nmbr
,#X.value('(//ID[sql:column("Nmbr")])[1]','int') AS ID
,#X.value('(//AppCode[sql:column("Nmbr")])[1]','nvarchar(max)') AS AppCode
,#X.value('(//SomeMore[sql:column("Nmbr")])[1]','nvarchar(max)') AS SomeMore
FROM Numbers;
The idea
count the IDs and return a derived tally table with a running number (in this case 1,2,3,4,5). Then use this number to address the elements by their position.
Two more approaches, just for fun :-)
This should work with many columns pretty fast:
DECLARE #X XML = '<ROOT>
<RECORD>
<ID>1</ID>
<AppCode>Code 1</AppCode>
<ID>2</ID>
<AppCode>Code 2</AppCode>
<ID>3</ID>
<AppCode>Code 3</AppCode>
<ID>4</ID>
<AppCode>Code 4</AppCode>
<ID>5</ID>
<AppCode>Code 5</AppCode>
</RECORD>
</ROOT>';
WITH allIDs AS
(
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS Pos
,id.value('text()[1]','int') AS ID
FROM #X.nodes('/ROOT/RECORD/ID') A(id)
)
SELECT ai.Pos
,ai.ID
,#X.value('/ROOT[1]/RECORD[1]/AppCode[sql:column("ai.Pos")][1]','nvarchar(max)') AS AppCode
,#X.value('/ROOT[1]/RECORD[1]/SomeMore[sql:column("ai.Pos")][1]','nvarchar(max)') AS SomeMore
FROM allIDs ai
ORDER BY ai.Pos;
The idea:
Read the IDs and find their position
Read the corresponding elements by using the ID's position via sql:column()
UPDATE: One more approach using conditional aggregation
This should be faster:
WITH AllNodes AS
(
SELECT (ROW_NUMBER() OVER(ORDER BY (SELECT NULL))-1) / 3 AS RowGroup
,nd.value('local-name(.)','nvarchar(max)') AS NodeName
,nd.value('text()[1]','nvarchar(max)') AS NodeValue
FROM #X.nodes('/ROOT/RECORD/*') A(nd)
)
SELECT RowGroup
,MAX(CASE WHEN NodeName='ID' THEN NodeValue END) AS ID
,MAX(CASE WHEN NodeName='AppCode' THEN NodeValue END) AS AppCode
,MAX(CASE WHEN NodeName='SomeMore' THEN NodeValue END) AS SomeMore
FROm AllNodes
GROUP BY RowGroup
The idea
We read all nodes as an EAV-structure. The (ROW_NUMBER-1)/3 (integer division!) will return a group index we can use in GROUP BY. The rest is old-fashioned PIVOT.
Warnings for both approaches:
This needs a structure without missing elements. If XML might include sections, where one of the follower nodes is missing, this won't work.
The solution with ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) did work for me in all cases I've used it. But it is worth to mention, that this is not guaranteed to return the correct position.
Assuming this is the format you are after:
ID Appcode
---------------
1 Code 1
2 Code 2
This is how I would do it:
DECLARE #X XML = '
<ROOT>
<RECORD>
<ID>1</ID>
<AppCode>Code 1</AppCode>
<ID>2</ID>
<AppCode>Code 2</AppCode>
</RECORD>
</ROOT>';
SELECT V.ID,AppCode
FROM #X.nodes('/ROOT/RECORD') AS X(R)
CROSS APPLY (VALUES(X.R.value('(./ID/text())[1]','int'),X.R.value('(./AppCode/text())[1]','varchar(10)')),
(X.R.value('(./ID/text())[2]','int'),X.R.value('(./AppCode/text())[2]','varchar(10)'))) V(ID,AppCode)
This, however, won't scale. So if you have an ID of 3, 4, it's not going to give you more rows.
If you can modify your XML format and use query as following to get result
DECLARE #X XML = '<ROOT>
<RECORD>
<ID>1</ID>
<AppCode>Code 1</AppCode>
</RECORD>
<RECORD>
<ID>2</ID>
<AppCode>Code 2</AppCode>
</RECORD>
<RECORD>
<ID>3</ID>
<AppCode>Code 3</AppCode>
</RECORD>
<RECORD>
<ID>4</ID>
<AppCode>Code 4</AppCode>
</RECORD>
<RECORD>
<ID>5</ID>
<AppCode>Code 5</AppCode>
</RECORD>
</ROOT>'
DECLARE #doc int
EXEC sp_xml_preparedocument #doc OUTPUT, #X
SELECT ID, AppCode
FROM OPENXML(#doc,'/ROOT/RECORD',2)
WITH (
ID int,
AppCode varchar(10)
)
The output will like
ID AppCode
1 Code 1
2 Code 2
3 Code 3
4 Code 4
5 Code 5
I can't find for the life of me if it's possible to add a direct reference to the schema in the root of my XML.
Here is my code:
Declare #Employee table (EmployeeID int,First_Name varchar(50),Last_Name varchar(50))
Insert into #Employee values
(1,'John','Smith'),
(2,'Jane','Doe' )
Select (Select CreatedBy='My Organization',CreateDate=GetDate() For XML Path('RecordHeader'),Type )
,(Select * From #Employee For XML Path('Employee'),Type )
For XML Path ('Employees'),Type
Which produces the following results:
<Employees>
<RecordHeader>
<CreatedBy>My Organization</CreatedBy>
<CreateDate>2016-10-18T16:09:48.110</CreateDate>
</RecordHeader>
<Employee>
<EmployeeID>1</EmployeeID>
<First_Name>John</First_Name>
<Last_Name>Smith</Last_Name>
</Employee>
<Employee>
<EmployeeID>2</EmployeeID>
<First_Name>Jane</First_Name>
<Last_Name>Doe</Last_Name>
</Employee>
</Employees>
Is there a way to add part in my SQL to make the root tag to include the following:
<Employees xmlns="https://www.blahblah.org/blahblah/BlahSchema.xsd">
Thank you for the help!
Always had trouble with xmlns, so I use a little hack
Declare #Employee table (EmployeeID int,First_Name varchar(50),Last_Name varchar(50))
Insert into #Employee values
(1,'John','Smith'),
(2,'Jane','Doe' )
Declare #XML xml = (
Select (Select CreatedBy='My Organization',CreateDate=GetDate() For XML Path('RecordHeader'),Type )
,(Select * From #Employee For XML Path('Employee'),Type )
For XML Path ('Employees'),Type
)
set #XML.modify('insert ( attribute MyNameSpace {"https://www.blahblah.org/blahblah/BlahSchema.xsd"}) into (/Employees)[1]')
set #XML = replace(cast(#XML as nvarchar(max)),'[MyNameSpace]','xmlns')
Select #XML
Returns
<Employees xmlns="https://www.blahblah.org/blahblah/BlahSchema.xsd">
<RecordHeader>
<CreatedBy>My Organization</CreatedBy>
<CreateDate>2017-01-10T15:19:42.873</CreateDate>
</RecordHeader>
<Employee>
<EmployeeID>1</EmployeeID>
<First_Name>John</First_Name>
<Last_Name>Smith</Last_Name>
</Employee>
<Employee>
<EmployeeID>2</EmployeeID>
<First_Name>Jane</First_Name>
<Last_Name>Doe</Last_Name>
</Employee>
</Employees>
EDIT to Exclude Empty Values
NULL will be omitted by default, so if you want to exclude empty values (not null), You can convert the value to NULL via NullIf(). For example
Declare #Employee table (EmployeeID int,First_Name varchar(50),Last_Name varchar(50))
Insert into #Employee values
(1,'John','Smith'),
(2,'Jane','Doe' ),
(3,'Betty',null ), -- No element for Last_Name
(4,'Susan','' ) -- No element for Last_Name
Declare #XML xml = (
Select (Select CreatedBy='My Organization',CreateDate=GetDate() For XML Path('RecordHeader'),Type )
,(Select EmployeeID = NullIf(EmployeeID,'')
,First_Name = NullIf(First_Name,'')
,Last_Name = NullIf(Last_Name,'')
From #Employee For XML Path('Employee'),Type )
For XML Path ('Employees'),Type
)
set #XML.modify('insert ( attribute MyNameSpace {"https://www.blahblah.org/blahblah/BlahSchema.xsd"}) into (/Employees)[1]')
set #XML = replace(cast(#XML as nvarchar(max)),'[MyNameSpace]','xmlns')
Select #XML
You can use the WITH NAMESPACES keyword, but that adds the name space to all the nodes:
Declare #Employee table (EmployeeID int,First_Name varchar(50),Last_Name varchar(50))
Insert into #Employee values
(1,'John','Smith'),
(2,'Jane','Doe' );
WITH XMLNAMESPACES (DEFAULT 'https://www.blahblah.org/blahblah/BlahSchema.xsd')
Select (Select CreatedBy='My Organization',CreateDate=GetDate() For XML Path('RecordHeader'),Type )
,(Select * From #Employee For XML Path('Employee'),Type )
For XML Path ('Employees'),Type
Result:
<Employees xmlns="https://www.blahblah.org/blahblah/BlahSchema.xsd">
<RecordHeader xmlns="https://www.blahblah.org/blahblah/BlahSchema.xsd">
<CreatedBy>My Organization</CreatedBy>
<CreateDate>2017-01-10T16:36:23.450</CreateDate>
</RecordHeader>
<Employee xmlns="https://www.blahblah.org/blahblah/BlahSchema.xsd">
<EmployeeID>1</EmployeeID>
<First_Name>John</First_Name>
<Last_Name>Smith</Last_Name>
</Employee>
<Employee xmlns="https://www.blahblah.org/blahblah/BlahSchema.xsd">
<EmployeeID>2</EmployeeID>
<First_Name>Jane</First_Name>
<Last_Name>Doe</Last_Name>
</Employee>
</Employees>
I am trying to generate a XML using FOR XML SQL Query. Structure of the XML is pretty straight forward, except the requirement calls for an extra sub-element, which has to appear only once at the top of the document between the Root Element and record element. Here is an example:
<Employees xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<RecordHeader>
<CreatedBy>My Organization</CreatedBy>
<CreateDate>1900-01-01T01:01:01-06:00</CreateDate>
</RecordHeader>
<Employee>
<EmployeeID>4</EmployeeID>
<FirstName>Rob</FirstName>
<LastName>Walters</LastName>
</Employee>
<Employee>
<EmployeeID>168</EmployeeID>
<FirstName>Rob</FirstName>
<LastName>Caron</LastName>
</Employee>
</Employees>
I created a FOR XML Query but I can't seem to get it to work properly for the RecordHeader. Any suggestions? Thanks!
SELECT
(SELECT 'My Organization' as [CreatedBy],
CONVERT(VARCHAR(33), GetDate(), 126) as [CreateDate] FOR XML PATH('RecordHeader'), ELEMENTS),
EmployeeID, FirstName, LastName
FROM Employees
FOR XML PATH('Employee'), ROOT('Employees'), ELEMENTS
Declare #Employee table (EmployeeID int,First_Name varchar(50),Last_Name varchar(50))
Insert into #Employee values
(1,'John','Smith'),
(2,'Jane','Doe' )
Select (Select CreatedBy='My Organization',CreateDate=GetDate() For XML Path('RecordHeader'),Type )
,(Select * From #Employee For XML Path('Employee'),Type )
For XML Path ('Employees'),Type
Returns
<Employees>
<RecordHeader>
<CreatedBy>My Organization</CreatedBy>
<CreateDate>2016-10-18T16:09:48.110</CreateDate>
</RecordHeader>
<Employee>
<EmployeeID>1</EmployeeID>
<First_Name>John</First_Name>
<Last_Name>Smith</Last_Name>
</Employee>
<Employee>
<EmployeeID>2</EmployeeID>
<First_Name>Jane</First_Name>
<Last_Name>Doe</Last_Name>
</Employee>
</Employees>
Is it possible to achieve the following table from of output using XQuery in SQL Server
Edit: A change in my requirement, Consider i have a table which stores the below xml and also UserID
DECLARE #XML xml
set #XML = '<Security>
<FiscalYear Name="2012">
<Country Id="204">
<State Id="1">
<City Id="10"></City>
</State>
<State Id="2">
<City Id="20"></City>
<City Id="30"></City>
</State>
<State Id ="3"></State>
</Country >
</FiscalYear>
</Security>'
CREATE TABLE #tmp_user
(UserID INT,SecurityXML XML)
INSERT INTO #tmp_user
( UserID, SecurityXML )
VALUES ( 1,
#XML
)
Now how can i get a o/p like
Output:
UserID StateID CityID
1 1 10
1 2 20
1 2 30
1 3 0
Is it possible to achieve?
I modified your XML a bit because it was invalid. Change the end tag </Subsidiary> to </Country>.
declare #XML xml
set #XML =
'<Security>
<FiscalYear Name="2012">
<Country Id="204">
<State Id="1">
<City Id="10"></City>
</State>
<State Id="2">
<City Id="20"></City>
<City Id="30"></City>
</State>
<State Id ="3"></State>
</Country>
</FiscalYear>
</Security>'
select S.N.value('#Id', 'int') as StateID,
coalesce(C.N.value('#Id', 'int'), 0) as CityID
from #XML.nodes('/Security/FiscalYear/Country/State') as S(N)
outer apply S.N.nodes('City') as C(N)
A version using a table instead of XML variable
select T.UserID,
S.N.value('#Id', 'int') as StateID,
coalesce(C.N.value('#Id', 'int'), 0) as CityID
from #tmp_user as T
cross apply T.SecurityXML.nodes('/Security/FiscalYear/Country/State') as S(N)
outer apply S.N.nodes('City') as C(N)