Split xml into smaller pieces - sql-server

I have an xml variable.
DECLARE #xml xml = N'<Items Category="AAA">
<Item Id="1">A</Item>
<Item Id="2">B</Item>
<Item Id="3">C</Item>
<Item Id="4">D</Item>
<Item Id="5">E</Item>
<Item Id="6">F</Item>
<Item Id="7">G</Item>
<Item Id="8">H</Item>
<Item Id="9">I</Item>
</Items>'
Could you help me write a query to split it into several pieces?
<Items Category="AAA">
<Item Id="1">A</Item>
<Item Id="2">B</Item>
<Item Id="3">C</Item>
</Items>
<Items Category="AAA">
<Item Id="4">D</Item>
<Item Id="5">E</Item>
<Item Id="6">F</Item>
</Items>
<Items Category="AAA">
<Item Id="7">G</Item>
<Item Id="8">H</Item>
<Item Id="9">I</Item>
</Items>
I mean XQuery, but any other idea is welcome.

Way to do this via XQuery modify:
DECLARE #xml xml = N'<Items Category="AAA">
<Item Id="1">A</Item>
<Item Id="2">B</Item>
<Item Id="3">C</Item>
<Item Id="4">D</Item>
<Item Id="5">E</Item>
<Item Id="6">F</Item>
<Item Id="7">G</Item>
<Item Id="8">H</Item>
<Item Id="9">I</Item>
</Items>';
DECLARE #temp TABLE (XmlData1 xml, XmlData2 xml, XmlData3 xml);
INSERT #temp VALUES (#xml, #xml, #xml);
UPDATE t
SET XmlData1.modify('delete /Items/Item[not(position() >= 1 and position() < 4)]'),
XmlData2.modify('delete /Items/Item[not(position() >= 4 and position() < 7)]'),
XmlData3.modify('delete /Items/Item[not(position() >= 7 and position() < 10)]')
FROM #temp t;
SELECT * FROM #temp;
Way to do this via FLWOR:
select #xml.query('
<Items Category="AAA">
{
for $item in /Items/Item[position() >= 1 and position() < 4]
return $item
}
</Items>');
You can freely change your split criteria in XPath.

Related

I can't delete empty containers in an xml document on SQL Server, such as "<Item />"

I started working for the first time and I don't know much yet, I've been suffering with this problem for two days.
I have a document of this type:
<Tables>
<dbo.ES_Connection_Modes>
<Item />
</dbo.ES_Connection_Modes>
<dbo.ES_Device_Categories>
<Item>
<LINK>1</LINK>
<F_Class>1</F_Class>
<N_Code>1</N_Code>
<B_Default>1</B_Default>
<B_Meter>1</B_Meter>
<B_Tranf>0</B_Tranf>
<B_Regist>0</B_Regist>
<B_Show_InMenu>1</B_Show_InMenu>
<N_Project>-1</N_Project>
<C_Const>EDC_Meter</C_Const>
</Item>
</dbo.ES_Device_Categories>
<dbo.ES_Indicating_Device_Types>
<Item />
</dbo.ES_Indicating_Device_Types>
<dbo.ES_Operating_Principles>
<Item />
</dbo.ES_Operating_Principles>
<dbo.ES_Precission_Classes>
<Item>
<LINK>7</LINK>
<N_Project>687783</N_Project>
<C_Name>2,0</C_Name>
<C_Const>EPC_20</C_Const>
</Item>
<Item>
<LINK>8</LINK>
<N_Project>687783</N_Project>
<C_Name>2,5</C_Name>
<C_Const>EPC_25</C_Const>
</Item>
</dbo.ES_Precission_Classes>
<dbo.ES_Granularity>
<Item />
</dbo.ES_Granularity>
</Tables>
I need to delete the empty item containers and the container containing it to get a document of the following type:
<Tables>
<dbo.ES_Device_Categories>
<Item>
<LINK>1</LINK>
<F_Class>1</F_Class>
<N_Code>1</N_Code>
<B_Default>1</B_Default>
<B_Meter>1</B_Meter>
<B_Tranf>0</B_Tranf>
<B_Regist>0</B_Regist>
<B_Show_InMenu>1</B_Show_InMenu>
<N_Project>-1</N_Project>
<C_Const>EDC_Meter</C_Const>
</Item>
</dbo.ES_Device_Categories>
<dbo.ES_Precission_Classes>
<Item>
<LINK>7</LINK>
<N_Project>687783</N_Project>
<C_Name>2,0</C_Name>
<C_Const>EPC_20</C_Const>
</Item>
<Item>
<LINK>8</LINK>
<N_Project>687783</N_Project>
<C_Name>2,5</C_Name>
<C_Const>EPC_25</C_Const>
</Item>
</dbo.ES_Precission_Classes>
</Tables>
I've tried a lot of things, deleted them, but here's the way I was hoping for, but I get an error:
SET #myDoc.modify('delete /Tables/*[contains(name(), "null")]');
Please try the following solution.
It is deleting 2nd level XML elements under the root (Tables) that have in turn Item elements without children.
SQL
DECLARE #myDoc XML =
N'<Tables>
<dbo.ES_Connection_Modes>
<Item/>
</dbo.ES_Connection_Modes>
<dbo.ES_Device_Categories>
<Item>
<LINK>1</LINK>
<F_Class>1</F_Class>
<N_Code>1</N_Code>
<B_Default>1</B_Default>
<B_Meter>1</B_Meter>
<B_Tranf>0</B_Tranf>
<B_Regist>0</B_Regist>
<B_Show_InMenu>1</B_Show_InMenu>
<N_Project>-1</N_Project>
<C_Const>EDC_Meter</C_Const>
</Item>
</dbo.ES_Device_Categories>
<dbo.ES_Indicating_Device_Types>
<Item/>
</dbo.ES_Indicating_Device_Types>
<dbo.ES_Operating_Principles>
<Item/>
</dbo.ES_Operating_Principles>
<dbo.ES_Precission_Classes>
<Item>
<LINK>7</LINK>
<N_Project>687783</N_Project>
<C_Name>2,0</C_Name>
<C_Const>EPC_20</C_Const>
</Item>
<Item>
<LINK>8</LINK>
<N_Project>687783</N_Project>
<C_Name>2,5</C_Name>
<C_Const>EPC_25</C_Const>
</Item>
</dbo.ES_Precission_Classes>
<dbo.ES_Granularity>
<Item/>
</dbo.ES_Granularity>
</Tables>';
SET #myDoc.modify('delete /Tables/*[not(Item/*)]');
-- test
SELECT #myDoc;

