Convert Xml to Table SQL Server - sql

I wonder how can i read a xml data and transform it to a table in TSQL?
For example:
<row>
<IdInvernadero>8</IdInvernadero>
<IdProducto>3</IdProducto>
<IdCaracteristica1>8</IdCaracteristica1>
<IdCaracteristica2>8</IdCaracteristica2>
<Cantidad>25</Cantidad>
<Folio>4568457</Folio>
</row>
<row>
<IdInvernadero>3</IdInvernadero>
<IdProducto>3</IdProducto>
<IdCaracteristica1>1</IdCaracteristica1>
<IdCaracteristica2>2</IdCaracteristica2>
<Cantidad>72</Cantidad>
<Folio>4568457</Folio>
</row>
To
8 3 8 8 25 4568457
3 3 1 2 72 4568457

This is the answer, hope it helps someone :)
First there are two variations on how the xml can be written:
1
<row>
<IdInvernadero>8</IdInvernadero>
<IdProducto>3</IdProducto>
<IdCaracteristica1>8</IdCaracteristica1>
<IdCaracteristica2>8</IdCaracteristica2>
<Cantidad>25</Cantidad>
<Folio>4568457</Folio>
</row>
<row>
<IdInvernadero>3</IdInvernadero>
<IdProducto>3</IdProducto>
<IdCaracteristica1>1</IdCaracteristica1>
<IdCaracteristica2>2</IdCaracteristica2>
<Cantidad>72</Cantidad>
<Folio>4568457</Folio>
</row>
Answer:
SELECT
Tbl.Col.value('IdInvernadero[1]', 'smallint'),
Tbl.Col.value('IdProducto[1]', 'smallint'),
Tbl.Col.value('IdCaracteristica1[1]', 'smallint'),
Tbl.Col.value('IdCaracteristica2[1]', 'smallint'),
Tbl.Col.value('Cantidad[1]', 'int'),
Tbl.Col.value('Folio[1]', 'varchar(7)')
FROM #xml.nodes('//row') Tbl(Col)
2.
<row IdInvernadero="8" IdProducto="3" IdCaracteristica1="8" IdCaracteristica2="8" Cantidad ="25" Folio="4568457" />
<row IdInvernadero="3" IdProducto="3" IdCaracteristica1="1" IdCaracteristica2="2" Cantidad ="72" Folio="4568457" />
Answer:
SELECT
Tbl.Col.value('#IdInvernadero', 'smallint'),
Tbl.Col.value('#IdProducto', 'smallint'),
Tbl.Col.value('#IdCaracteristica1', 'smallint'),
Tbl.Col.value('#IdCaracteristica2', 'smallint'),
Tbl.Col.value('#Cantidad', 'int'),
Tbl.Col.value('#Folio', 'varchar(7)')
FROM #xml.nodes('//row') Tbl(Col)
Taken from:
http://kennyshu.blogspot.com/2007/12/convert-xml-file-to-table-in-sql-2005.html
http://msdn.microsoft.com/en-us/library/ms345117(SQL.90).aspx

