XML generate from SQL Query with perfect XML structure - sql

MY SQl "employee" Table Look Like
+-------+---------+--------+----------+
| Empid | Empname | Salary | Location |
+-------+---------+--------+----------+
| 1 | Arul | 100 | Chennai |
+-------+---------+--------+----------+
XML Generate from SQl Query:
select * from employee for xml path, root('root')
from this Sql Query I'm Getting My XML Files as given below
<root>
<employee>
<Empid>1</Empid>
<Empname>Arul</Empname>
<Salary>100</Salary>
<Location>Chennai</Location>
</employee>
</root>
But My Expected Output XML from SQL query as
<root>
<column>Empid</column>
<value>1</value>
<column>Empname</column>
<value>Arul</value>
</root>

As you were told already, the needed output format is really bad and erronous. Nevertheless this can be done quite easily:
DECLARE #mockup TABLE(Empid INT,Empname VARCHAR(100),Salary DECIMAL(10,4),[Location] VARCHAR(100));
INSERT INTO #mockup VALUES(1,'Arul',100,'Chennai')
,(2,'One',200,'More');
SELECT 'Empid' AS [Column]
,EmpId AS [Value]
,'Empname' AS [Column]
,Empname AS [Value]
-- follow this pattern...
FROM #mockup t
FOR XML PATH('employee'),ROOT('root');
The result
<root>
<employee>
<Column>Empid</Column>
<Value>1</Value>
<Column>Empname</Column>
<Value>Arul</Value>
</employee>
<employee>
<Column>Empid</Column>
<Value>2</Value>
<Column>Empname</Column>
<Value>One</Value>
</employee>
</root>
But - by any chance - you should try to change this format. This is awful to query and will be your private headache for sure...
Some better suggestions:
<Employee>
<Column name="EmpId" value="1" />
<Column name="Empname" value="Arul" />
</Employee>
or
<employee id="1" name="Arul" />
or
<employee>
<Id>1</Id>
<Name>Arul</Name>
</employee>
or (if you really, really want to stick with this), at least a column index like here
<root>
<employee>
<Column inx="1">Empid</Column>
<Value inx="1">1</Value>
<Column inx="2">Empname</Column>
<Value inx="2">Arul</Value>
</employee>
<employee>
<Column inx="1">Empid</Column>
<Value inx="1">2</Value>
<Column inx="2">Empname</Column>
<Value inx="2">One</Value>
</employee>
</root>
The query for the last one is this
SELECT 1 AS [Column/#inx]
,'Empid' AS [Column]
,1 AS [Value/#inx]
,EmpId AS [Value]
,2 AS [Column/#inx]
,'Empname' AS [Column]
,2 AS [Value/#inx]
,Empname AS [Value]
-- follow this pattern...
FROM #mockup t
FOR XML PATH('employee'),ROOT('root');

Related

Extract Date Value from child node

I am needing to extract a date from a nested xml and am running into the issue of no output. The date I am attempting to retrieve is for Field name="A". My expected output is 2022-04-12. Here is the sample data along with the query I have generated. Not exactly sure where I am going wrong with the query. Any help will be greatly appreciated:
declare #xml xml
set #xml =
'<root xlmns="http://www.example.com">
<header></header>
<alt-header></alt-header>
<Main>
<Employee>
<Department>
<Category>
<Team>
<Name>Fun</Name>
<Sub>
<field name="A">
<Field-value xmlns:xsi="http://www.example2.com" xsi:type="date">
<value>2022-04-12</value>
</Field-value>
</field>
<field name="B">
<Field-value xmlns:xsi="http://www.example2.com" xsi:type="date">
<value>2021-04-12</value>
</Field-value>
</field>
</Sub>
</Team>
</Category>
</Department>
</Employee>
<Employee>
<Department>
<Category>
<Team>
<Name>Times</Name>
<Sub>
<field name="B">
<Field-value xmlns:xsi="http://www.cde.com" xsi:type="date">
<value>2021-04-12</value>
</Field-value>
</field>
</Sub>
</Team>
</Category>
</Department>
</Employee>
</Main>
</root>';
WITH XMLNAMESPACES (DEFAULT 'http://www.example.com',
'http://www.example2.com' as xsi)
SELECT x.query('./Department/Category/Team/Name[../Name="FUN"]/Sub/Field [#name="A"]/field-value/value').value('.', 'nvarchar(100)')A_date
from #xml.nodes('/root/Main/Employee') tempxml(x)
//Department/Category/Team[Name[text()="Fun"]]/Sub/field[#name="A"]/Field-value/value
The // in the beginning means that xpath will search everythere in the document. Since you are looking within Employee elements, you can change //Department to ./Department or even Department. All three should work.

How to update xml node value based on another node value in SQL Server

I have the following employees XML in "Test" table
<employees>
<employee>
<id>1</id>
<name>John doe 1</name>
</employee>
<employee>
<id>2</id>
<name>John doe 2</name>
</employee>
<employee>
<id>3</id>
<name>John doe 3</name>
</employee>
</employees>
I have to update the employee name whose id is 3. I am updating the following way.
update Test
set employeesXML.modify('replace value of (/employees/employee/name/text())[1]
with "xyz"')
where employeesXML.exist(N'/employees/employee/id[text()="3"]')
It updates the employee name with id 1 but if I don't specify the node index it throws an error.
How do I replace the value of a node based on the value of another node?
To select node with id, use following:
DECLARE #xml xml = '<employees>
<employee>
<id>1</id>
<name>John doe 1</name>
</employee>
<employee>
<id>2</id>
<name>John doe 2</name>
</employee>
<employee>
<id>3</id>
<name>John doe 3</name>
</employee>
</employees>'
SELECT #xml.value('(//employee[id=3]/name)[1]', 'nvarchar(max)')
To modify, use:
SET #xml.modify('replace value of (//employee[id=3]/name/text())[1] with "xyz"')

XML to SQL Server Parsing Issue

I'm parsing below XML and trying to fetch all the attributes/values of node.
declare #XBL xml='
<Root>
<Department>
<Employees>
<Employee type="temp">
Jason
</Employee>
<Employee type="perm">
Roy
</Employee>
</Employees>
</Department>
<Department>
<Employees >
<Employee type="temp2">
Kevin
</Employee>
</Employees>
</Department>
</Root>'
SELECT
[Type] = XC.value('(#type)[1]', 'varchar(25)'),
[Name] = XC.value('(../Employee)[1]' , 'varchar(30)')
FROM
#XBL.nodes('Root/Department/Employees/Employee') AS XTbl(XC)
Output of above query gives me all the attributes but with first value only(Jason).
Type Name
temp Jason
perm Jason
temp2 Kevin
Expected Output:
Type Name
temp Jason
perm Roy
temp2 Kevin
This should be what you're after:
SELECT XBL.E.value('#type','varchar(25)') AS [Type],
XBL.E.value('(./text())[1]','varchar(30)') AS [Name]
FROM #XBL.nodes('Root/Department/Employees/Employee') XBL(E);
Note the use of /text() as well. When returning data from inside a node, adding /text() actually improves the performance of the query.
Edit: Also, based on your sample xml, the value returned for [Name] would actually be '{Line break} Jason{Line break}' (Obviously replace the line break with the actual character). Is that what you intend, or do you want the whitespace and line breaks/carriage returns removed as well?
You're selecting the first Employee child of the parent Department:
[Name] = XC.value('(../Employee)[1]' , 'varchar(30)'
^^^^^^^^^^^^^^^^
To select the current Employee, use:
[Name] = XC.value('(.)[1]' , 'varchar(30)')
^^^^^^
Example at SQL Fiddle.

SQL XML Check if at-least one of node has value

I am writing a stored procedure where I need to check if one of the XML field "EmployeeId" has value in it. It should return the first found value in xml nodes. For eg, in below example, it should return node with value 19.
#Table NVARCHAR(MAX),
DECLARE #xmlEmployees XML = CAST(#Table AS XML);
Following is xml structure
<Employee>
<EmployeeId></EmployeeId>
</Employee>
<Employee>
<EmployeeId></EmployeeId>
</Employee>
<Employee>
<EmployeeId>19</EmployeeId>
</Employee>
<Employee>
<EmployeeId>21</EmployeeId>
</Employee>
In the above structure, the query should return the node with value 19.
Just use correct XPath expression:
DECLARE #xmlEmployees XML = '<Employee>
<EmployeeId></EmployeeId>
</Employee>
<Employee>
<EmployeeId></EmployeeId>
</Employee>
<Employee>
<EmployeeId>19</EmployeeId>
</Employee>
<Employee>
<EmployeeId>21</EmployeeId>
</Employee>';
SELECT #xmlEmployees.value('(//EmployeeId[text()])[1]', 'int')
Given your example I used this in SQL Server 2014 and it returned the value of 19, I just selected the top 1 result, and if the xml node is empty and is of value type INT it seems SQL Server converts it to 0, so I added a WHERE clause to filter out the top 2 entries as their EmployeeId values equal zero:
DECLARE #xDoc XML = '<Employee>
<EmployeeId></EmployeeId>
</Employee>
<Employee>
<EmployeeId></EmployeeId>
</Employee>
<Employee>
<EmployeeId>19</EmployeeId>
</Employee>
<Employee>
<EmployeeId>21</EmployeeId>
</Employee>'
SELECT TOP 1 a.doc.value('EmployeeId[1]', 'INT') AS [EmployeeId]
FROM #xDoc.nodes('/Employee') as a(doc)
WHERE a.doc.value('EmployeeId[1]', 'INT') <> 0

Processing XML Hierarchy with MS SQL

How would I specify the following XML Hierarchy into readable columns in Microsoft SQL?
<transaction id=1>
<item id=1>
<price>1</price>
</item>
<item id=2>
<price>1</price>
</item>
</transaction>
<transaction>
<item id=1>
<price>1</price>
</item>
</transaction>
for instance
select
x.i.value('(????)','Varchar(max)') [TransId]
x.i.value('(????)','Varchar(max)') [ItemId]
x.i.value('(????)','Varchar(max)') [PriceId]
from #xml.nodes('/transaction') x(i)
Thanks in advance.
Attribute values must always appear in quotation marks in XML.
Not sure about the desired output. An example would be:
declare #xml xml
Select #xml=
'<transaction id="1">
<item id="1">
<price>1</price>
</item>
<item id="2">
<price>2</price>
</item>
</transaction>
<transaction>
<item id="1">
<price>3</price>
</item>
</transaction>'
SELECT
y.value('../#id','int') as TransactionID,
y.value('#id','int') as ItemID,
y.value('(./price/text())[1]', 'Varchar(max)') as Price
FROM #xml.nodes('/transaction/item') as x(y)
order by TransactionID,ItemID
with the output:
NULL 1 3
1 1 1
1 2 2
Actually usually it's faster to shred XML from parent to child using apply, like this:
select
t.c.value('#id','int') as TransId,
i.c.value('#id','int') as ItemId,
i.c.value('(price/text())[1]', 'int') as PriceId
from #xml.nodes('transaction') as t(c)
outer apply t.c.nodes('item') as i(c)
order by TransId, ItemID
sql fiddle demo