SUM of filtered attributes - xslt-1.0

I have got this XML
<food>
<foodprice foodID="1" Price="10.0000"></foodprice>
<foodprice foodID="2" Price="20.0000"></foodprice>
<foodprice foodID="3" Price="30.0000"></foodprice>
</food>
<basket Name="Any1">
<vegetables foodID="1"></vegetables>
<vegetables foodID="2"></vegetables>
</basket>
<basket Name="Any2">
<vegetables foodID="1"></vegetables>
</basket>
<basket Name="Any3">
<vegetables foodID="1"></vegetables>
<vegetables foodID="3"></vegetables>
</basket>
<basket Name="Any4">
<vegetables foodID="2"></vegetables>
<vegetables foodID="3"></vegetables>
</basket>
Now, I selected values for each basket used variable and filter
<xsl:template match="vegetables">
<xsl:variable name="vegId" select="#foodID"/>
<xsl:for-each select="/ROOT/food/foodprice[#foodID=$vegId]">
<xsl:value-of select="(/ROOT/food/foodprice[#foodID=$vegId]/#Price div count(/ROOT/Basket/vegetables[#foodID=$vegId]))"/>
</xsl:for-each>
</xsl:template>
The result is -
Any1
3,33
10
Any2
3,33
Any3
3,33
15
Any4
10
15
My question is, how can I calculate the sum of Any1 13,33; Any2 3,33; Any3 18,33; Any4 25 etc.
thanks

Related

XML select statement to loop over all rows

I have
mytable consists of one XMLTYPE column called 'RS'. RS contains:
<test>
<mycol>
<name>a</name>
<number>1</number>
<number>2</number>
<number>50</number>
<number>60</number>
</mycol>
<mycol>
<name>b</name>
<number>5</number>
<number>820</number>
<number>601</number>
</mycol>
<mycol>
<name>c</name>
<number>6</number>
<number>8</number>
<number>62</number>
</mycol>
etc...
</test>
I'm looking to run a select statement that will display ALL names and up to 2 numbers from mytable.
something like this select statement but for all rows and without calling mycol[] several times.
select a.RS.extract('/test/mycol[1]/name[1]/text()').getstringval() as Names,
a.RS.extract('/test/mycol[1]/a[1]/text()').getstringval() ||''||
a.RS.extract('/test/mycol[1]/a[2]/text()').getstringval() ||''||
a.RS.extract('/test/mycol[1]/a[3]/text()').getstringval()
as num
from mytable a;/
output should be:
Names | num
a | 1 2
b | 5 820
c | 6 8
etc...
Thanks in advance.
Xml_table and string-join may be helpful

How to iterate through every potential XML subelement using SQL Server

