XML Parsing & T-SQL - sql-server

Given the following from an XML field in a table:
<View>
<Criminal xmlns="http://tempuri.org/crimes.xsd">
<Person>
<PersonID>1234</PersonID>
<LastName>SMITH</LastName>
<FirstName>KEVIN</FirstName>
<Cases>
<PersonID>1234</PersonID>
<CaseNumber>12CASE34</CaseNumber>
</Cases>
</Person>
</Criminal>
</View>
How would I pull the Person/PersonID, LastName, Firstname info? Same goes for the CaseNumber.
My next issue is similar to above but lets add a second namespace:
<MessageContent xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Content>Content in here!!</Content>
<Type>Empty</Type>
</MessageContent>
Notice I have 2 namespaces in there AND they also have ":xsi" and ":xsd" in there too. I think those are referred to as schemas.

Try this:
DECLARE #table TABLE (ID INT NOT NULL, XmlContent XML)
INSERT INTO #table VALUES(1, '<View>
<Criminal xmlns="http://tempuri.org/crimes.xsd">
<Person>
<PersonID>1234</PersonID>
<LastName>SMITH</LastName>
<FirstName>KEVIN</FirstName>
<Cases>
<PersonID>1234</PersonID>
<CaseNumber>12CASE34</CaseNumber>
</Cases>
</Person>
</Criminal>
</View>')
;WITH XMLNAMESPACES('http://tempuri.org/crimes.xsd' AS ns)
SELECT
PersonID = XmlContent.value('(/View/ns:Criminal/ns:Person/ns:PersonID)[1]', 'int'),
FirstName = XmlContent.value('(/View/ns:Criminal/ns:Person/ns:FirstName)[1]', 'varchar(50)'),
LastName = XmlContent.value('(/View/ns:Criminal/ns:Person/ns:LastName)[1]', 'varchar(50)')
FROM #table
WHERE ID = 1
Returns an output of:
And for your second part of the question: yes, you have two namespaces defined - but they're not being used at all - so you can basically just ignore them:
INSERT INTO #table VALUES(2, '<MessageContent xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Content>Content in here!!</Content>
<Type>Empty</Type>
</MessageContent>')
SELECT
Content = XmlContent.value('(/MessageContent/Content)[1]', 'varchar(50)'),
Type = XmlContent.value('(/MessageContent/Type)[1]', 'varchar(50)')
FROM #table
WHERE ID = 2
Returns:

Related

How to select all children from XML in Sql Server?

