Loop and Parse XML in SQL Server - sql-server

I need help to loop in and parse XML the way I wanted in SQL Server, Below is the XML.
DECLARE #Request XML =
'<Customer>
<Order>
<orderData>
<id>1</id>
</orderData>
<orderData>
<id>2</id>
</orderData>
<orderData>
<id>3</id>
<orderItem>
<description>Phone</description>
<price>299</price>
</orderItem>
<orderItem>
<description>Tablet</description>
<price>599</price>
</orderItem>
</orderData>
</Order>
</Customer>'
Below is how I wanted to retrieve data
|ItemId|ItemDesc |ItemPrice|
|1 |NULL |NULL |
|2 |NULL |NULL |
|3 |Phone |299 |
|3 |Tablet |599 |
Only ItemId 3 has description and price but in my query below it's applied to both itemid 1 and 2
SELECT
od.od_col.value('id[1]','int') AS ItemId
, oi.oi_col.value('description[1]','varchar(250)')AS ItemDesc
, oi.oi_col.value('price[1]','varchar(250)') AS ItemPrice
FROM
#Request.nodes('/Customer') cus(cus_col)
CROSS APPLY #Request.nodes('/Customer/Order/orderData') od(od_col)
CROSS APPLY #Request.nodes('/Customer/Order/orderData/orderItem') oi(oi_col)

PLease try the following solution.
SQL
DECLARE #Request XML =
'<Customer>
<Order>
<orderData>
<id>1</id>
</orderData>
<orderData>
<id>2</id>
</orderData>
<orderData>
<id>3</id>
<orderItem>
<description>Phone</description>
<price>299</price>
</orderItem>
<orderItem>
<description>Tablet</description>
<price>599</price>
</orderItem>
</orderData>
</Order>
</Customer>';
SELECT p.value('(id/text())[1]','INT') AS ItemId
, c.value('(description/text())[1]','VARCHAR(250)')AS ItemDesc
, c.value('(price/text())[1]','DECIMAL(10,2)') AS ItemPrice
FROM #Request.nodes('/Customer/Order/orderData') AS t1(p)
OUTER APPLY p.nodes('orderItem') AS t2(c);
Output
+--------+----------+-----------+
| ItemId | ItemDesc | ItemPrice |
+--------+----------+-----------+
| 1 | NULL | NULL |
| 2 | NULL | NULL |
| 3 | Phone | 299.00 |
| 3 | Tablet | 599.00 |
+--------+----------+-----------+

Related

Join 2 Table with same column and display the same column to one single column