I have a large XML file of over 45K contacts and I need to iterate through their subelement transactions into a SQL table. I've looked at several solutions to this, using value(), node(), etc..., but no examples appear to have an XML structure close to mine:
<Contacts>
<Contact>
<ContactID>1234</ContactID>
<ContactName>’John Doe’</ContactName>
<DOB>09031978</DOB>
<Address>’123 Main Street’</Address>
<Transactions>
<Transaction>
<TransactionID>4490</TransactionID>
<ProductName>’Recliner’</ProductName>
<Cost>123.00</Cost>
<PurchaseDate>07042020
</Transaction>
<Transaction>
<TransactionID>5678</TransactionID>
<ProductName>’Lamp’</ProductName>
<Cost>45.00</Cost>
<PurchaseDate>07042020
<Transaction>
</Transactions>
</Contact>
<Contact>
<ContactID>4567</ContactID>
<ContactName>’Jane Doe’</ContactName>
<DOB>05191984</DOB>
<Address>’567 Fake Street’</Address>
<Transactions>
<Transaction>
<TransactionID>4378</TransactionID>
<ProductName>’Coffee Table’</ProductName>
<Cost>225.00</Cost>
<PurchaseDate>07042018
</Transaction>
</Transactions>
</Contact>
</Contacts>
I need these data in a result like below:
ContactID
TransactionID
ProductName
Cost
PurchaseDate
1234
4490
Recliner
123.00
4 July 2020
1234
5678
Lamp
45.00
4 July 2020
4567
4378
Coffee Table
225.00
4 July 2018
I've tried a query using the following script:
EXEC sp_xml_preparedocument #idoc OUTPUT, #doc
-- Execute a SELECT stmt using OPENXML rowset provider.
SELECT *
FROM OPENXML (#idoc, '/Contacts/Contact/Transactions/Transaction',2)
WITH (ContactID int '../ContactID',
TransactionID int 'TransactionID',
ProductName nvarchar(50) 'ProductName',
Cost float 'Cost',
PurchaseDate date 'PurchaseDate')
But this will return either a null for ContactID; or return only one transaction for each ContactID. But I need it to iterate and get as many transactions as exist for a contact.
Any insights would be most welcome!
Try to avoid sp_xml_preparedocument because it uses large amounts of memory that can't be used by SQL Server until you remember to free it up by invoking sp_xml_removedocument.
What you're asking for can be easily achieved using nodes() with a cross apply, e.g. (after fixing your XML sample):
declare #doc xml = N'<Contacts>
<Contact>
<ContactID>1234</ContactID>
<ContactName>’John Doe’</ContactName>
<DOB>09031978</DOB>
<Address>’123 Main Street’</Address>
<Transactions>
<Transaction>
<TransactionID>4490</TransactionID>
<ProductName>’Recliner’</ProductName>
<Cost>123.00</Cost>
<PurchaseDate>07042020</PurchaseDate>
</Transaction>
<Transaction>
<TransactionID>5678</TransactionID>
<ProductName>’Lamp’</ProductName>
<Cost>45.00</Cost>
<PurchaseDate>07042020</PurchaseDate>
</Transaction>
</Transactions>
</Contact>
<Contact>
<ContactID>4567</ContactID>
<ContactName>’Jane Doe’</ContactName>
<DOB>05191984</DOB>
<Address>’567 Fake Street’</Address>
<Transactions>
<Transaction>
<TransactionID>4378</TransactionID>
<ProductName>’Coffee Table’</ProductName>
<Cost>225.00</Cost>
<PurchaseDate>07042018</PurchaseDate>
</Transaction>
</Transactions>
</Contact>
</Contacts>';
select
Cont.value('ContactID[1]', 'int') as ContactID
,Trans.value('TransactionID[1]', 'int') as TransactionID
,Trans.value('ProductName[1]', 'nvarchar(50)') as ProductName
,Trans.value('Cost[1]', 'float') as Cost
,convert(date, concat(substring(purDate,1,2), '/', substring(purDate,3,2), '/', substring(purDate,5,4)), 101) as PurchaseDate
from #doc.nodes('//Contact') nodes1(Cont)
cross apply nodes1.Cont.nodes('Transactions/Transaction') nodes2(Trans)
outer apply (
select purDate = Trans.value('PurchaseDate[1]', 'nvarchar(8)')
) temp;
Which yields:
ContactID
TransactionID
ProductName
Cost
PurchaseDate
1234
4490
’Recliner’
123
2020-07-04
1234
5678
’Lamp’
45
2020-07-04
4567
4378
’Coffee Table’
225
2018-07-04

Converting SQL query result to XML [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
Input data
id year Name provid prov
1 1995 MAC 1995-11_CL236 reg 236
1 1995 MAC 1995-11_CL230 reg 230 (1)
1 1995 MAC 1995-11_CL229J reg 229J
1 1995 MAC 1995-11_CL260 reg 260
My query looks like this
select
id, year, Name, prov, provid
from
Table
for xml path ('entry'), root('legref'), elements
The above Query generating different entry for each row. But I need group by id year, name and provide single entry with different prov and provid.
<legref>
<entry>
<id>1</id>
<year>1995</year>
<Name>MAC</Name>
<prov>reg 229J</prov>
<provid>NSW_REG_1995-11_CL229J</provid>
</entry>
<entry>
<id>1</id>
<year>1995</year>
<Name>MAC</Name>
<prov>reg 230 (1)</prov>
<provid>NSW_REG_1995-11_CL230</provid>
</entry>
<entry>
<id>1</id>
<year>1995</year>
<Name>MAC</Name>
<prov>reg 236</prov>
<provid>NSW_REG_1995-11_CL236</provid>
</entry>
<entry>
<id>1</id>
<year>1995</year>
<Name>MAC</Name>
<prov>reg 260</prov>
<provid>NSW_REG_1995-11_CL260</provid>
</entry>
</legref>
Output Data:
How do I convert SQL query result to XML?
Expected result set:
<legref>
<entry>
<id>1<id>
<year>1995</year>
<Name>MAC</Name>
<prov provID="1995-11_CL230">reg 230 (1)</prov>
<prov provID="1995-11_CL236">reg 236</prov>
<prov provID="1995-11_CL260">reg 260</prov>
<prov provID="1995-11_CL229J">reg 229J</prov>
</entry>
</legref>
Try This
FIDDLE DEMO
SELECT ID, Year, Name,
(
SELECT ProvID AS 'Prov/#ProvID',Prov
FROM tbl t
WHERE ID = t.ID AND Name = t.Name
FOR XML PATH(''),TYPE
)
FROM tbl
GROUP BY ID, Year, Name
FOR XML PATH ('Entry'),ROOT('legref')
Output
<legref>
<Entry>
<ID>1</ID>
<Year>1995</Year>
<Name>MAC</Name>
<Prov ProvID="1995-11_CL236">Reg 236</Prov>
<Prov ProvID="1995-11_CL230">Reg 230</Prov>
<Prov ProvID="1995-11_CL229J">Reg 229J</Prov>
<Prov ProvID="1995-11_CL260">Reg 260</Prov>
</Entry>
</legref>
Please try below query,
SELECT id
,year
,Name
,prov
,provid
FROM TABLE
FOR XML RAW ,ELEMENTS
I have tried it for different table for same type of result.
Please check snap:

How to Select distinct values in FOR XML PATH?

Given the following tables where T_DATA.ID = PARENT_ID or CHILD.ID
Name: T_DATA
+----+------+--------+
| ID | CODE | VALUE |
+----+------+--------+
| 1 | 3186 | value1 |
| 2 | 3186 | value2 |
| 3 | 3189 | value3 |
| 4 | 3189 | value4 |
| 5 | 3190 | value5 |
+----+------+--------+
Name: T_DATA_LINK
+-----------+----------+
| PARENT_ID | CHILD_ID |
+-----------+----------+
| 1 | 3 |
| 1 | 4 |
+-----------+----------+
I want to return an xml structure like this:
<ITEM_LIST>
<ITEM>
<CODE>3186</CODE>
<ROWS>
<ROW>
<ID>1</ID>
<ROW_INDEX>0</ROW_INDEX>
<VALUE>value1</VALUE>
</ROW>
<ROW>
<ID>2</ID>
<ROW_INDEX>1</ROW_INDEX>
<VALUE>value2</VALUE>
</ROW>
</ROWS>
</ITEM>
<ITEM>
<CODE>3189</CODE>
<ROWS>
<ROW>
<ID>3</ID>
<ROW_INDEX>0</ROW_INDEX>
<VALUE>value3</VALUE>
</ROW>
<ROW>
<ID>4</ID>
<ROW_INDEX>1</ROW_INDEX>
<VALUE>value4</VALUE>
</ROW>
</ROWS>
</ITEM>
<ITEM>
<CODE>3190</CODE>
<VALUE>value5</VALUE>
</ITEM>
</ITEM_LIST>
The ROW_INDEX is incremented by 1 for every ROW.
I need the T_DATA_LINK table to know whether an ITEM has a parent or not.
If it has a parent it means that there is more than one record with that CODE value and they need to be displayed as ROWS, otherwise it has to be displayed as a single ITEM.
UPDATE
I actually need to check the T_DATA_LINK table since there may be cases where an ITEM has a parent and only one record, but it still need to be displayed as a ROW.
#Shnugo I tried your solution, but even if now I get the correct values inside the ROWS, I get duplicates for each ITEM that has more than one record.
This is probably because I had to add to the GROUP BY the other fields I need to return with the SELECT which I didn't add to the example in order to keep it simpler.
For example, the ID need to be displayed at the ITEM level for the items which don't have any ROWS.
UPDATE 2
#Shnugo you are correct. Items 3 and 4 are the children of Item 1, but you don't see the relationship in the xml.
All the items are unique, always.
The items that are referenced in T_DATA_LINK are still unique, but are linked to each other in my application where they are displayed inside a table.
Basically the PARENT is the first column of the table and the children are the others columns.
This is the updated output I want to get.
ID should be always -1 for the items that have rows.
PARENT_CODE should be the CODE of the parent (if the item is a parent then it is equal to the CODE)
<ITEM_LIST>
<ITEM>
<ID>-1</ID>
<CODE>3186</CODE>
<PARENT_CODE>3186</PARENT_CODE>
<ROWS>
<ROW>
<ID>1</ID>
<ROW_INDEX>0</ROW_INDEX>
<VALUE>value1</VALUE>
</ROW>
<ROW>
<ID>2</ID>
<ROW_INDEX>1</ROW_INDEX>
<VALUE>value2</VALUE>
</ROW>
</ROWS>
</ITEM>
<ITEM>
<ID>-1</ID>
<CODE>3189</CODE>
<PARENT_CODE>3186</PARENT_CODE>
<ROWS>
<ROW>
<ID>3</ID>
<ROW_INDEX>0</ROW_INDEX>
<VALUE>value3</VALUE>
</ROW>
<ROW>
<ID>4</ID>
<ROW_INDEX>1</ROW_INDEX>
<VALUE>value4</VALUE>
</ROW>
</ROWS>
</ITEM>
<ITEM>
<ID>5</ID>
<CODE>3190</CODE>
<VALUE>value5</VALUE>
</ITEM>
</ITEM_LIST>
This is a new answer... Please try to put all needed information into the initial question...
DECLARE #t_data TABLE(ID INT,CODE INT,VALUE VARCHAR(100));
INSERT INTO #t_data VALUES
(1,3186,'value1')
,(2,3186,'value2')
,(3,3189,'value3')
,(4,3189,'value4')
,(5,3190,'value5');
DECLARE #t_data_link TABLE(PARENT_ID INT, CHILD_ID INT)
INSERT INTO #t_data_link VALUES
(1,3)
,(1,4);
--The CTE links the two tables and allows to handle them as one derived table
WITH Combined AS
(
SELECT d.*
,d2.CODE AS PARENT_CODE
,COUNT(*) OVER(PARTITION BY d.CODE) AS CountRows
FROM #t_data AS d
LEFT JOIN #t_data_link AS dl ON d.ID=dl.CHILD_ID
LEFT JOIN #t_data AS d2 ON dl.PARENT_ID=d2.ID
)
SELECT CASE WHEN c.CountRows>1 THEN -1 END AS ID
,CASE WHEN c.CountRows>1 THEN c.CODE END AS CODE
,CASE WHEN c.CountRows>1 THEN ISNULL(c.PARENT_CODE,c.CODE) END AS PARENT_CODE
--This part for elements with just one row per code
,(
SELECT d2.ID
,d2.CODE
,d2.VALUE
FROM #t_data AS d2
WHERE c.CODE=d2.CODE
AND c.CountRows=1
FOR XML PATH(''),TYPE
)
--This part for elements with more rows per code
,(
SELECT d2.ID
,ROW_NUMBER() OVER(ORDER BY (SELECT NULL))-1 AS ROW_INDEX
,d2.VALUE
FROM #t_data AS d2
WHERE c.CODE=d2.CODE
AND c.CountRows>1
FOR XML PATH('ROW'),ROOT('ROWS'),TYPE
)
FROM Combined AS c
GROUP BY c.CODE,c.CountRows,c.PARENT_CODE
FOR XML PATH('ITEM'),ROOT('ITEM_LIST');
The result
<ITEM_LIST>
<ITEM>
<ID>-1</ID>
<CODE>3186</CODE>
<PARENT_CODE>3186</PARENT_CODE>
<ROWS>
<ROW>
<ID>1</ID>
<ROW_INDEX>0</ROW_INDEX>
<VALUE>value1</VALUE>
</ROW>
<ROW>
<ID>2</ID>
<ROW_INDEX>1</ROW_INDEX>
<VALUE>value2</VALUE>
</ROW>
</ROWS>
</ITEM>
<ITEM>
<ID>-1</ID>
<CODE>3189</CODE>
<PARENT_CODE>3186</PARENT_CODE>
<ROWS>
<ROW>
<ID>3</ID>
<ROW_INDEX>0</ROW_INDEX>
<VALUE>value3</VALUE>
</ROW>
<ROW>
<ID>4</ID>
<ROW_INDEX>1</ROW_INDEX>
<VALUE>value4</VALUE>
</ROW>
</ROWS>
</ITEM>
<ITEM>
<ID>5</ID>
<CODE>3190</CODE>
<VALUE>value5</VALUE>
</ITEM>
</ITEM_LIST>
XML will omit any NULL value. The WHERE clause in the subselects will return with NULL if there's nothing found...

Querying SQL for grouped data and receiving it in BizTalk?

I have a query that returns data organized by group. I am wanting to have them come out in a grouped XML similar to the format below. I am planning to pass it in as a XML message into BizTalk using the WCF-SQL port adapter.
Data:
ID GroupID Item ID FileName
1 1 1 File001.txt
2 1 2 File001.txt
3 2 3 File002.txt
4 2 4 File002.txt
5 2 5 File002.txt
6 3 6 File003.txt
7 3 7 File003.txt
8 null 8 File004.txt
9 null 9 File005.txt
XML Ouput (input to BizTalk)
<GroupInfo ID=1 FileName=File001.txt>
<Items>
<Item ID=1 />
<Item ID=2 />
</Items>
</GroupInfo>
<GroupInfo ID=2 FileName=File002.txt>
<Items>
<Item ID=3 />
<Item ID=4 />
<Item ID=5 />
</Items>
</GroupInfo>
<GroupInfo ID=3 FileName=File003.txt>
<Items>
<Item ID=6 />
<Item ID=7 />
</Items>
</GroupInfo>
<GroupInfo FileName=File004.txt>
<Items>
<Item ID=8 />
</Items>
</GroupInfo>
<GroupInfo FileName=File005.txt>
<Items>
<Item ID=9 />
</Items>
</GroupInfo>
I'm not sure what to do to get the output in the required format. Please help.
To accomplish this, you would have to use FOR XML results from the Stored Procedure.
All documented here: http://technet.microsoft.com/en-us/library/ms178107.aspx
In this case, you might find it easier to join the table to itself on GroupID as FOR XML AUTO will automatically create child records for the joined rows.