I have the below XML when I run the select query it considers only the first value, what I am missing here?
<?xml version="1.0" encoding="utf-16"?>
<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<string>1</string>
<string>2</string>
<string>3</string>
.............
</ArrayOfString>
Below query only returns 1, I am expecting it to return all values like 1,2,3...
SELECT xmlu.v1.value('string[1]', 'BIGINT')
FROM
#xml.nodes('/ArrayOfString')
AS xmlU(v1)
If you want one row per string node, you need to include the string node in your nodes clause:
SELECT AOS.s.value('(./text())[1]','bigint') AS string --If it's string, why are you asking for a bigint?
FROM (VALUES(#XML))V(X)
CROSS APPLY V.X.nodes('/ArrayOfString/string')AOS(s);
It seems that you are looking for something like the following.
It is better not to use the IN clause because it usually gets converted to inefficient OR expressiions.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, city VARCHAR(20));
INSERT INTO #tbl (city) VALUES
('Miami'),
('Orlando'),
('Dallas'),
('New York'),
('Hollywood');
DECLARE #parameter XML =
N'<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<string>1</string>
<string>2</string>
<string>3</string>
</ArrayOfString>'
-- DDL and sample data population, end
;WITH rs AS
(
SELECT c.value('.', 'INT') AS ID
FROM #parameter.nodes('/ArrayOfString/string/text()') AS t(c)
)
SELECT *
FROM #tbl AS t INNER JOIN
rs ON t.ID = rs.ID;

SQL: How to select multiple xml nodes of the same name into a comma separated list

Suppose you had an XML field in a database table that looked like this:
<PEOPLE>
<PERSON>
<FIRST_NAME>John</FIRST_NAME>
<LAST_NAME>Doe</LAST_NAME>
</PERSON>
<PERSON>
<FIRST_NAME>Jane</FIRST_NAME>
<LAST_NAME>Doe</LAST_NAME>
</PERSON>
</PEOPLE>
And you wanted to get the names of all the people into a comma-separated list (e.g. "John Doe, Jane Doe"). Right now, my SQL query looks like this:
SELECT STUFF((
SELECT ', ' + FIRST_NAME + ' ' + LAST_NAME
FROM(
SELECT
p.value('(./FIRST_NAME)[1]', 'nvarchar(max)') as FIRST_NAME,
p.value('(./LAST_NAME)[1]', nvarchar(max)') as LAST_NAME
FROM [TABLE]
CROSS APPLY [FIELD].nodes('PEOPLE/PERSON') t(p)
) PEOPLE_ROWS
FOR XML PATH('')), 1, 1, '') COMMA_SEPARATED_LIST
Right now, this works just fine, and I get a nice comma separated list, but I thought there was an easier way to do this. Is there some xpath magic that yields simlar results?
It is relatively easy to implement by using XQuery and FLWOR expresssion.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, xmldata XML);
INSERT INTO #tbl (xmldata)
VALUES
(N'<PEOPLE>
<PERSON>
<FIRST_NAME>John</FIRST_NAME>
<LAST_NAME>Doe</LAST_NAME>
</PERSON>
<PERSON>
<FIRST_NAME>Jane</FIRST_NAME>
<LAST_NAME>Smith</LAST_NAME>
</PERSON>
</PEOPLE>');
-- DDL and sample data population, end
DECLARE #separator CHAR(1) = ',';
SELECT ID
, xmldata.query('for $i in /PEOPLE/PERSON
let $person := concat(data($i/FIRST_NAME[1])," ",data($i/LAST_NAME[1]))
return if ($i is (/PEOPLE/PERSON[last()])[1]) then $person
else concat($person, sql:variable("#separator"))')
.value('.', 'NVARCHAR(MAX)') AS [Comma_separated_list]
FROM #tbl;
Output
+----+--------------------------+
| ID | Comma_separated_list |
+----+--------------------------+
| 1 | John Doe, Jane Smith |
+----+--------------------------+

How to insert null value in a table from a XML?

I have a table which has several columns (Int, Bool) as nullable. I have one stored procedure which is taking XML as the input parameter. I'm trying to pass null values of some of these columns, But its inserting 0 instead of null.
declare #temp XML;
set #temp = '
<ArrayOfTestFileEntity xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<TestFileEntity>
<TestId xsi:nil="true" />
<MainTestNo xsi:nil="true" />
<TestCode xsi:nil="true" />
<TestCode1 />
<FlgTemp xsi:nil="true" />
</TestFileEntity>
</ArrayOfTestFileEntity>'
declare #xmlInput as XML = #temp;
declare #xmlOutput as XML;
BEGIN
SET NOCOUNT ON;
DECLARE #insertedTable table(Id int);
MERGE INTO Test AS Trg USING (
SELECT
d.x.value('TestId[1]', 'int') AS TestId,
d.x.value('MainTestNo[1]', 'int') AS MainTestNo,
d.x.value('TestCode[1]', 'int') AS TestCode,
d.x.value('TestCode1[1]', 'int') AS TestCode1,
d.x.value('FlgTemp[1]', 'bit') AS FlgTemp
FROM
#xmlInput.nodes('/ArrayOfTestFileEntity/TestFileEntity') AS d(x)
) AS Src ON Trg.Id = Src.Id
WHEN Matched THEN
UPDATE SET
Trg.TestId = Src.TestId,
Trg.MainTestNo = Src.MainTestNo,
Trg.TestCode = Src.TestCode,
Trg.TestCode1 = Src.TestCode1,
Trg.FlgTemp = Src.FlgTemp,
WHEN NOT matched BY TARGET THEN
INSERT
([TestId]
,[MainTestNo]
,[TestCode]
,[TestCode1]
,[FlgTemp])
VALUES
(Src.TestId,
Src.MainTestNo,
Src.TestCode,
Src.TestCode1,
Src.FlgTemp)
OUTPUT INSERTED.Id INTO #insertedTable;
set #xmlOutput = (SELECT * FROM #insertedTable for XML AUTO, ROOT('RowsUpserted'));
select #xmlOutput;
END
Basically you need to use [not(#xsi:nil = "true")] when you select value from xml.
So i have applied it in your query. So if you observed the xml you have noticed that i have added value 16 for <TestCode1> tag and keep rest of the tag as it is.
declare #temp XML;
set #temp = '
<ArrayOfTestFileEntity xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<TestFileEntity>
<TestId xsi:nil="true" />
<MainTestNo xsi:nil="true" />
<TestCode xsi:nil="true" />
<TestCode1>16</TestCode1>
<FlgTemp xsi:nil="true" />
</TestFileEntity>
</ArrayOfTestFileEntity>'
DECLARE #xmlInput as XML = #temp;
--MERGE INTO Test AS Trg USING (
SELECT
d.x.value('TestId[1][not(#xsi:nil = "true")]', 'int') AS TestId,
d.x.value('MainTestNo[1][not(#xsi:nil = "true")]', 'int') AS MainTestNo,
d.x.value('TestCode[1][not(#xsi:nil = "true")]', 'int') AS TestCode,
d.x.value('TestCode1[1][not(#xsi:nil = "true")]', 'int') AS TestCode1,
d.x.value('FlgTemp[1][not(#xsi:nil = "true")]', 'bit') AS FlgTemp
FROM
#xmlInput.nodes('/ArrayOfTestFileEntity/TestFileEntity') AS d(x)

tsql populate xml tag when there is no data

Is there a way to generate the xml tag when there is no data retrieved for that row?
For example:
Select
firstname,
middlename,
lastname
from
Names
for xml path('name'), type)
Outputs:
<name>
<firstname>John</firstname>
<middlename>Jim</middlename>
<lastname>Smith</lastname>
</name>
But if there is no middle name it skips that row in the xml.
<name>
<firstname>Jane</firstname>
<lastname>Smith</lastname>
</name>
I'm looking for this output if there is no data for middle name:
<name>
<firstname>Jane</firstname>
<middlename />
<lastname>Smith</lastname>
</name>
Can this be achieved?
Thanks!
You could try something like this:
SELECT
FirstName,
MiddleName = ISNULL(MiddleName, ''),
LastName
FROM
#input
FOR XML PATH('name')
which gives you
<name>
<FirstName>John</FirstName>
<MiddleName></MiddleName>
<LastName>Smith</LastName>
</name>
You can do this using XSNIL like this:
DECLARE #DataSource TABLE
(
[FirstName] NVARCHAR(32)
,[MiddleName] NVARCHAR(32)
,[LastName] NVARCHAR(32)
)
INSERT INTO #DataSource ([FirstName], [MiddleName], [LastName])
VALUES ('John','Smith','Tomas')
,('John',NULL,'Tomas')
,('John',NULL,NULL)
,(NULL,'Smith','Tomas')
SELECT [FirstName]
,[MiddleName]
,[LastName]
FROM #DataSource
FOR XML PATH('NAME'), ELEMENTS XSINIL, TYPE

Import XML with Attribute to SQL Server Table

I can't get the value for the XML attribute 'Country' into my table.
What am I doing wrong?
Here is my XML:
<?xml version="1.0" encoding="utf-8"?>
<CustomerDetails>
<PersonalInfo Country="USA">
<CustID>1001</CustID>
<CustLastName>Smith</CustLastName>
<DOB>2011-05-05T09:25:48.253</DOB>
<Address>
<Addr1>100 Smith St.</Addr1>
<City>New York</City>
</Address>
</PersonalInfo>
</CustomerDetails>
Here is my SQL:
Drop table #Cust
CREATE TABLE #Cust
(CustID INT, CustLastName VARCHAR(10)
, DOB DATETIME, Addr1 VARCHAR(100), City VARCHAR(10), Country VARCHAR(20))
insert into #Cust
select
c3.value('CustID[1]','int'),
c3.value('CustLastName[1]','varchar(10)'),
c3.value('DOB[1]','DATETIME'),
c3.value('(Address/Addr1)[1]','VARCHAR(100)'),
c3.value('(Address/City)[1]','VARCHAR(10)'),
c3.value('Country[1]','VARCHAR(20)')
from
(
select
cast(c1 as xml)
from
OPENROWSET (BULK 'C:\Users\wattronts\Documents\XMLImportTest.xml',SINGLE_BLOB) as T1(c1)
)as T2(c2)
cross apply c2.nodes('/CustomerDetails/PersonalInfo') T3(c3)
Select * from #Cust
Thanks for your help.
Use # to specify that you want an attribute.
T3.c3.value('#Country', 'varchar(50)')

Resources