Xquery sum of values - sum

I've got this part of code:
<recipe>
{ for $r in doc("retete.xml")//recipe
return <rec title="{$r/title}">{
<item>{
for $r3 in doc("listaPreturi.xml")//produs
for $r2 in $r/ingredients/ingredient
where $r2/#name = $r3/#denumire
return <it>{$r3/pret * $r2/#amount / $r3/pret/#cantitate}</it>
}
</item>
}</rec>}
</recipe>
And the result is this
<recipe>
<rec title="Pui la cuptor">
<item>
<it>1550</it>
<it>250</it>
</item>
</rec>
<rec title="Curcan la cuptor">
<item>
<it>1550</it>
<it>1889.9999999999998</it>
</item>
</rec>
</recipe>
How can I add in same function values for each <it> different <item> ?

Related

How to specify / Add tags before the Nested XML on SQL Server

I am trying to bypass C# to build a google shopping feed directly from SQL Server.
This is what I have for the nested XML body.
DECLARE #XMLBODY XML
SET #XMLBODY = (
SELECT
ID as "ID",
title as "title",
link as "link",
[description] as "description",
image_link as "image_link"
,[Price] as "Price"
,[mpn] as "mpn"
,[brand] as "brand"
,[Condition] as "Condition"
,[availability] as "availability"
,[shipping_weight] as "shipping_weight"
,[google_product_category] as "google_product_category"
,[custom_label_0] as "custom_label_0"
,[custom_label_1] as "custom_label_1"
,[custom_label_2] as "custom_label_2"
,[custom_label_3] as "custom_label_3"
,[custom_label_4] as "custom_label_4" FROM dbo.SmartShoppingFeed
FOR XML PATH ('item'))
SELECT #XMLBODY
And as result, I am getting the following XML (only showing first 2..)
<item>
<ID>22760</ID>
<title>76101 - Product Name A</title>
<link>landingpage2.aspx</link>
<description>body spacers</description>
<image_link>productimage_001.jpg</image_link>
<Price>427.6300</Price>
<mpn>76101AA</mpn>
</item>
<item>
<ID>22760</ID>
<title>76101 - Product Name B</title>
<link>landingpage2.aspx</link>
<description>body spacers</description>
<image_link>productimage_002.jpg</image_link>
<Price>427.6300</Price>
<mpn>76102AA</mpn>
</item>
And what I need as final result is the body with schema, version, and NS before the body and closing tag after the body as follow
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:g="http://base.google.com/ns/1.0">
<channel>
<title>storename</title>
<link>https://www.storename.com/</link>
<description></description>
<language>en-US</language>
<pubDate>Thu, 25 Jul 2019 20:43:33 GMT</pubDate>
<lastBuildDate>Thu, 25 Jul 2019 20:43:33 GMT</lastBuildDate>
<item>
<ID>22760</ID>
<title>76101 - Product Name A</title>
<link>landingpage2.aspx</link>
<description>body spacers</description>
<image_link>productimage_001.jpg</image_link>
<Price>427.6300</Price>
<mpn>76101AA</mpn>
</item>
<item>
<ID>22760</ID>
<title>76101 - Product Name B</title>
<link>landingpage2.aspx</link>
<description>body spacers</description>
<image_link>productimage_002.jpg</image_link>
<Price>427.6300</Price>
<mpn>76102AA</mpn>
</item>
</channel>
</rss>
I have tried many workarounds to add them statically by casting as string and use concat to combine the head and tail with the body but that didn't work as planned.
Thanks for the help in advance.
You can use XQuery to easily re-shape the final XML as follows:
SQL
DECLARE #XMLBODY XML = '<item>
<ID>22760</ID>
<title>76101 - Product Name A</title>
<link>landingpage2.aspx</link>
<description>body spacers</description>
<image_link>productimage_001.jpg</image_link>
<Price>427.6300</Price>
<mpn>76101AA</mpn>
</item>
<item>
<ID>22760</ID>
<title>76101 - Product Name B</title>
<link>landingpage2.aspx</link>
<description>body spacers</description>
<image_link>productimage_002.jpg</image_link>
<Price>427.6300</Price>
<mpn>76102AA</mpn>a
</item>';
DECLARE #finalXML XML;
SET #finalXML = #XMLBODY.query('<rss version="2.0" xmlns:g="http://base.google.com/ns/1.0">
<channel>
<title>storename</title>
<link>https://www.storename.com/</link>
<description></description>
<language>en-US</language>
<pubDate>Thu, 25 Jul 2019 20:43:33 GMT</pubDate>
<lastBuildDate>Thu, 25 Jul 2019 20:43:33 GMT</lastBuildDate>
{
for $item in /item
return $item
}
</channel>
</rss>
');
SELECT #finalXML;
Strictly speaking, Google product feed requires prefixes so the solution may look like this.
DECLARE #XMLBODY XML
with xmlnamespaces('http://base.google.com/ns/1.0' as g,
'http://base.google.com/cns/1.0' as c)
SELECT #XMLBODY = (
SELECT
ID as "g:ID",
title as "title",
link as "link",
[description] as "description",
image_link as "g:image_link"
,[Price] as "g:price"
,[mpn] as "g:mpn"
,[brand] as "g:brand"
,[Condition] as "g:condition"
,[availability] as "g:availability"
,[shipping_weight] as "g:shipping_weight"
,[google_product_category] as "g:google_product_category"
,[custom_label_0] as "c:custom_label_0"
,[custom_label_1] as "c:custom_label_1"
FROM dbo.SmartShoppingFeed
FOR XML PATH ('item'),root('root'))
SELECT #XMLBODY
And then apply great #Yitzhak Khabinsky's solution.