I have 3 tables:
DATA_IZIN_BODY
DATA_IZIN_DETAIL
DATA_IZIN_DETAILPC
Here is the sample data :
DATA_IZIN_BODY//UPDATE
|ID_B|NIK |PERMIT TYPE|REASON |NAME |SUBMISSION DATE |STATUS |
|----|-----|-----------|-------------|------|-----------------|--------|
|N1 |1070 |ABSENT |SICK |John |9/5/2019 |PENDING |
|N2 |1088 |LEAVE |LATE |Laura |8/6/2019 |APPROVED|
|N3 |1009 |ABSENT |CANNT ATTND |Emmet |8/8/2019 |APPROVED|
DATA_IZIN_DETAIL*//UPDATE***
|ID |ID_B |NIK |DETAIL DATE |DETAIL HOUR|STATUS |FLAG|
|----|-----|-----|-------------|-----------|--------|----|
|001 |N1 |1070 |10/5/2019 |08.00 |NULL |1 |
|002 |N1 |1070 |11/5/2019 |07.00 |NULL |1 |
|003 |N1 |1070 |12/6/2019 |08.00 |NULL |1 |
|004 |N3 |1009 |9/8/2019 |09.00 |NULL |1 |
|005 |N3 |1088 |10/6/2019 |10.00 |NULL |1 |
|006 |N3 |1009 |11/8/2019 |11.00 |NULL |1 |
DATA_IZIN_DETAILPC*//UPDATE***
|ID |ID_B|NIK |DETAIL DATE |STATUS |FLAG |
|----|----|-----|-------------|--------|------|
|001 |N1 |1070 |13/5/2019 |NULL |2 |
|002 |N1 |1070 |14/6/2019 |NULL |2 |
|003 |N3 |1009 |12/8/2019 |NULL |2 |
THE GOAL
|ID |ID_B|NIK |NAME |PERMIT TYPE|REASON |DETAIL DATE |STATUS |FLAG|
|----|----|-----|------|-----------|-----------|-------------|--------|----|
|001 |N1 |1070 |John |ABSENT |SICK |13/5/2019 |NULL |2 |
|002 |N1 |1070 |John |ABSENT |SICK |14/6/2019 |NULL |2 |
|003 |N3 |1009 |Emmet |ABSENT |CANNT ATTND|12/8/2019 |NULL |2 |
|001 |N1 |1070 |John |ABSENT |SICK |10/5/2019 |NULL |1 |
|002 |N1 |1070 |John |ABSENT |SICK |11/5/2019 |NULL |1 |
|003 |N1 |1070 |John |ABSENT |SICK |12/6/2019 |NULL |1 |
|004 |N3 |1009 |Emmet |ABSENT |CANNT ATTND|9/8/2019 |NULL |1 |
|005 |N3 |1088 |Emmet |ABSENT |CANNT ATTND|10/6/2019 |NULL |1 |
|006 |N3 |1009 |Emmet |ABSENT |CANNT ATTND|11/8/2019 |NULL |1 |
And here is my query, tried with case but won't work as expected:
select
b.izin_id, b.IZIN_NIK
/*case
when pc.Flag = 2 then 'PC'
when d.Flag = 1 then 'DT'end
as Flag*/
from DATA_IZIN_BODY b
inner join DATA_IZIN_DETAIL d on d.IZIN_ID = b.IZIN_ID
inner join DATA_IZIN_DETAILPC pc on pc.IZIN_ID = b.IZIN_ID
but how do we combine:
DATA_IZIN_DETAIL.izin_id & DATA_IZIN_DETAILPC.izin_id into 1 column as ID ?
DATA_IZIN_DETAIL.Flag & DATA_IZIN_DETAILPC.Flag into 1 column as Flag?
update
ON GOAL : COLUMN ID & FLAG CONTAIN VALUE OF DATA_IZIN_DETAILPC.ID & DATA_IZIN_DETAIL.ID, DATA_IZIN_DETAILPC.FLAG & DATA_IZIN_DETAIL.FLAG
update 2
Data_izin_body
1. ID_B : nvarchar
2. NIK : nvarchar
3. Permit_Type : nvarchar
4. Reason : nvarchar
5. Name : nvarchar
6. Submission_Date : smalldatetime
7. Status : nvarchar
Data_izin_detail
1. ID : nvarchar
2. ID_B : nvarchar
2. NIK : nvarchar
3. Detail_Date : smalldatetime
4. Detail_Hour : nvarchar
5. Status : nvarchar
6. Flag : int
Data_izin_detailpc
1. ID : nvarchar
2. ID_B : nvarchar
2. NIK : nvarchar
3. Detail_Date : smalldatetime
4. Status : nvarchar
5. Flag : int
So the UNION ALL combines the 2 secondary tables. Then the main table joins to that.
SELECT Details.ID,
b.ID_B,
b.IZIN_NIK AS NIK,
b.Name,
b.[Permit Type],
b.Reason,
Details.[Detail Date],
Details.Status,
Details.Source,
Details.Flag
FROM DATA_IZIN_BODY b
JOIN
(SELECT ID, ID_B, [DETAIL DATE], STATUS, 'DT' AS Source, FLAG
FROM DATA_IZIN_DETAIL
UNION ALL
SELECT ID, ID_B, [DETAIL DATE], STATUS, 'PC' AS Source, FLAG
FROM DATA_IZIN_DETAILPC) AS Details
ON b.ID_B = Details.ID_B
ORDER BY b.NAME,
Details.[DETAIL DATE]
Not sure you still need the FLAG column if you have the Source column...
It seems that tables DATA_IZIN_DETAIL and DATA_IZIN_DETAILPC have the same field structure. If that is intended, you can perform a UNION on two SELECT queries:
SELECT [ID], [ID_B], [NIK], [NAME], [DETAIL DATE], [STATUS], [FLAG] FROM [DATA_IZIN_DETAILPC]
UNION ALL
SELECT [ID], [ID_B], [NIK], [NAME], [DETAIL DATE], [STATUS], [FLAG] FROM [DATA_IZIN_DETAIL]
Edit:
Based on the recent information in your updated question, I would suggest the following query to get your desired results:
SELECT
D.[ID],
D.[ID_B],
D.[NIK],
B.[Name],
B.[Permit_Type],
B.[Reason],
D.[Detail_Date],
D.[Status],
D.[Flag]
FROM
[DATA_IZIN_DETAILPC] AS D
INNER JOIN [DATA_IZIN_BODY] AS B ON B.[ID_B] = D.[ID_B] --AND B.[NIK] = D.[NIK]
UNION ALL
SELECT
D.[ID],
D.[ID_B],
D.[NIK],
B.[Name],
B.[Permit_Type],
B.[Reason],
D.[Detail_Date],
D.[Status],
D.[Flag]
FROM
[DATA_IZIN_DETAIL] AS D
INNER JOIN [DATA_IZIN_BODY] AS B ON B.[ID_B] = D.[ID_B] --AND B.[NIK] = D.[NIK]

Get Value from XML attribute in SQL Server 2008 Using OPENXML