Parse XML file using SQL Server, and return child node section as XML

I have an XML file that looks like this:
<NodeA Type="A" Date="2015-10-28">
<NodeB Amount="0.00">
<Items />
</NodeB>
<NodeB Amount="0.00">
<Items>
<Item code="2" val="50.00" />
<Item code="8" val="50.00" />
</Items>
</NodeB>
</NodeA>
I am trying to use SQL Server and XPath to extract the child nodes of NodeA as XML.
Based on the above XML, I need to return a table with 2 rows, containing the following fields:
Type (nvarchar) | Date (Date) | ChildNode (XML)
-----------------------------------------------
A 2015-10-28 <NodeB Amount="0.00"><Items /></NodeB>
A 2015-10-28 <NodeB Amount="0.00"><Items>...</NodeB>
I know I can do this using C#, but is there a way to do this using XPath? I've some success with xPath returning the field values from NodeA, but can't seem to get NodeB to display as XML.
Try this -
DECLARE #xml AS XML
SET #xml = '<NodeA Type="A" Date="2015-10-28">
<NodeB Amount="0.00">
<Items />
</NodeB>
<NodeB Amount="0.00">
<Items>
<Item code="2" val="50.00" />
<Item code="8" val="50.00" />
</Items>
</NodeB>
</NodeA>'
SELECT c.value('(/NodeA/#Type)[1]', 'varchar(50)') AS Type,
c.value('(/NodeA/#Date)[1]', 'varchar(50)') AS Date,
c.query('.') AS ChildNodeXML
FROM #xml.nodes('/NodeA/NodeB') as T(C)
Result
Type Date ChildNodeXML
---- ------ ----------------------------------
A 2015-10-28 <NodeB Amount="0.00"><Items /></NodeB>
A 2015-10-28 <NodeB Amount="0.00"><Items><Item code="2" val="50.00" /><Item code="8" val="50.00" /></Items></NodeB>

Empty results when filtering based on position in XQuery