Can yo help me with getting some data from a XML?

Getting SQL data from multiple XML
I already tried to put the code in an XML variable and select OrderNumber, ProductionLine and ItemId's but having some troubles with the query.
DECLARE #DXML XML = '<ComDecom OrderNumber="101983026"
ProductionLine="14" BatchNumber="02-00" ItemObjectTypeId="1"
ItemFlag="20" EventGuid="989bfdb4-9dd8-40be-9872-1e0bae7cc4d6"
LastMessage="false" HostName="PMIPTLISWCT0014+1">
<Item ItemId="LESTCNNGxDDCPq1bSF1S119052306" TimeStamp="2019-05-23
07:56:07.475 +01:00" SeqNumber="175660" />
<Item ItemId="LESTCNNGxDDCPq1bSF1S119052306" TimeStamp="2019-05-23
07:56:07.519 +01:00" SeqNumber="175661" />
<Item ItemId="LESTCNoTmCiiVu1bSF1S119052306" TimeStamp="2019-05-23
07:56:08.487 +01:00" SeqNumber="175662" />
</ComDecom>'
SELECT ComDeCom.value('#OrderNumber', 'int') AS OrderNumber
,ComDecom.value('#ProductionLine', 'int') AS ProductionLine
,ItemTbl.value('#ItemId', 'varchar') AS Item
FROM #dxml.nodes('/ComDecom/') AS ComDecomTbl(ComDecom)
CROSS APPLY ComDecom.Item.nodes('Site') AS ItemTbl(Item)
I think you are looking for this-
DECLARE #DXML XML=
'<ComDecom OrderNumber="101983026" ProductionLine="14" BatchNumber="02-00" ItemObjectTypeId="1" ItemFlag="20" EventGuid="989bfdb4-9dd8-40be-9872-1e0bae7cc4d6" LastMessage="false" HostName="PMIPTLISWCT0014+1">
<Item ItemId="LESTCNNGxDDCPq1bSF1S119052306" TimeStamp="2019-05-23 07:56:07.475 +01:00" SeqNumber="175660" />
<Item ItemId="LESTCNNGxDDCPq1bSF1S119052306" TimeStamp="2019-05-23 07:56:07.519 +01:00" SeqNumber="175661" />
<Item ItemId="LESTCNoTmCiiVu1bSF1S119052306" TimeStamp="2019-05-23 07:56:08.487 +01:00" SeqNumber="175662" />
</ComDecom>';
SELECT
T.N.value('#OrderNumber', 'int') AS OrderNumber,
T.N.value('#ProductionLine', 'int') AS ProductionLine,
T2.N2.value('#ItemId', 'varchar(MAX)') AS Item
FROM #dxml.nodes('/ComDecom') AS T(N)
CROSS APPLY #dxml.nodes('/ComDecom/Item') AS T2(N2)
Output is-
OrderNumber ProductionLine Item
101983026 14 LESTCNNGxDDCPq1bSF1S119052306
101983026 14 LESTCNNGxDDCPq1bSF1S119052306
101983026 14 LESTCNoTmCiiVu1bSF1S119052306