I'm trying to extract a value from my XML and seem to be struggling. Hope someone can help
here is my XML
'<Transfer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Products>
<Product TUT="true" ID="38319223176264031724">
<Identifier>38319223176264031724</Identifier>
<ProductItemCode>83192</ProductItemCode>
<Qty>1</Qty>
<NetWeight>23.100</NetWeight>
<GrossWeight>23.684</GrossWeight>
<SerialNumber>317</SerialNumber>
<ECertItemNumber>2</ECertItemNumber>
<Markets Type="ECERT">
<Market>EU</Market>
<Market>US</Market>
</Markets>
<Attribute Name="PackDate">2016-09-20T00:00:00</Attribute>
<Attribute Name="PlantID">124</Attribute>
<Attribute Name="SlgrDate">2016-09-19T00:00:00</Attribute>
</Product>
<Product TUT="true" ID="28319219766306010024">
<Identifier>28319219766306010024</Identifier>
<ProductItemCode>83192</ProductItemCode>
<Qty>1</Qty>
<NetWeight>19.700</NetWeight>
<GrossWeight>20.284</GrossWeight>
<SerialNumber>100</SerialNumber>
<ECertItemNumber>2</ECertItemNumber>
<Markets Type="ECERT">
<Market>EU</Market>
<Market>US</Market>
</Markets>
<Attribute Name="PackDate">2016-11-01T00:00:00</Attribute>
<Attribute Name="PlantID">124</Attribute>
<Attribute Name="SlgrDate">2016-10-31T00:00:00</Attribute>
</Product>
</Products>
</Transfer>'
What I want to extract are the Identifier, ProductItemCode, NetWeight, GrossWeight, AND the Attribute Values of PackDate and SlgrDate.
I can easily get all of the fields EXCEPT for Attribute Values of PackDate and SlgrDate
Here is my code for the fields
if OBJECT_ID('tempdb..#XmlImportTest') is not null
drop table #XmlImportTest
CREATE TABLE #XmlImportTest(
xmlFileName VARCHAR(300) NOT NULL,
xml_data XML NOT NULL
)
GO
DECLARE #xmlFileName VARCHAR(3000)
SELECT #xmlFileName = 'K:\Upload\CSNXML\WaybillXml.xml'
--– dynamic sql is just so we can use #xmlFileName variable in OPENROWSET
EXEC('INSERT INTO #XmlImportTest(xmlFileName, xml_data)
SELECT ''' + #xmlFileName + ''', xmlData
FROM(
SELECT *
FROM OPENROWSET (BULK ''' + #xmlFileName + ''', SINGLE_BLOB) AS XMLDATA
) AS FileImport (XMLDATA)
')
GO
DECLARE #XML AS XML, #hDoc AS INT, #SQL NVARCHAR (MAX)
select #xml = (SELECT xml_data from #XmlImportTest)
EXEC sp_xml_preparedocument #hDoc OUTPUT, #XML
SELECT Identifier as barcode,ProductItemCode as standpack,SerialNumber, NetWeight netwt_ind, GrossWeight grosswt_ind
FROM OPENXML (#hDoc, '/Transfer/Products/Product',2)
WITH (Identifier varchar(80),
ProductItemCode varchar(10),
SerialNumber varchar(48),
NetWeight decimal(13,2),
GrossWeight decimal(13,2)
)
exec sp_xml_removedocument #hDoc
the xml file contains the same as the sample xml provided
Now I have no idea how to get the value out of the Attributes for each product.
I am running this in SQL SERVER 2008
Use the optional ColPattern to specify the XPath to the node you want.
FROM OPENXML (#hDoc, '/Transfer/Products/Product',2)
WITH (
Identifier varchar(80),
ProductItemCode varchar(10),
SerialNumber varchar(48),
NetWeight decimal(13,2),
GrossWeight decimal(13,2),
PackDate datetime 'Attribute[#Name = "PackDate"]',
PlantID int 'Attribute[#Name = "PlantID"]',
SlgrDate datetime 'Attribute[#Name = "SlgrDate"]'
)
FROM OPENXML is outdated and should not be used anymore (rare exceptions exist)
Try to use the up-to-date XML-type-methods:
SELECT p.value(N'#TUT',N'bit') AS TUT
,p.value(N'#ID',N'nvarchar(max)') AS ID
,p.value(N'(Identifier/text())[1]',N'nvarchar(max)') AS Identifier
,p.value(N'(ProductItemCode/text())[1]',N'int') AS ProductItemCode
,p.value(N'(Qty/text())[1]',N'int') AS Qty
,p.value(N'(NetWeight/text())[1]',N'decimal(14,4)') AS NetWeight
,p.value(N'(SerialNumber/text())[1]',N'int') AS SerialNumber
,p.value(N'(ECertItemNumber/text())[1]',N'int') AS ECertItemNumber
,p.value(N'(Markets/#Type)[1]',N'nvarchar(max)') AS Markets_Type
,m.value(N'text()[1]',N'nvarchar(max)') AS Markets_Market
,p.value(N'(Attribute[#Name="PackDate"]/text())[1]',N'datetime') AS PackDate
,p.value(N'(Attribute[#Name="PlantID"]/text())[1]',N'int') AS PlantID
,p.value(N'(Attribute[#Name="SlgrDate"]/text())[1]',N'datetime') AS SlgrDate
FROM #xml.nodes(N'Transfer/Products/Product') AS A(p)
CROSS APPLY a.p.nodes(N'Markets/Market') AS B(m);
The result
+-----+----------------------+----------------------+-----------------+-----+-----------+--------------+-----------------+--------------+----------------+-------------------------+---------+-------------------------+
| TUT | ID | Identifier | ProductItemCode | Qty | NetWeight | SerialNumber | ECertItemNumber | Markets_Type | Markets_Market | PackDate | PlantID | SlgrDate |
+-----+----------------------+----------------------+-----------------+-----+-----------+--------------+-----------------+--------------+----------------+-------------------------+---------+-------------------------+
| 1 | 38319223176264031724 | 38319223176264031724 | 83192 | 1 | 23.1000 | 317 | 2 | ECERT | EU | 2016-09-20 00:00:00.000 | 124 | 2016-09-19 00:00:00.000 |
+-----+----------------------+----------------------+-----------------+-----+-----------+--------------+-----------------+--------------+----------------+-------------------------+---------+-------------------------+
| 1 | 38319223176264031724 | 38319223176264031724 | 83192 | 1 | 23.1000 | 317 | 2 | ECERT | US | 2016-09-20 00:00:00.000 | 124 | 2016-09-19 00:00:00.000 |
+-----+----------------------+----------------------+-----------------+-----+-----------+--------------+-----------------+--------------+----------------+-------------------------+---------+-------------------------+
| 1 | 28319219766306010024 | 28319219766306010024 | 83192 | 1 | 19.7000 | 100 | 2 | ECERT | EU | 2016-11-01 00:00:00.000 | 124 | 2016-10-31 00:00:00.000 |
+-----+----------------------+----------------------+-----------------+-----+-----------+--------------+-----------------+--------------+----------------+-------------------------+---------+-------------------------+
| 1 | 28319219766306010024 | 28319219766306010024 | 83192 | 1 | 19.7000 | 100 | 2 | ECERT | US | 2016-11-01 00:00:00.000 | 124 | 2016-10-31 00:00:00.000 |
+-----+----------------------+----------------------+-----------------+-----+-----------+--------------+-----------------+--------------+----------------+-------------------------+---------+-------------------------+
Hint: If you do not need the <Markets> as 1:n-relation (here doubling your resultset!), just remove the CROSS APPLY and the line starting with m.value.

SQL-Server XML-Bulk-Import and reading as table-data

I have the following Problem:
For the XML-Import into SQL-Sever, I use this code:
DROP TABLE XMLwithOpenXML
CREATE TABLE XMLwithOpenXML
(
Id INT IDENTITY PRIMARY KEY,
XMLData XML,
LoadedDateTime DATETIME
)
INSERT INTO XMLwithOpenXML(XMLData, LoadedDateTime)
SELECT CONVERT(XML, BulkColumn) AS BulkColumn, GETDATE()
FROM OPENROWSET(BULK '\\WINSER1\\proALPHA\\templates_eBus\\Test.xml', SINGLE_BLOB) AS x;
DECLARE #XML AS XML, #hDoc AS INT, #SQL NVARCHAR (MAX)
SELECT #XML = XMLData FROM XMLwithOpenXML
EXEC sp_xml_preparedocument #hDoc OUTPUT, #XML
It works fine. But here in the XML, I don't know, what should I do:
<MIME_INFO>
<MIME>
<MIME_TYPE>image/jpeg</MIME_TYPE>
<MIME_SOURCE>ube105252.jpg</MIME_SOURCE>
<MIME_PURPOSE>normal</MIME_PURPOSE>
<MIME_ORDER>1</MIME_ORDER>
</MIME>
<MIME>
<MIME_TYPE>image/jpeg</MIME_TYPE>
<MIME_SOURCE>bbd372670.jpg</MIME_SOURCE>
<MIME_PURPOSE>logo</MIME_PURPOSE>
<MIME_ORDER>2</MIME_ORDER>
</MIME>
</MIME_INFO>
An user of me, needs both <MIME>-Blocks. But they are named the same!
How can I get this 8 rows contented in the 2 <Mime>-Tags? Rename is not the solution, because the XML has over 2.000.000 rows!
THX.
EDIT 16:20
Here the rest of the code above. With this tags it works fine:
DECLARE #XML AS XML, #hDoc AS INT, #SQL NVARCHAR (MAX)
SELECT #XML = XMLData FROM XMLwithOpenXML
EXEC sp_xml_preparedocument #hDoc OUTPUT, #XML
SELECT [SUPPLIER_AID]
,REFERENCE_FEATURE_SYSTEM_NAME
,REFERENCE_FEATURE_GROUP_ID
FROM OPENXML(#hDoc, 'BMECAT/T_NEW_CATALOG/ARTICLE')
--FROM OPENXML(#hDoc, 'BMECAT/T_NEW_CATALOG/ARTICLE/ARTICLE_ORDER_DETAILS')
--## Hier werden die gewünschten Columns deklariert.
WITH
(
SUPPLIER_AID [varchar](25) 'SUPPLIER_AID'
,REFERENCE_FEATURE_SYSTEM_NAME [varchar](25) 'REFERENCE_FEATURE_SYSTEM_NAME'
,REFERENCE_FEATURE_GROUP_ID [varchar](25) 'REFERENCE_FEATURE_GROUP_ID'
)
-----------------------------EDIT 16092016 / 08:14-----------------------------
I still don't understand your code, because you use not the real table "XMLwithOpenXML". Hier is one article of ~ 20.000 in the XML:
<BMECAT>
<T_NEW_CATALOG>
<ARTICLE mode="new">
<SUPPLIER_AID>9900026005</SUPPLIER_AID>
<MIME_INFO>
<MIME>
<MIME_TYPE>image/jpeg</MIME_TYPE>
<MIME_SOURCE>ube105252.jpg</MIME_SOURCE>
<MIME_PURPOSE>normal</MIME_PURPOSE>
<MIME_ORDER>1</MIME_ORDER>
</MIME>
<MIME>
<MIME_TYPE>image/jpeg</MIME_TYPE>
<MIME_SOURCE>bbd372670.jpg</MIME_SOURCE>
<MIME_PURPOSE>logo</MIME_PURPOSE>
<MIME_ORDER>2</MIME_ORDER>
</MIME>
<MIME>
<MIME_TYPE>image/jpeg</MIME_TYPE>
<MIME_SOURCE>ube305149.jpg</MIME_SOURCE>
<MIME_PURPOSE>logo</MIME_PURPOSE>
<MIME_ORDER>3</MIME_ORDER>
</MIME>
<MIME>
<MIME_TYPE>image/jpeg</MIME_TYPE>
<MIME_SOURCE>ube108453.jpg</MIME_SOURCE>
<MIME_PURPOSE>others</MIME_PURPOSE>
<MIME_ORDER>4</MIME_ORDER>
</MIME>
<MIME>
<MIME_TYPE>application/pdf</MIME_TYPE>
<MIME_SOURCE>ube007100.pdf</MIME_SOURCE>
<MIME_PURPOSE>others</MIME_PURPOSE>
<MIME_ORDER>5</MIME_ORDER>
</MIME>
</MIME_INFO>
</ARTICLE>
</T_NEW_CATALOG>
</BMECAT>
You see there is one SUPPLIER_AID and four times a <MIME>-tag. I need only the first and the second (where normal and logo). What is in this case with the SUPPLIER_AID? I think the code has to look like:
WITH Numbered AS
(
SELECT LoadedDateTime
,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS ID
--,a.query('.') AS SUPPLIER_AID
,m.query('.') AS mime
FROM XMLwithOpenXML AS t
CROSS APPLY t.XMLData.nodes('BMECAT/T_NEW_CATALOG/ARTICLE/MIME_INFO/MIME') AS A(m)
)
SELECT ID
--,[SUPPLIER_AID].value('(ARTICLE)[1]','nvarchar(max)') AS SUPPLIER_AID
,mime.value('(MIME/MIME_TYPE)[1]','nvarchar(max)') AS MIME_TYPE
,mime.value('(MIME/MIME_SOURCE)[1]','nvarchar(max)') AS MIME_SOURCE
,mime.value('(MIME/MIME_PURPOSE)[1]','nvarchar(max)') AS MIME_PURPOSE
,mime.value('(MIME/MIME_ORDER)[1]','nvarchar(max)') AS MIME_ORDER
FROM Numbered
With the new code, I get this:
+-----------+--------------+-------------+-----------+
|MIME_TYPE |MIME_SOURCE |MIME_PURPOSE |MIME_ORDER |
+-----------+--------------+-------------+-----------+
|image/jpeg |ube105252.jpg |normal |1 |
+-----------+--------------+-------------+-----------+
|image/jpeg |bbd372670.jpg |logo |2 |
+-----------+--------------+-------------+-----------+
|image/jpeg |ube105252.jpg |logo |3 |
+-----------+--------------+-------------+-----------+
|image/jpeg |bbd372670.jpg |others |4 |
+-----------+--------------+-------------+-----------+
|image/jpeg |bbd372670.jpg |others |5 |
+-----------+--------------+-------------+-----------+
But what I need is something like:
+-------------+------------+------------------+--------------+-------------+
|SUPPLIER_AID | MIME_TYPE | MIME_SOURCE | MIME_PURPOSE | MIME_ORDER |
+-------------+------------+------------------+--------------+-------------+
|9900026005 | image/jpeg | ube105252.jpg | normal | 1 |
+-------------+------------+------------------+--------------+-------------+
|9900026005 | image/jpeg | bbd372670.jpg | logo | 2 |
+-------------+------------+------------------+--------------+-------------+
Your approach with FROM OPENXML is outdated and should not be used any more. There are much better XML methods like .node(), .value(), .query() and .modify().
The way you get the XML into your table is quite OK. Once you have it there, you should continue like this:
Attention I use a declared mock-up-table to simulate your table.
DECLARE #XMLwithOpenXML TABLE(XMLData XML,LoadedDateTime DATETIME);
INSERT INTO #XMLwithOpenXML VALUES
('<MIME_INFO>
<MIME>
<MIME_TYPE>image/jpeg</MIME_TYPE>
<MIME_SOURCE>ube105252.jpg</MIME_SOURCE>
<MIME_PURPOSE>normal</MIME_PURPOSE>
<MIME_ORDER>1</MIME_ORDER>
</MIME>
<MIME>
<MIME_TYPE>image/jpeg</MIME_TYPE>
<MIME_SOURCE>bbd372670.jpg</MIME_SOURCE>
<MIME_PURPOSE>logo</MIME_PURPOSE>
<MIME_ORDER>2</MIME_ORDER>
</MIME>
</MIME_INFO>',GETDATE());
At this point, your XML is successfully taken into your table
The CTE "Numbered" will read all MIME elements using .nodes() in there inherent order and number them accordingly.
The SELECT pulls the actual data
WITH Numbered AS
(
SELECT LoadedDateTime
,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS ID
,m.query('.') AS mime
FROM #XMLwithOpenXML AS t
CROSS APPLY t.XMLData.nodes('/MIME_INFO/MIME') AS A(m)
)
SELECT ID
,LoadedDateTime
,mime.value('(MIME/MIME_TYPE)[1]','nvarchar(max)') AS MIME_TYPE
,mime.value('(MIME/MIME_SOURCE)[1]','nvarchar(max)') AS MIME_SOURCE
,mime.value('(MIME/MIME_PURPOSE)[1]','nvarchar(max)') AS MIME_PURPOSE
,mime.value('(MIME/MIME_ORDER)[1]','nvarchar(max)') AS MIME_ORDER
FROM Numbered
The result
+----+-------------------------+------------+---------------+--------------+------------+
| ID | LoadedDateTime | MIME_TYPE | MIME_SOURCE | MIME_PURPOSE | MIME_ORDER |
+----+-------------------------+------------+---------------+--------------+------------+
| 1 | 2016-09-15 16:37:30.730 | image/jpeg | ube105252.jpg | normal | 1 |
+----+-------------------------+------------+---------------+--------------+------------+
| 2 | 2016-09-15 16:37:30.730 | image/jpeg | bbd372670.jpg | logo | 2 |
+----+-------------------------+------------+---------------+--------------+------------+
UPDATE
You did not show the full XML... With the example given above this code extracts all you might want to knwo:
WITH Numbered AS
(
SELECT Id
,LoadedDateTime
,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS MimeRowNr
,a.value('#mode','nvarchar(max)') ARTICLE_MODE
,a.value('SUPPLIER_AID[1]','nvarchar(max)') AS SUPPLIER_AID
,m.query('.') AS mime
FROM XMLwithOpenXML AS t
CROSS APPLY t.XMLData.nodes('/BMECAT/T_NEW_CATALOG/ARTICLE') AS A(a)
CROSS APPLY a.nodes('MIME_INFO/MIME') AS B(m)
)
SELECT Id
,MimeRowNr
,LoadedDateTime
,ARTICLE_MODE
,SUPPLIER_AID
,mime.value('(MIME/MIME_TYPE)[1]','nvarchar(max)') AS MIME_TYPE
,mime.value('(MIME/MIME_SOURCE)[1]','nvarchar(max)') AS MIME_SOURCE
,mime.value('(MIME/MIME_PURPOSE)[1]','nvarchar(max)') AS MIME_PURPOSE
,mime.value('(MIME/MIME_ORDER)[1]','int') AS MIME_ORDER
FROM Numbered;
The result
+----+-----------+-------------------------+--------------+--------------+-----------------+---------------+--------------+------------+
| Id | MimeRowNr | LoadedDateTime | ARTICLE_MODE | SUPPLIER_AID | MIME_TYPE | MIME_SOURCE | MIME_PURPOSE | MIME_ORDER |
+----+-----------+-------------------------+--------------+--------------+-----------------+---------------+--------------+------------+
| 1 | 1 | 2016-09-16 09:32:53.570 | new | 9900026005 | image/jpeg | ube105252.jpg | normal | 1 |
+----+-----------+-------------------------+--------------+--------------+-----------------+---------------+--------------+------------+
| 1 | 2 | 2016-09-16 09:32:53.570 | new | 9900026005 | image/jpeg | bbd372670.jpg | logo | 2 |
+----+-----------+-------------------------+--------------+--------------+-----------------+---------------+--------------+------------+
| 1 | 3 | 2016-09-16 09:32:53.570 | new | 9900026005 | image/jpeg | ube305149.jpg | logo | 3 |
+----+-----------+-------------------------+--------------+--------------+-----------------+---------------+--------------+------------+
| 1 | 4 | 2016-09-16 09:32:53.570 | new | 9900026005 | image/jpeg | ube108453.jpg | others | 4 |
+----+-----------+-------------------------+--------------+--------------+-----------------+---------------+--------------+------------+
| 1 | 5 | 2016-09-16 09:32:53.570 | new | 9900026005 | application/pdf | ube007100.pdf | others | 5 |
+----+-----------+-------------------------+--------------+--------------+-----------------+---------------+--------------+------------+

XML parsing error when trying to read node values of XML

I have a XML file(TestArticles.xml), which I need to import into SQL SERVER 2014 and read the data from the various nodes and insert it into few other tables in the same database.
TestArticles.xml
<?xml version="1.0" encoding="UTF-8"?>
<Articles>
<sv:node sv:name="test1"
xmlns:sv="http://www.jcp.org/jcr/sv/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<sv:property sv:name="jcr:primaryType" sv:type="Name">
<sv:value>mgnl:tax-article</sv:value>
</sv:property>
<sv:property sv:name="jcr:createdBy" sv:type="String">
<sv:value>system</sv:value>
</sv:property>
</sv:node>
<sv:node sv:name="test2"
xmlns:sv="http://www.jcp.org/jcr/sv/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<sv:property sv:name="jcr:primaryType" sv:type="Name">
<sv:value>mgnl:tax-article</sv:value>
</sv:property>
<sv:property sv:name="jcr:createdBy" sv:type="String">
<sv:value>admin</sv:value>
</sv:property>
</sv:node>
</Articles>
I tried the following steps:
Import XML data from an XML file into SQL Server table using the OPENROWSET function
USE DataMigration
GO
CREATE TABLE ArticlesXML
(
Id INT IDENTITY PRIMARY KEY,
ArticlesXMLData XML,
LoadedDateTime DATETIME
)
INSERT INTO ArticlesXML(ArticlesXMLData, LoadedDateTime)
SELECT CONVERT(XML, BulkColumn) AS BulkColumn, GETDATE()
FROM OPENROWSET(BULK 'C:\Temp\articles.xml', SINGLE_BLOB) AS x;
SELECT * FROM ArticlesXML
Parsing the XML data using the OPENXML function
USE DataMigration
GO
DECLARE #XML AS XML, #hDoc AS INT, #SQL NVARCHAR (MAX)
SELECT #XML = ArticlesXMLData FROM ArticlesXML
EXEC sp_xml_preparedocument #hDoc OUTPUT, #XML
SELECT ArticleName
FROM OPENXML(#hDoc, 'Articles/sv:node')
WITH
(
ArticleName [varchar](100) '#sv:name'
)
EXEC sp_xml_removedocument #hDoc
GO
On executing the above query, I am getting the below mentioned error:
Msg 6603, Level 16, State 2, Line 14
XML parsing error: Reference to undeclared namespace prefix: 'sv'.
I want to fetch the following from the TestArticles.xml
sv:name from each sv:node
sv:value from each sv:property node within the sv:node
Can anyone please help me to resolve this issue?
With this query you can read all data within your XML:
CREATE TABLE ArticlesXML
(
Id INT IDENTITY PRIMARY KEY,
ArticlesXMLData XML,
LoadedDateTime DATETIME
)
GO
SET IDENTITY_INSERT ArticlesXML ON;
INSERT INTO ArticlesXML(Id,ArticlesXMLData,LoadedDateTime) VALUES
(1,
'<?xml version="1.0" encoding="UTF-8"?>
<Articles>
<sv:node sv:name="test1"
xmlns:sv="http://www.jcp.org/jcr/sv/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<sv:property sv:name="jcr:primaryType" sv:type="Name">
<sv:value>mgnl:tax-article</sv:value>
</sv:property>
<sv:property sv:name="jcr:createdBy" sv:type="String">
<sv:value>system</sv:value>
</sv:property>
</sv:node>
<sv:node sv:name="test2"
xmlns:sv="http://www.jcp.org/jcr/sv/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<sv:property sv:name="jcr:primaryType" sv:type="Name">
<sv:value>mgnl:tax-article</sv:value>
</sv:property>
<sv:property sv:name="jcr:createdBy" sv:type="String">
<sv:value>admin</sv:value>
</sv:property>
</sv:node>
</Articles>',GETDATE());
SET IDENTITY_INSERT ArticlesXML OFF;
WITH XMLNAMESPACES('http://www.jcp.org/jcr/sv/1.0' AS sv
,'http://www.w3.org/2001/XMLSchema-instance' AS xsi)
SELECT Id
,Article.value('#sv:name','varchar(max)') AS ArticleName
,Property.value('#sv:name','varchar(max)') AS PropertyName
,Property.value('#sv:type','varchar(max)') AS PropertyType
,Property.value('sv:value[1]','varchar(max)') AS PropertyValue
,LoadedDateTime
FROM ArticlesXML
CROSS APPLY ArticlesXML.ArticlesXMLData.nodes('/Articles/sv:node') A(Article)
CROSS APPLY A.Article.nodes('sv:property') AS B(Property);
--CleanUp
--DROP TABLE ArticlesXML;
The Result
+----+-------------+-----------------+--------------+------------------+-------------------------+
| Id | ArticleName | PropertyName | PropertyType | PropertyValue | LoadedDateTime |
+----+-------------+-----------------+--------------+------------------+-------------------------+
| 1 | test1 | jcr:primaryType | Name | mgnl:tax-article | 2016-03-31 13:52:26.753 |
+----+-------------+-----------------+--------------+------------------+-------------------------+
| 1 | test1 | jcr:createdBy | String | system | 2016-03-31 13:52:26.753 |
+----+-------------+-----------------+--------------+------------------+-------------------------+
| 1 | test2 | jcr:primaryType | Name | mgnl:tax-article | 2016-03-31 13:52:26.753 |
+----+-------------+-----------------+--------------+------------------+-------------------------+
| 1 | test2 | jcr:createdBy | String | admin | 2016-03-31 13:52:26.753 |
+----+-------------+-----------------+--------------+------------------+-------------------------+
If you want to query this somehow (filter, aggregation...) you have several chances:
put the result into a temp table and use this however you like
same with a declared table variable
Surround and use it as CTE
(This looks like this)
WITH XMLNAMESPACES('http://www.jcp.org/jcr/sv/1.0' AS sv
,'http://www.w3.org/2001/XMLSchema-instance' AS xsi)
,TableDataCTE AS
(
SELECT Id
,Article.value('#sv:name','varchar(max)') AS ArticleName
,Property.value('#sv:name','varchar(max)') AS PropertyName
,Property.value('#sv:type','varchar(max)') AS PropertyType
,Property.value('sv:value[1]','varchar(max)') AS PropertyValue
,LoadedDateTime
FROM ArticlesXML
CROSS APPLY ArticlesXML.ArticlesXMLData.nodes('/Articles/sv:node') A(Article)
CROSS APPLY A.Article.nodes('sv:property') AS B(Property)
)
SELECT * FROM TableDataCTE;
WHERE ...
or you might use XQuery predicats ( e.g. .nodes('/Articles/sv:node[sv:name="test1"]') )
EDIT The enhanced SELECT to reflect your need to read multi-value properties according to the sampe you gave in your comment:
WITH XMLNAMESPACES('http://www.jcp.org/jcr/sv/1.0' AS sv
,'http://www.w3.org/2001/XMLSchema-instance' AS xsi)
SELECT Id
,Article.value('#sv:name','varchar(max)') AS ArticleName
,Property.value('#sv:name','varchar(max)') AS PropertyName
,Property.value('#sv:type','varchar(max)') AS PropertyType
,Value.value('.','varchar(max)') AS PropertyValue
,LoadedDateTime
FROM ArticlesXML
CROSS APPLY ArticlesXML.ArticlesXMLData.nodes('/Articles/sv:node') A(Article)
CROSS APPLY A.Article.nodes('sv:property') AS B(Property)
CROSS APPLY B.Property.nodes('sv:value') AS C(Value);
The result:
+----+-------------+-----------------+--------------+------------------+-------------------------+
| Id | ArticleName | PropertyName | PropertyType | PropertyValue | LoadedDateTime |
+----+-------------+-----------------+--------------+------------------+-------------------------+
| 1 | test1 | jcr:primaryType | Name | mgnl:tax-article | 2016-03-31 20:30:27.240 |
+----+-------------+-----------------+--------------+------------------+-------------------------+
| 1 | test1 | jcr:createdBy | String | system | 2016-03-31 20:30:27.240 |
+----+-------------+-----------------+--------------+------------------+-------------------------+
| 1 | test2 | jcr:primaryType | Name | mgnl:tax-article | 2016-03-31 20:30:27.240 |
+----+-------------+-----------------+--------------+------------------+-------------------------+
| 1 | test2 | jcr:createdBy | String | admin | 2016-03-31 20:30:27.240 |
+----+-------------+-----------------+--------------+------------------+-------------------------+
| 1 | test3 | jcr:createdBy | String | admin1 | 2016-03-31 20:30:27.240 |
+----+-------------+-----------------+--------------+------------------+-------------------------+
| 1 | test3 | jcr:createdBy | String | admin2 | 2016-03-31 20:30:27.240 |
+----+-------------+-----------------+--------------+------------------+-------------------------+
| 1 | test3 | jcr:createdBy | String | admin3 | 2016-03-31 20:30:27.240 |
+----+-------------+-----------------+--------------+------------------+-------------------------+
| 1 | test3 | jcr:createdBy | String | admin4 | 2016-03-31 20:30:27.240 |
+----+-------------+-----------------+--------------+------------------+-------------------------+
| 1 | test3 | jcr:createdBy | String | admin5 | 2016-03-31 20:30:27.240 |
+----+-------------+-----------------+--------------+------------------+-------------------------+
| 1 | test3 | jcr:createdBy | String | admin6 | 2016-03-31 20:30:27.240 |
+----+-------------+-----------------+--------------+------------------+-------------------------+
You need to explicitly define the prefix first, for example, using WITH XMLNAMESPACES and XQuery :
;WITH XMLNAMESPACES('http://www.jcp.org/jcr/sv/1.0' as sv)
SELECT c.value('#sv:name', 'varchar(100)') As Name
FROM ArticlesXML a
CROSS APPLY ArticlesXMLData.nodes('Articles/sv:node') t(c)

Convert XML Nodes To Rows in SQL Server

Consider the below sample:
declare #somexml as xml
set #somexml = '
<Settings>
<Users>
<ID>1</ID>
<ID>2</ID>
<ID>3</ID>
<ID>4</ID>
<ID>5</ID>
</Users>
</Settings>'
The above XML has some ID values that I need to convert to rows of data that can be used in a temp table to perform joins against.
I can't quite get the syntax correct, I've tried a number of samples that I've come across:
SELECT T.r.value('.','int') as id
FROM #somexml.nodes('/Settings/Users') T(r)
Returns:
|ID |
|------|
|12345 |
The following:
SELECT T.r.query('.') as id
from #somexml.nodes('/Settings/Users/ID') as T(r)
Returns:
|ID |
|-----------|
|<ID>1</ID> |
|<ID>2</ID> |
|<ID>3</ID> |
|<ID>4</ID> |
|<ID>5</ID> |
I'm getting close, but I want to remove the XML tags to just leave the ID values like so:
|ID |
|---|
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
Here's a fiddle if you want to play/solve that way: SQL Fiddle
Any help is appreciated as always.
Replace T.r.query('.') as id with T.r.value('.', 'INT') as id
SELECT T.r.value('.','int') as id
FROM #somexml.nodes('/Settings/Users/ID') T(r)

Resources