Empty results when filtering based on position in XQuery - sql-server

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).

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;

Modify a portion of an XML with key/value structure

I need to write a SQL Server query to change the values of the following example XML, each one should have a different new value:
Example:
From
<Item>
<key>RabbitMQConnection</key>
<value Tr="PropertyBag">
<Item>
<key>Encoding</key>
<value>65001</value>
</Item>
<Item>
<key>HostName</key>
<value>TESTHOST</value>
</Item>
<Item>
<key>UserName</key>
<value>USER</value>
</Item>
<Item>
<key>Password</key>
<value>PASS</value>
</Item>
<Item>
<key>QueueName</key>
<value>TESTQUEUE</value>
</Item>
<Item>
<key>VirtualHost</key>
<value>TESTVHOST</value>
</Item>
</value>
</Item>
<Item>
to
<Item>
<key>RabbitMQConnection</key>
<value Tr="PropertyBag">
<Item>
<key>Encoding</key>
<value>65001</value>
</Item>
<Item>
<key>HostName</key>
<value>TESTHOST22</value>
</Item>
<Item>
<key>UserName</key>
<value>USER222</value>
</Item>
<Item>
<key>Password</key>
<value>PASS22</value>
</Item>
<Item>
<key>QueueName</key>
<value>TESTQUEUE22</value>
</Item>
<Item>
<key>VirtualHost</key>
<value>TESTVHOST22</value>
</Item>
</value>
</Item>
<Item>
I'm really struggling with xpath language, can I get some help please?
Many thanks!
Please try the following.
You can modify the rest of the XML elements one by one by using the same approach.
SQL
DECLARE #xml XML =
N'<Item>
<key>RabbitMQConnection</key>
<value Tr="PropertyBag">
<Item>
<key>Encoding</key>
<value>65001</value>
</Item>
<Item>
<key>HostName</key>
<value>TESTHOST</value>
</Item>
<Item>
<key>UserName</key>
<value>USER</value>
</Item>
<Item>
<key>Password</key>
<value>PASS</value>
</Item>
<Item>
<key>QueueName</key>
<value>TESTQUEUE</value>
</Item>
<Item>
<key>VirtualHost</key>
<value>TESTVHOST</value>
</Item>
</value>
</Item>';
DECLARE #HostName NVARCHAR(30) = 'NewTESTHOST';
SET #xml.modify('replace value of (/Item/value/Item[key="HostName"]/value/text())[1]
with (sql:variable("#HostName"))' );
-- test
SELECT #xml;

How to extract values from a xml request body with values in an array in wiremock

I am trying to extract line-item value from the below request body into my response in wiremock.
Request:
**<request>
<lineItem>
<item>
<name> name </name>
<quantity> 12 </quantity>
<type>type</type>
</item>
</ lineItem>
<lineItem>
<item>
<name> name2 </name>
<quantity> 10 </quantity>
<type>type2</type>
</item>
</ lineItem>
</request>**
Expected Response:
**<response>
<lineItem>
<item>
<name> name </name>
<quantity> 12 </quantity>
<type>type</type>
</item>
</ lineItem>
<lineItem>
<item>
<name> name2 </name>
<quantity> 10 </quantity>
<type>type2</type>
</item>
</ lineItem>
</response>**
I tried something like this:
<response>
{{#each (xPath request.body '/lineItem/item ') as |element| }}
<lineItem>
<item>
<quantity> (xPath request.body '//lineItem/item/quantity/text()'}}</quantity>
</item>
</ lineItem>
</response>
but for 2 different lineItem in request (as given above), I got 3 lineItem values in response and always getting the first item values in all the 3 lineItems:
Response I got:
**<response>
<lineItem>
<item>
<quantity> 12 </quantity>
</item>
</ lineItem>
<lineItem>
<item>
<quantity> 12 </quantity>
</item>
</ lineItem>
<lineItem>
<item>
<quantity> 12 </quantity>
</item>
</ lineItem>
</response>**
Can someone help me with this scenario?

xml nodes() in tsql - matching parent nodes with child node attributes

I am trying to query following xml structure:
<effect><![CDATA[<p>some text</p>]]>
<product code="4298271" />
<product code="4298273" />
<product code="4298274" />
<product code="4298275" />
<product code="4298276" />
</effect>
<effect><![CDATA[<p>some other text</p>]]>
<product code="5298271" />
<product code="5298273" />
<product code="5298274" />
<product code="5298275" />
<product code="5298276" />
</effect>
I need to transform this data to a table like below:
Effect ProductCode
some text 4298271
some text 4298273
some text 4298274
...
...
Is it even possible ? I can get the list of product rows which are under every effect parent but no idea how to match it with the effect header from which the text should be queried.
Let's say that effect is root node for simplifying.
Even though the XML isn't really a valid document - you can still query it in T-SQL using XQuery - something like this:
DECLARE #input XML = '<effect><![CDATA[<p>some text</p>]]>
<product code="4298271" />
<product code="4298273" />
<product code="4298274" />
<product code="4298275" />
<product code="4298276" />
</effect>
<effect><![CDATA[<p>some other text</p>]]>
<product code="5298271" />
<product code="5298273" />
<product code="5298274" />
<product code="5298275" />
<product code="5298276" />
</effect>'
SELECT
xc.value('(../text())[1]', 'varchar(50)'),
xc.value('(#code)[1]', 'int')
FROM
#input.nodes('/effect/product') AS XT(XC)
This produces an output:
Adjust as needed ....

Split xml into smaller pieces

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.

Resources