How to insert XML to SQL

I try to add a XML file to SQL 2008.
My XML:
<ItemList>
<Section Index="0" Name="cat0">
<Item Index="0" Slot="0" />
<Item Index="1" Slot="0" />
</Section>
<Section Index="1" Name="cat1">
<Item Index="33" Slot="0" />
<Item Index="54" Slot="0" />
</Section>
<Section Index="2" Name="cat2">
<Item Index="55" Slot="0" />
<Item Index="78" Slot="0" />
</Section>
</ItemList>
SQL Column :
Name = Section Name,
Cat = Section Index,
Index = Item Index,
Slot = Item Slot.
My Example :
DECLARE #input XML = 'MY XML file'
SELECT
Name = XCol.value('#Index','varchar(25)'),
Cat = XCol.value('#Name','varchar(25)'),
[Index] = 'Unknown', /* Index from <Item>*/
Slot = 'Unknown' /* Slot from <Item> */
FROM #input.nodes('/ItemList/Section') AS test(XCol)
I don't know how to add values from "Item".
Thank you very much!
You can do it like this:
select
Name = XCol.value('../#Index','varchar(25)'),
Cat = XCol.value('../#Name','varchar(25)'),
[Index] = XCol.value('#Index','varchar(25)'),
Slot = XCol.value('#Slot','varchar(25)')
from
#input.nodes('/ItemList/Section/Item') AS test(XCol)
Key idea: take data one level deeper, not /ItemList/Section, but /ItemList/Section/Item. So in this case you are able to access attributes of Item and also you can access attributes of parent element (Section in your case) by specifying ../#Attribute_Name
Different than the previous answer - CROSS APPLY with the children Item nodes:
SELECT
Name = XCol.value('#Index','varchar(25)'),
Cat = XCol.value('#Name','varchar(25)'),
[Index] = XCol2.value('#Index','varchar(25)'),
Slot = XCol2.value('#Slot','varchar(25)')
FROM #input.nodes('/ItemList/Section') AS test(XCol)
CROSS APPLY XCol.nodes('Item') AS test2(XCol2)

Format / Indent XML code while sending email using sp_send_dbmail [formatting xml code in mail body]

I am trying to send a dbmail with XML code as a body, see my code below showing the same,
declare #xml xml = '<?xml version="1.0"?>
<Order>
<Date>2003/07/04</Date>
<CustomerId>123</CustomerId>
<CustomerName>Acme Alpha</CustomerName>
<Item>
<ItemId> 987</ItemId>
<ItemName>Coupler</ItemName>
<Quantity>5</Quantity>
</Item>
<Item>
<ItemId>654</ItemId>
<ItemName>Connector</ItemName>
<Quantity unit="12">3</Quantity>
</Item>
<Item>
<ItemId>579</ItemId>
<ItemName>Clasp</ItemName>
<Quantity>1</Quantity>
</Item>
</Order>'
--SELECT #xml
declare #bodyprep varchar(max)
select #bodyprep = cast(#xml as varchar(max))
EXEC msdb. dbo.sp_send_dbmail
#profile_name='Profile-A' ,
#recipients ='.com',
#from_address = '.com' ,
#subject = 'test',
#body = #bodyprep
but the mail i receive is unaligned like shown below,
<Order><Date>2003/07/04</Date><CustomerId>123</CustomerId><CustomerName>Acme Alpha</CustomerName><Item><ItemId> 987</ItemId><ItemName>Coupler</ItemName><Quantity>5</Quantity></Item><Item><ItemId>654</ItemId><ItemName>Connector</ItemName><Quantity unit="12">3</Quantity></Item><Item><ItemId>579</ItemId><ItemName>Clasp</ItemName><Quantity>1</Quantity></Item></Order>
Is there a way to format or indent the XML code above ?
I would like to send a formatted code as shown below,
<Order>
<Date>2003/07/04</Date>
<CustomerId>123</CustomerId>
<CustomerName>Acme Alpha</CustomerName>
<Item>
<ItemId> 987</ItemId>
<ItemName>Coupler</ItemName>
<Quantity>5</Quantity>
</Item>
<Item>
<ItemId>654</ItemId>
<ItemName>Connector</ItemName>
<Quantity unit="12">3</Quantity>
</Item>
<Item>
<ItemId>579</ItemId>
<ItemName>Clasp</ItemName>
<Quantity>1</Quantity>
</Item>
</Order>
Thanks
msdb.dbo.sp_send_dbmail has an additional parameter, #body_format = 'HTML', which would allow you to display the XML as you like, once you'd decorated it with the proper HTML markup. That's the only way you can control the appearance of the email body to the level you've specified, i.e. with proper indentation and color-coding.
If you can do without the color-coding, #body_format = 'TEXT' (the default) is fine, but either way you have some work on your hands to get it indented as you indicate.