I tried to print the details of 3rd and 4th products in the catalog, but I am getting empty results.
XML input:
<catalog>
<product dept="WMN">
<number>557</number>
<name language="en">Fleece Pullover</name>
<colorChoices>navy black</colorChoices>
</product>
<product dept="ACC">
<number>563</number>
<name language="en">Floppy Sun Hat</name>
</product>
<product dept="ACC">
<number>443</number>
<name language="en">Deluxe Travel Bag</name>
</product>
<product dept="MEN">
<number>784</number>
<name language="en">Cotton Dress Shirt</name>
<colorChoices>white gray</colorChoices>
<desc>Our <i>favorite</i> shirt!</desc>
</product>
</catalog>
what I tried:
SELECT xDoc.query(' for $prod in //product
let $x:=$prod
return (<Item>{data($x[3])}{data($x[4])}</Item>)')
FROM AdminDocs
where id=6
Output displayed:
<Item />
<Item />
<Item />
<Item />
This defines a loop over all products:
for $prod in //product
For each of those products, you return the third and fourth node out of a sequence of a single product, which obviously will yield empty nodes:
let $x:=$prod
return (<Item>{data($x[3])}{data($x[4])}</Item>)
For a better understanding, return the whole product node each time:
for $prod in //product
return <item>{ $prod }</item>
Which will result in something like this:
<item>
<product dept="WMN">
<number>557</number>
<name language="en">Fleece Pullover</name>
<colorChoices>navy black</colorChoices>
</product>
</item>
<item>
<product dept="ACC">
<number>563</number>
<name language="en">Floppy Sun Hat</name>
</product>
</item>
<item>
<product dept="ACC">
<number>443</number>
<name language="en">Deluxe Travel Bag</name>
</product>
</item>
<item>
<product dept="MEN">
<number>784</number>
<name language="en">Cotton Dress Shirt</name>
<colorChoices>white gray</colorChoices>
<desc>Our <i>favorite</i> shirt!</desc>
</product>
</item>
And each of those <product/> elements being a $prod[1]. Try:
for $prod in //product
return <item>{ $prod[1] }</item>
which will return the exactly same result, compared to
for $prod in //product
return <item>{ $prod[2] }</item>
(or any other positional predicate) resulting in empty <item/> nodes.
Finally, to return only the third and fourth products, shift the positional predicate into the for loop argument:
for $prod in //product[position() = 3 or position() = 4]
return <item>{ $prod }</item>
(and apply data(...) as needed, but I'd guess it won't yield the results you expect here).

How to Insert xml data into SQL Server table?

How to import below XML data into SQL Server table with three columns?
<dataset>
<metadata>
<item name="NAME_LAST" type="xs:string" length="62" />
<item name="NAME_FIRST" type="xs:string" length="62" />
<item name="NAME_MIDDLE" type="xs:string" length="32" />
</metadata>
<data>
<row>
<value>SMITH</value>
<value>MARY</value>
<value>N</value>
</row>
<row>
<value>SMITH2</value>
<value>MARY2</value>
<value>N2</value>
</row>
</data>
</dataset>
Try this:
DECLARE #input XML = '<dataset>
<metadata>
<item name="NAME_LAST" type="xs:string" length="62" />
<item name="NAME_FIRST" type="xs:string" length="62" />
<item name="NAME_MIDDLE" type="xs:string" length="32" />
</metadata>
<data>
<row>
<value>SMITH</value>
<value>MARY</value>
<value>N</value>
</row>
<row>
<value>SMITH2</value>
<value>MARY2</value>
<value>N2</value>
</row>
</data>
</dataset>'
INSERT INTO dbo.YourTable(ColName, ColFirstName, ColOther)
SELECT
Name = XCol.value('(value)[1]','varchar(25)'),
FirstName = XCol.value('(value)[2]','varchar(25)'),
OtherValue = XCol.value('(value)[3]','varchar(25)')
FROM
#input.nodes('/dataset/data/row') AS XTbl(XCol)
Insert XML Data into sql Server table
Declare #retValue1 varchar(50);
Declare #XmlStr XML;
SET #XmlStr='<Customers>
<customer>
<ID>111589</ID>
<FirstName>name1</FirstName>
<LastName>Lname1</LastName>
<Company>ABC</Company>
</customer>
<customer>
<ID>12345</ID>
<FirstName>name2</FirstName>
<LastName>Lname2</LastName>
<Company>ABC</Company>
</customer>
<customer>
<ID>14567</ID>
<FirstName>name3</FirstName>
<LastName>Lname3</LastName>
<Company>DEF</Company>
</customer>
</Customers>';
#retValue='Failed';
INSERT INTO [test_xmlinsert](
[id],
[firstName],
[lastName],
[company]
)
SELECT
COALESCE([Table].[Column].value('ID[1]', 'int'),0) as 'ID',
[Table].[Column].value('FirstName [1]', 'varchar(50)') as ' FirstName ',
[Table].[Column].value(' LastName[1]', 'varchar(50)') as ' LastName',
[Table].[Column].value(' Company [1]', 'varchar(50)') as ' Company'
FROM #XmlStr.nodes('/ Customers / customer') as [Table]([Column])
IF(##ROWCOUNT > 0 )
SET #retValue='SUCCESS';

Get XML output from SQL query [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 8 years ago.
Improve this question
I am writing SQL query to get output as below . I am not using any tables from database.
A query to get the desired output as
<Property ID="0" Name="Color" NumOfItems="11">
<Item ID="0" Name="D"/>
<Item ID="1" Name="E"/>
<Item ID="2" Name="F"/>
<Item ID="3" Name="G"/>
<Item ID="4" Name="H"/>
<Item ID="5" Name="I"/>
<Item ID="6" Name="J"/>
<Item ID="7" Name="K"/>
<Item ID="8" Name="L"/>
<Item ID="9" Name="M"/>
<Item ID="10" Name="N"/>
</Property>
I tried as this
SELECT
(
SELECT '0' AS 'Item/#ID','D' AS 'Item/#Name' FOR XML PATH(''), TYPE ),
(SELECT '1' AS 'Item/#ID','E' AS 'Item/#Name' FOR XML PATH(''), TYPE ),
(SELECT '2' AS 'Item/#ID','F' AS 'Item/#Name' FOR XML PATH(''), TYPE ),
(SELECT '3' AS 'Item/#ID','G' AS 'Item/#Name' FOR XML PATH(''), TYPE ),
(SELECT '4' AS 'Item/#ID','H' AS 'Item/#Name' FOR XML PATH(''), TYPE ),
(SELECT '5' AS 'Item/#ID','I' AS 'Item/#Name' FOR XML PATH(''), TYPE ),
(SELECT '6' AS 'Item/#ID','J' AS 'Item/#Name' FOR XML PATH(''), TYPE ),
(SELECT '7' AS 'Item/#ID','K' AS 'Item/#Name' FOR XML PATH(''), TYPE ),
(SELECT '8' AS 'Item/#ID','L' AS 'Item/#Name' FOR XML PATH(''), TYPE ),
(SELECT '9' AS 'Item/#ID','M' AS 'Item/#Name' FOR XML PATH(''), TYPE ),
(SELECT '10' AS 'Item/#ID','N' AS 'Item/#Name' FOR XML PATH(''), TYPE )
FOR XML PATH(''),
ROOT('Property'),TYPE
I am not using any tables from database
Well just SELECT it...
SELECT CAST ('<Property ID="0" Name="Color" NumOfItems="11">
<Item ID="0" Name="D"/>
<Item ID="1" Name="E"/>
<Item ID="2" Name="F"/>
<Item ID="3" Name="G"/>
<Item ID="4" Name="H"/>
<Item ID="5" Name="I"/>
<Item ID="6" Name="J"/>
<Item ID="7" Name="K"/>
<Item ID="8" Name="L"/>
<Item ID="9" Name="M"/>
<Item ID="10" Name="N"/>
</Property>' AS XML)
1)
SELECT *
FROM (VALUES (0, 'Color', 11)) Property(ID, Name, NumOfItems)
JOIN (VALUES (0,0,'D'), (0,1,'E'), (0,2,'F')) Item(PropertyID,ID,Name)
ON Property.ID = Item.PropertyID
FOR XML AUTO
Output:
<Property ID="0" Name="Color" NumOfItems="11">
<Item PropertyID="0" ID="0" Name="D" />
<Item PropertyID="0" ID="1" Name="E" />
<Item PropertyID="0" ID="2" Name="F" />
</Property>
2)
SELECT *,
(SELECT * FROM (VALUES (0,'D'), (1, 'E'), (2, 'F')) MamboJambo("#ID", "#Name") FOR XML PATH('Item'),TYPE)
FROM (VALUES (0,'Color',11)) CocoJambo("#ID", "#Name", "#NumOfItems")
FOR XML PATH('Property')
3)
SELECT CONVERT(XML, N'<Property ID="0" Name="Color" NumOfItems="11">
<Item ID="0" Name="D"/>
<Item ID="1" Name="E"/>
<Item ID="2" Name="F"/>
</Property>').query(N'
for $x in (/Property)
return
<Property ID="{$x/#ID}" Name="{$x/#Name}" NumOfItems="{$x/#NumOfItems}" >
{for $y in ($x/Item) return $y}
</Property>
');

Resources