SQL Server parse XML to table - multiple node with the same name - sql-server

I would like to parse XML into a table in SQL Server 2012 where my XML has nodes with the same name.
My SQL query which return only the first row:
SELECT
[date] = Node.Data.value('(date)[1]', 'NVARCHAR(MAX)'),
name = Node.Data.value('(name)[1]', 'VARCHAR(MAX)')
FROM
#xml.nodes('result/subject') Node(Data)
XML sample
<result>
<subject>
<date>2019-06-03</date>
<name>AZGREX</name>
<name>ABGDFC</name>
<name>WWGDFW</name>
<name>FDSFSD</name>
<name>FSDWEW</name>
<name>CXZCXZ</name>
<name>GWGRE</name>
</subject>
</result>

You need to use nodes in the FROM:
DECLARE #XML xml = '<result>
<subject>
<date>2019-06-03</date>
<name>AZGREX</name>
<name>ABGDFC</name>
<name>WWGDFW</name>
<name>FDSFSD</name>
<name>FSDWEW</name>
<name>CXZCXZ</name>
<name>GWGRE</name>
</subject>
</result>';
SELECT r.[subject].value('(date/text())[1]','date') AS [date],
s.[name].value('(./text())[1]','varchar(6)') AS [name] --obviously, you'll likely need a larger length
FROM (VALUES(#XML))V(X)
CROSS APPLY V.X.nodes('result/subject') r([subject])
CROSS APPLY r.[subject].nodes('name') s([name]);

Related

Slow XML import with SQL server

I have a XML file with a size of 1GB.
I use the following code to load the data into sql server.
DECLARE #xmlvar XML
SELECT #xmlvar = BulkColumn
FROM OPENROWSET(BULK 'C:\Data\demo.xml', SINGLE_BLOB) x;
WITH XMLNAMESPACES(DEFAULT 'ux:no::ehe:v5:actual:aver',
'ux:no:ehe:v5:move' AS ns4,
'ux:no:ehe:v5:cat:fill' as ns3,
'ux:no:ehe:v5:centre' as ns2)
SELECT
zs.value(N'(../#versionCode)', 'VARCHAR(100)') as versionCode,
zs.value(N'(#Start)', 'VARCHAR(50)') as Start_date,
zs.value(N'(#End)', 'VARCHAR(50)') as End_date
into testtbl
FROM #xmlvar.nodes('/ns4:Dataview1/ns4:Content/ns4:gen') A(zs);
I takes now more than 2 hours to run the query and it is not finished.
I have tested the query with a smaller version of the XML file and that works.
Any tips on improving the loading speed?
Thank you.
Update XML file:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns4:Dataview1 xmlns="ux:no::ehe:v5:actual:aver" xmlns:ns4="ux:no:ehe:v5:move">
<ns4:Content versionCode="16000">
<ns4:gen start="1961-07-01" end="1961-07-01">
</ns4:gen>
<ns4:gen start="2017-09-19">
</ns4:gen>
<ns4:gen start="1961-07-02" end="2016-09-30">
</ns4:gen>
<ns4:gen start="2016-10-01" end="2017-09-18">
</ns4:gen>
</ns4:Content>
</ns4:Dataview1>
(1) As #Stu already pointed out, loading XML file first into a single row table will speed up the process of loading significantly.
(2) it is not a good idea to traverse XML up in the XPath expressions. Like here:
c.value('../#versionCode', 'VARCHAR(100)') as versionCode
But the XML structure was not shared in the question. So, it is impossible to suggest anything concrete.
2nd CROSS APPLY is simulating 1-to-many relationship in the XML hierarchy.
Check it out below.
SQL
CREATE TABLE tbl (
ID INT IDENTITY(1, 1) PRIMARY KEY,
XmlColumn XML
);
INSERT INTO tbl(XmlColumn)
SELECT * FROM OPENROWSET(BULK N'C:\Data\demo.xml', SINGLE_BLOB) AS x;
WITH XMLNAMESPACES(DEFAULT 'ux:no::ehe:v5:actual:aver',
'ux:no:ehe:v5:move' AS ns4,
'ux:no:ehe:v5:cat:fill' as ns3,
'ux:no:ehe:v5:centre' as ns2)
SELECT c.value('#versionCode', 'VARCHAR(100)') as versionCode,
x.value('#start', 'DATE') as Start_date,
x.value('#end', 'DATE') as End_date
INTO dbo.testtbl
FROM tbl
CROSS APPLY XmlColumn.nodes('/ns4:Dataview1/ns4:Content') AS t1(c)
CROSS APPLY t1.c.nodes('ns4:gen') AS t2(x);
In my opinion it's better to use an SSIS Package for importing XML files.
It has a component named "XML Source" for loading XML file.
There is a useful article at : https://www.sqlshack.com/import-xml-documents-into-sql-server-tables-using-ssis-packages/

SQL query to retrieve XML data

I have some XML in SQL Server:
Code to retrieve:
declare #xmlresponse xml
select top 1 #xmlrespone = xmlresponse from dto.t
;with xmlnamespaces(default 'http://www3')
select inputRefId = Node.Data.Value('.', 'varchar(50)')
from #xmlresponse.nodes('.') Node(Data)
This returns both fields (inputrefid and crn) as one string.
I would like to retrieve the values in separate fields starting with the inputrefid, but no matter what I try I get NULL.
e.g
select inputRefId = Node.Data.Value('(/inputRefId)[1]', 'varchar(50)')
Try it with this XQuery:
;WITH xmlnamespaces('http://www3' AS ns)
SELECT
inputRefId = Data.value('(ns:inputRefId)[1]', 'varchar(50)'),
crn = Data.value('(ns:crn)[1]', 'varchar(50)')
FROM
#xmlresponse.nodes('/resultDTO') Node(Data)
The point is that your top-level node - <resultDTO> does not have the http://www3 XML namespace - so you cannot really use the http://www3 as the default XML namespace for all the nodes in the XML - you need to be more specific and only apply it where it's really been set.

SQL Server output as XML all on one line

I'm using SQL Server 2014 and I'm new to XML. I'm trying to export 3 tables into XML. The 3 tables are:
Property (Parent)
PropertyArea (Child)
PropertyNotes (Child)
In SQL world, there is a left join from:
Property > PropertyArea
Property > PropertyNotes
The primary and foreign key is [property_id]
I have the below SQL code. However, when I open it in Notepad ++ it just creates the XML all on one line, and I was expecting to see it formatted.
DECLARE #XMLOutput XML
DECLARE #XMLOutputChar nvarchar(max)
;WITH XMLNAMESPACES('MyNameSpace' as ns)
SELECT #XMLOutput =
(
SELECT ISNULL(T1.[propertyusercode],'')AS propertyusercode,
ISNULL(T1.[NAME],'')AS [NAME],
(
SELECT
(
SELECT
[AreaCode]
,[AreaDesc]
FROM [PropertyArea]
WHERE [Property_Id] = T1.property_id
FOR XML PATH('Area'), TYPE
)
FOR XML PATH('Area'), TYPE
),
(
SELECT
(
SELECT
[NotesModuleNumber]
,[NotesPageNumber]
,[NotesText]
FROM [PropertyNotes]
WHERE [Property_Id] = T1.property_id
FOR XML PATH('Notes'), TYPE
)
FOR XML PATH('Notes'), TYPE
)
FROM Property T1
FOR XML PATH('Property'),TYPE, ROOT('Loader'),ELEMENTS XSINIL
)
SET #XMLOutputChar = '<?xml version="1.0" encoding="UTF-8"?>' + CONVERT(nvarchar(max),#XMLOutput)
SELECT #XMLOutputChar AS XMLOutput
Am I missing something? Is my code correct?
If you just want to see it on Notepad++, use plugin XML Tools to "pretty print" the XML into an easier to read format.
Copy paste from SQL Server:
After formatting:

Need help to format output of SQL Server XML sibling query

Consider the following SQL Server XML output:
<CUSTOMER>
<CUST_ID>TEST_CUSTOMER_01</CUST_ID>
<ORG_CODE>MY_ORG</ORG_CODE>
<CUSTOMER_TYPE CUST_TYPE="RETAIL" />
<CUSTOMER_COUNTRY CTRY_CODE="US" />
</CUSTOMER>
It was generated by the following SQL statement.
SELECT
CUSTOMER.CUST_ID, CUSTOMER.ORG_CODE,
(SELECT CUSTOMER_TYPE.CUST_TYPE
FROM CUSTOMER_TYPE
WHERE CUSTOMER.CUST_ID = CUSTOMER_TYPE.CUSTOMER_ID
FOR XML AUTO, TYPE),
(SELECT CUSTOMER_COUNTRY.CTRY_CODE
FROM CUSTOMER_COUNTRY
WHERE CUSTOMER.CUST_ID = CUSTOMER_COUNTRY.CUSTOMER_ID
FOR XML AUTO, TYPE)
FROM
CUSTOMER
WHERE
CUSTOMER.CUST_ID = 'TEST_CUSTOMER_01'
FOR XML AUTO, ELEMENTS
GO
It's required that the output look like the output below. Substituting ELEMENTS for the two TYPE words in the query above doesn't do it.
How then do I do it?
<CUSTOMER>
<CUST_ID>TEST_CUSTOMER_01</CUST_ID>
<ORG_CODE>MY_ORG</ORG_CODE>
<CUSTOMER_TYPE>
<CUST_TYPE>SHIP_TO</CUST_TYPE>
</CUSTOMER_TYPE>
<CUSTOMER_COUNTRY>
<CTRY_CODE>US</CTRY_CODE>
</CUSTOMER_COUNTRY>
</CUSTOMER>
Thanks!
The best (best in usage and performance!) is FOR XML PATH. It is very intuitive and easy to define any output you want simply by naming them.
SELECT
'TEST_CUSTOMER_01' AS [CUST_ID]
,'MY_ORG' AS [ORG_CODE]
,'SHIP_TO' AS [CUSTOMER_TYPE/CUST_TYPE]
,'US' AS [CUSTOMER_COUNTRY/CTRY_CODE]
--FROM
-- CUSTOMER
--WHERE
-- CUSTOMER.CUST_ID = 'TEST_CUSTOMER_01'
FOR XML PATH('CUSTOMER')
Try this query.
select CUST_ID,ORG_CODE,( SELECT CUSTOMER_TYPE.CUST_TYPE AS CUST_TYPE
FROM #customer_type CUSTOMER_TYPE
WHERE CUSTOMER.CUST_ID = CUSTOMER_TYPE.CUSTOMER_ID
FOR XML path (''),TYPE) AS 'CUSTOMER_TYPE' ,(
SELECT CUSTOMER_COUNTRY.CTRY_CODE as CTRY_CODE
FROM #customer_country CUSTOMER_COUNTRY
WHERE CUSTOMER.CUST_ID = CUSTOMER_COUNTRY.CUSTOMER_ID
for xml path (''),TYPE
) AS 'CUSTOMER_COUNTRY' from
#customer CUSTOMER
for xml auto, ELEMENTS
Using the above query you will get the result as
<CUSTOMER>
<CUST_ID>TEST_CUSTOMER_01</CUST_ID>
<ORG_CODE>MY_ORG</ORG_CODE>
<CUSTOMER_TYPE>
<CUST_TYPE>SHIP_TO</CUST_TYPE>
</CUSTOMER_TYPE>
<CUSTOMER_COUNTRY>
<CTRY_CODE>US</CTRY_CODE>
</CUSTOMER_COUNTRY>
</CUSTOMER>

Return XML cell data in XML query with SQL Server 2012

I have a table in SQL Server 2012 that contains some customer data. One of the columns in the table contains license data that is stored as XML. The type of the cell is nvarchar(MAX).
Is it possible to use FOR XML (or some other method) so that when the data is returned the XML from the license data is included as XML rather than a formatted string?
If I simply use FOR XML RAW, then the result is:
<Customers id="1" CustomerName="FirstCustomer"
LicenseData="<license customerid="1">...More data here...</license>" />
What I would liket to get is:
<Customers id="1" CustomerName="FirstCustomer">
<license customerid="1">
...More data here...
</license>
</Customers>
Is there any way to make that happen?
If the XML is a valid fragment then you can simply CAST it to XML.
SELECT CAST(MyColumn as XML) as MyXml
declare #temp table (id int, customername nvarchar(128), data nvarchar(max))
insert into #temp
select 1, 'FirstCustomer', '<license customerid="1"><element id="2">data1</element><element id="3"/></license>'
select id, customername, cast(data as xml)
from #temp
for xml raw
And you'll get results like this:
<row id="1" customername="FirstCustomer">
<license customerid="1">
<element id="2">data1</element>
<element id="3" />
</license>
</row>

Resources