Extracting part of XMLType in PL/SQL because of repeating node inside another repeating mode

I have a XMLType object and I want to extract opening times into table.
<workspace>
<title>workspace1</title>
<item>
<day>1</day>
<openingTime>8:00</openingTime>
<closingTime>12:00</closingTime>
</item>
<item>
<day>1</day>
<openingTime>13:00</openingTime>
<closingTime>18:00</closingTime>
</item>
<workspace>
<workspace>
<title>workspace2</title>
<item>
<day>1</day>
<openingTime>9:00</openingTime>
<closingTime>14:00</closingTime>
</item>
<item>
<day>3</day>
<openingTime>12:00</openingTime>
<closingTime>16:00</closingTime>
</item>
<workspace>
I would use something like:
SELECT ExtractValue(Value(p),'workspace/item/day/text()') as day
,ExtractValue(Value(p),'workspace/item/openingTime/text()') as open
,ExtractValue(Value(p),'workspace/item/closingTime/text()') as close
FROM TABLE (XMLSequence(Extract(y,'workspace'))) p
WHERE ExtractValue(Value(p),'/workspace/title/text()') LIKE 'workspace1';
where y is XMLType above. But that won't work, because it will still find more than one item node. I need to extract ALL element values for title workspace2 (values 1, 9:00, 14:00, 3, 12:00, 16:00). It would help if I could extract not only value, but whole part of XMLType. Any ideas?
Thanks, Michal
Your target may be achieved by using XMLTable :
with x as ( -- Just to introduce XML parameter
select
xmltype('
<workspace_list>
<workspace>
<title>workspace1</title>
<item>
<day>1</day>
<openingTime>8:00</openingTime>
<closingTime>12:00</closingTime>
</item>
<item>
<day>1</day>
<openingTime>13:00</openingTime>
<closingTime>18:00</closingTime>
</item>
</workspace>
<workspace>
<title>workspace2</title>
<item>
<day>1</day>
<openingTime>9:00</openingTime>
<closingTime>14:00</closingTime>
</item>
<item>
<day>3</day>
<openingTime>12:00</openingTime>
<closingTime>16:00</closingTime>
</item>
</workspace>
</workspace_list>
') xfield
from dual
)
select
workspace, day, opening_time, closing_time
from
XMLTable(
'
for $i in $doc//workspace[title eq $workspace_filter]
for $j in $i/item
return
<wks_item>
<wks_name>{$i/title/text()}</wks_name>
{$j/*}
</wks_item>
'
passing
(select xfield from x) as "doc",
('workspace1') as "workspace_filter"
columns
workspace varchar2(100) path '//wks_name',
day varchar2(100) path '//day',
opening_time varchar2(100) path '//openingTime',
closing_time varchar2(100) path '//closingTime'
)
SQLFiddle Example
Note that I introduced <workspace_list> top element and added slash for closing <workspace> elements to make XML valid.
With some #ThinkJet tricks your query may look like this
with x as (
select
xmltype('
<workplaces>
<workspace>
<title>workspace1</title>
<item>
<day>1</day>
<openingTime>8:00</openingTime>
<closingTime>12:00</closingTime>
</item>
<item>
<day>1</day>
<openingTime>13:00</openingTime>
<closingTime>18:00</closingTime>
</item>
</workspace>
<workspace>
<title>workspace2</title>
<item>
<day>1</day>
<openingTime>9:00</openingTime>
<closingTime>14:00</closingTime>
</item>
<item>
<day>3</day>
<openingTime>12:00</openingTime>
<closingTime>16:00</closingTime>
</item>
</workspace>
</workplaces>
') xfield
from dual
)
SELECT "day", "openingTime", "closingTime"
FROM xmltable('$doc//workspace[title=$workspace_filter]/item'
passing (select xfield from x) as "doc",
('workspace1') as "workspace_filter"
columns "openingTime" path '//openingTime',
"closingTime" path '//closingTime',
"day" path '//day')