The sp_xml_preparedocument stored procedure will parse the XML and the OPENXML rowset provider will show you a relational view of the XML data.
For details and more examples check the OPENXML documentation.
As for your question,
DECLARE #XML XML
SET #XML = '<rows><row>
<IdInvernadero>8</IdInvernadero>
<IdProducto>3</IdProducto>
<IdCaracteristica1>8</IdCaracteristica1>
<IdCaracteristica2>8</IdCaracteristica2>
<Cantidad>25</Cantidad>
<Folio>4568457</Folio>
</row>
<row>
<IdInvernadero>3</IdInvernadero>
<IdProducto>3</IdProducto>
<IdCaracteristica1>1</IdCaracteristica1>
<IdCaracteristica2>2</IdCaracteristica2>
<Cantidad>72</Cantidad>
<Folio>4568457</Folio>
</row></rows>'
DECLARE #handle INT
DECLARE #PrepareXmlStatus INT
EXEC #PrepareXmlStatus= sp_xml_preparedocument #handle OUTPUT, #XML
SELECT *
FROM OPENXML(#handle, '/rows/row', 2)
WITH (
IdInvernadero INT,
IdProducto INT,
IdCaracteristica1 INT,
IdCaracteristica2 INT,
Cantidad INT,
Folio INT
)
EXEC sp_xml_removedocument #handle

Related

select field's string sql

I have a column data structure like this, it can more than one row, more then one value, i want to get the deliveryID's value from this field, is there any way to do it?
i have a look of nodes and xmlpath, value()
is there a elegant way to do it? thanks very much
<row>
<value id="1ae95d67-599e-4ab6-9ffd-08d4d90ab608" display-name="Cardholder_id" data-type="Int32">17</value>
<value id="1ae95d67-599e-4ab6-9ffd-08d4d90ab608" display-name="Cardholder_id" data-type="Int32">17</value>
<value id="eb71fd46-f0b2-401d-9775-08d4d90ab608" display-name="Card_Number">3083 2614 5022 21321</value>
<value id="4fc261b2-f776-4fd4-8e1d-08d4d90ab608" display-name="Email_Address">jello#anc.com</value>
<value id="c867d4e5-cc0b-4ee6-b911-08d6134132e0" display-name="BP_TRIGGERS_2.0">{"transactions":[{"BP_CommsRef":"V0001","BP_Offer_Expiry":"2018-10-01T00:00:00","deliveryId":"20320925","Job_Number":"A34F443","Send_Date":"2018-09-26T00:00:00"}]}</value>
</row>
If you have SQL Server 2016 or above you can use OPENXMLand JSON_VALUE
DECLARE #XML XML ='<row>
<value id="1ae95d67-599e-4ab6-9ffd-08d4d90ab608" display-name="Cardholder_id" data-type="Int32">17</value>
<value id="1ae95d67-599e-4ab6-9ffd-08d4d90ab608" display-name="Cardholder_id" data-type="Int32">17</value>
<value id="eb71fd46-f0b2-401d-9775-08d4d90ab608" display-name="Card_Number">3083 2614 5022 21321</value>
<value id="4fc261b2-f776-4fd4-8e1d-08d4d90ab608" display-name="Email_Address">jello#anc.com</value>
<value id="c867d4e5-cc0b-4ee6-b911-08d6134132e0" display-name="BP_TRIGGERS_2.0">{"transactions":[{"BP_CommsRef":"V0001","BP_Offer_Expiry":"2018-10-01T00:00:00","deliveryId":"20320925","Job_Number":"A34F443","Send_Date":"2018-09-26T00:00:00"}]}</value>
</row>'
DECLARE #idoc int
EXEC sp_xml_preparedocument #idoc OUTPUT, #XML;
SELECT id, [display-name],
CASE WHEN ISJSON(JsonText) > 0 THEN JSON_VALUE(JsonText, '$.transactions[0].deliveryId') END deliveryId
, JsonText
FROM
OPENXML(#idoc,'row/value')
WITH (id uniqueidentifier,
[display-name] varchar(20),
JsonText varchar(max) '.' )
Result:
id display-name deliveryId JsonText
------------------------------------ -------------------- ------------------ --------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1AE95D67-599E-4AB6-9FFD-08D4D90AB608 Cardholder_id NULL 17
1AE95D67-599E-4AB6-9FFD-08D4D90AB608 Cardholder_id NULL 17
EB71FD46-F0B2-401D-9775-08D4D90AB608 Card_Number NULL 3083 2614 5022 21321
4FC261B2-F776-4FD4-8E1D-08D4D90AB608 Email_Address NULL jello#anc.com
C867D4E5-CC0B-4EE6-B911-08D6134132E0 BP_TRIGGERS_2.0 20320925 {"transactions":[{"BP_CommsRef":"V0001","BP_Offer_Expiry":"2018-10-01T00:00:00","deliveryId":"20320925","Job_Number":"A34F443","Send_Date":"2018-09-26T00:00:00"}]}
You can also use this syntax.
SELECT
RowValue.value('#id', 'uniqueidentifier') id,
CASE WHEN ISJSON(RowValue.value('.', 'VARCHAR(max)') ) > 0
THEN JSON_VALUE(RowValue.value('.', 'VARCHAR(max)'), '$.transactions[0].deliveryId')
END AS deliveryId
FROM
(VALUES(#XML)) AS T (XmlValue)
CROSS APPLY
T.XmlValue.nodes('row/value') AS XT(RowValue)

How read xml data from a xml file without tags in SQL Server?

I know how to read the xml data from a file where the info is organized in tags, I mean a file like this:
<?xml version='1.0' encoding='UTF-8'?>
<dataset>
<Administrador>
<id> 8 </id>
<nombre> Nelle </nombre>
<valorDocId> 8399335355 </valorDocId>
<contrasenna> Glenn </contrasenna>
</Administrador>
<Administrador>
<id> 9 </id>
<nombre> Gayler </nombre>
<valorDocId> 1310348693 </valorDocId>
<contrasenna> Madonna </contrasenna>
</Administrador>
</dataset>
The code I used to read it is:
use Proyecto1
declare #filedata XML
select #filedata=BulkColumn from OpenRowSet(Bulk'File directory', Single_blob) x;
insert into Table(id, nombre, valorDocId, clave)
select
xData.value('id[1]', 'int') id,
xData.value('nombre[1]','varchar(30)') nombre,
xData.value('valorDocId[1]','int') valorDocId,
xData.value('contrasenna[1]','varchar(20)') clave
from #fileData.nodes('/dataset/Administrador') as
x(xData)
But now I need to read a xml file that is not organized in tags, at least not like the last one, the xml is like this:
<?xml version='1.0' encoding='UTF-8'?>
<dataset>
<Administrador id="1" nombre="Nelle" valorDocId="8399335355" contrasenna="Glenn"/>
<Administrador id="2" nombre="Gayler" valorDocId="1310348693" contrasenna="Madonna"/>
</dataset>
But the code I used before doesn't works, it throws an error that says that I can't insert a NULL value in the column 'id', so what I supposed is that the data is not being read. So how can I read that second file?
Example
Declare #XML xml = '
<dataset>
<Administrador id="1" nombre="Nelle" valorDocId="8399335355" contrasenna="Glenn"/>
<Administrador id="2" nombre="Gayler" valorDocId="1310348693" contrasenna="Madonna"/>
</dataset>
'
Select id = x.v.value('#id','int')
,nombre = x.v.value('#nombre','varchar(50)')
,valorDocId = x.v.value('#valorDocId','varchar(50)')
,contrasenna = x.v.value('#contrasenna','varchar(50)')
From #Xml.nodes('dataset/Administrador') x(v)
Returns
id nombre valorDocId contrasenna
1 Nelle 8399335355 Glenn
2 Gayler 1310348693 Madonna
EDIT - To Get your XML from a File
Declare #XML xml
Select #XML = BulkColumn FROM OPENROWSET(BULK 'C:\Working\SomeXMLFile.xml', SINGLE_BLOB) x;
Select id = x.v.value('#id','int')
,nombre = x.v.value('#nombre','varchar(50)')
,valorDocId = x.v.value('#valorDocId','varchar(50)')
,contrasenna = x.v.value('#contrasenna','varchar(50)')
From #Xml.nodes('dataset/Administrador') x(v)

XML to SQL - SQL Server

I'm traversing an XML file to read nodes and fill into to SQL Server tables. I have a Root node having Department node which further may have one or more as element. I want to select all the possible values from in a SQL result set.
Please find below XML I'm referring:
DECLARE #x XML='
<Root>
<Department>
<DeptID>D101</DeptID>
<DeptID>D102</DeptID>
</Department>
</Root>'
I'm using below SQL Query to get the data from XML but I can read only first DeptID as I'm passing [1] inside DeptID[1]. If I pass [2] I can get thee second value. But in real life scenario, I won't be able to know how many DeptID would be there in the XML. So I want a generic script to read as many as DeptIDs comes in XML.
SELECT n.value('DeptID[1]','varchar(10)') AS DeptID FROM #x.nodes('/Root/Department') R(n)
You can use OpenXMl method of sql server to get more elements in table as follows.
Step 1: Suppose this is your sample XML data.
DECLARE #XML XML='
<ROOT>
<Customers>
<Customer CustomerID="C001" CustomerName="Arshad Ali">
<Orders>
<Order OrderID="10248" OrderDate="2012-07-04T00:00:00">
<OrderDetail ProductID="10" Quantity="5" />
<OrderDetail ProductID="11" Quantity="12" />
<OrderDetail ProductID="42" Quantity="10" />
</Order>
</Orders>
<Address> Address line 1, 2, 3</Address>
</Customer>
<Customer CustomerID="C002" CustomerName="Paul Henriot">
<Orders>
<Order OrderID="10245" OrderDate="2011-07-04T00:00:00">
<OrderDetail ProductID="11" Quantity="12" />
<OrderDetail ProductID="42" Quantity="10" />
</Order>
</Orders>
<Address> Address line 5, 6, 7</Address>
</Customer>
<Customer CustomerID="C003" CustomerName="Carlos Gonzlez">
<Orders>
<Order OrderID="10283" OrderDate="2012-08-16T00:00:00">
<OrderDetail ProductID="72" Quantity="3" />
</Order>
</Orders>
<Address> Address line 1, 4, 5</Address>
</Customer>
</Customers>
</ROOT>'
Step 2: Use of OPENXML method to get elements at any level as follows.
DECLARE #hDoc AS INT, #SQL NVARCHAR (MAX)
EXEC sp_xml_preparedocument #hDoc OUTPUT, #XML
SELECT CustomerID, CustomerName, Address, OrderID, OrderDate, ProductID, Quantity
FROM OPENXML(#hDoc, 'ROOT/Customers/Customer/Orders/Order/OrderDetail')
WITH
(
CustomerID [varchar](50) '../../../#CustomerID',
CustomerName [varchar](100) '../../../#CustomerName',
Address [varchar](100) '../../../Address',
OrderID [varchar](1000) '../#OrderID',
OrderDate datetime '../#OrderDate',
ProductID [varchar](50) '#ProductID',
Quantity int '#Quantity'
)
EXEC sp_xml_removedocument #hDoc
GO
Above steps will give you following Output.
Try it like this
DECLARE #x XML='
<Root>
<Department>
<DeptID>D101</DeptID>
<DeptID>D102</DeptID>
</Department>
</Root>';
SELECT d.value('text()[1]','varchar(10)') AS DeptID
FROM #x.nodes('/Root/Department/DeptID') A(d);
Your own code
SELECT n.value('DeptID[1]','varchar(10)') AS DeptID
FROM #x.nodes('/Root/Department') R(n)
... follows the right idea. But .nodes() must return the repeating element, which is <DeptID>. Your approach is looking for the first <DeptID> within <Department> actually

Extracting data from XML Nodes

I am testing this simple script and just wondering why it didn't work.
DECLARE #myDoc xml
DECLARE #ProdID int
SET #myDoc =
'<Datatype xmlns="V8_0_1" id="113" name="PF_CleaningMode" hdl="47/4/SB8_3-910-8243-19/0/113" odobjid="915">
<Datatypevalue id="2" name="Intermittent" />
<Datatypevalue id="1" name="Continuous" />
<Datatypevalue id="0" name="Off" />
</Datatype>'
SET #ProdID = #myDoc.value('(Datatype/#id)[1]', 'int' )
SELECT #ProdID
Hope you can assist. Thanks.
You're just simply ignoring the default XML namespace that's defined on your <Datatype> node:
<Datatype xmlns="V8_0_1" id="113" name="PF_CleaningMode"
**************
You need to include that in your query!
Like this:
;WITH XMLNAMESPACES(DEFAULT 'V8_0_1')
SELECT #myDoc.value('(Datatype/#id)[1]', 'INT')
and you'll get the expected output of
113

openxml with asynchron structure

I am working with MS SQL Server 2012.
I am trying to use the function openxml with the following statement / xml data:
DECLARE #XML AS XML, #hDoc AS INT, #SQL NVARCHAR (MAX)
SET #SQL = '<master>
<List>
<Col>
<DisplayFieldName>Peter</DisplayFieldName>
<Value>
<string>Yes</string>
</Value>
</Col>
<Col>
<DisplayFieldName>Tom</DisplayFieldName>
<Value>
<string>No</string>
</Value>
</Col>
<Col>
<DisplayFieldName>Numerics</DisplayFieldName>
<Value>
<string>50 </string>
<string>100 </string>
<string>150 </string>
<string>200 </string>
</Value>
</Col>
</List>
</master>'
SELECT #XML = CONVERT(XML,#SQL)
EXEC sp_xml_preparedocument #hDoc OUTPUT, #XML
SELECT *
FROM OPENXML(#hDoc, '/master/List/Col/Value',3)
WITH
(
string [varchar](max)
)
EXEC sp_xml_removedocument #hDoc
The result is this:
string
1 Yes
2 No
3 50
I understand why this happens, but actually I would like to display just all numerical values (50,100,150,200) or at least all values in the field (Yes, No, 50, 100, 150, 200) which also would be sufficient.
FROM OPENXML is outdated and should not be used any more (There are some rare exceptions...)
You should rather use the XML-methods like .value(),.nodes(),.query() and .exist().
Give this a try: It is fully inlined (ad-hoc), easier to read and maintain - and much faster:
DECLARE #XML AS XML;
SET #XML = '<master>
<List>
<Col>
<DisplayFieldName>Peter</DisplayFieldName>
<Value>
<string>Yes</string>
</Value>
</Col>
<Col>
<DisplayFieldName>Tom</DisplayFieldName>
<Value>
<string>No</string>
</Value>
</Col>
<Col>
<DisplayFieldName>Numerics</DisplayFieldName>
<Value>
<string>50 </string>
<string>100 </string>
<string>150 </string>
<string>200 </string>
</Value>
</Col>
</List>
</master>';
SELECT C.value('DisplayFieldName[1]','nvarchar(max)') AS DisplayFieldName
,V.value('.','nvarchar(max)') AS string
FROM #XML.nodes('/master/List/Col') AS A(C)
CROSS APPLY C.nodes('Value/string') AS B(V);
The result
DisplayFieldName string
----------------------------
Peter Yes
Tom No
Numerics 50
Numerics 100
Numerics 150
Numerics 200
UPDATE
If you need the numerics only, you could append the same XQuery-filter to the .nodes() XPath, as pointed by Alex: /master/List/Col[DisplayFieldName="Numerics"]
You can give this a try, might return more than what you are expecting:
SELECT *
FROM OPENXML(#hDoc, '/master/List/Col/Value/*', 3)
WITH
(
string [varchar](max) '.'
)
Make xpath deeper. Filter data. Something like this.
SELECT text string
FROM OPENXML(#hDoc, '/master/List/Col[DisplayFieldName="Numerics"]/Value/string',3)
--WITH
--(
--string [varchar](max)
--)
where text is not null
update
You can also use native xml methods.
select t.v.value('.','varchar(100)') x
from #xml.nodes('master/List/Col[DisplayFieldName="Numerics"]/Value/string') t(v)
In my experience openxml works faster with large texts.