XSLT 1.0 Group By and Sum to compare - xslt-1.0

I have the following XML
<AllTransactions>
<TaxSummaries>
<TaxSummary>
<TypeId>1</TypeId>
<Amount>100.00</Amount>
</TaxSummary>
<TaxSummary>
<TypeId>1</TypeId>
<Amount>200.00</Amount>
</TaxSummary>
<TaxSummary>
<TypeId>2</TypeId>
<Amount>300.00</Amount>
</TaxSummary>
<TaxSummary>
<TypeId>2</TypeId>
<Amount>400.00</Amount>
</TaxSummary>
</TaxSummaries>
</AllTransactions>
<CurrentTransaction>
<Taxes>
<Tax>
<TypeId>1</TypeId>
<Amount>25.00</Amount>
</Tax>
<Tax>
<TypeId>1</TypeId>
<Amount>25.00</Amount>
</Tax>
<Tax>
<TypeId>2</TypeId>
<Amount>50.00</Amount>
</Tax>
<Tax>
<TypeId>2</TypeId>
<Amount>50.00</Amount>
</Tax>
<Taxes>
</CurrentTransaction>
Collection of TaxSummary within \AllTransaction\TaxSummaries and
Collection of Tax within CurrentTansaction\Taxes
I need to compare the sum of Amount by TypeId in \AllTransaction\TaxSummaries against the sum of Amount by TypeId in CurrentTansaction\Taxes using XSLT.

Related

How can I extract the first node value from a XMLTYPE?

I have a xml which is in a clob field of my table
This is the code I am currently using:
create table
xml_key
(
mycol number,
xml clob
);
insert into xml_key
values(1653,
'<AppHdr>
<Fr>
<FIId>
<FinInstnId>
<BICFI>CRESUS33</BICFI>
</FinInstnId>
</FIId>
</Fr>
<To>
<FIId>
<FinInstnId>
<BICFI>CAYTTRISXXX</BICFI>
</FinInstnId>
</FIId>
</To>
<BizMsgIdr>e9aab4a5-bc32-4548-93f6-aa47dd97d77</BizMsgIdr>
<MsgDefIdr>pac.004.001.09</MsgDefIdr>
<BizSvc>swift.cbprplus.02</BizSvc>
<CreDt>2019-08-12T13:59:26+01:00</CreDt>
<Rltd>
<Fr>
<FIId>
<FinInstnId>
<BICFI>LOYDGB21002</BICFI>
</FinInstnId>
</FIId>
</Fr>
<To>
<FIId>
<FinInstnId>
<BICFI>CRESUS33</BICFI>
</FinInstnId>
</FIId>
</To>
<BizMsgIdr>1234567890</BizMsgIdr>
<MsgDefIdr>pacs.008.001.08</MsgDefIdr>
<CreDt>2019-08-12T12:59:26+01:00</CreDt>
</Rltd>
</AppHdr>');
when i extract BIC code from xml i have a problem.
I don't want double nodes like CRESUS33LOYDGB21002
How can I extract only CRESUS33 as FromBIC from xml?
select XMLTYPE(t.xml).EXTRACT('//Fr/FIId/FinInstnId/BICFI/text()').getStringVal() fromBIC,
XMLTYPE(t.xml).EXTRACT('//Rltd/Fr/FIId/FinInstnId/BICFI/text()').getStringVal() RltdFromBIC
from xml_key t
where mycol=1653 ;
FROMBIC RLTDFROMBIC
--------------------------------- --------------------------------------
CRESUS33LOYDGB21002 LOYDGB21002
Thank you
The problem is you are using //Fr as a shortcut in both extract calls, and as Fr is a node both directly under AppHdr and under AppHdr/Rltd the first XPath matches both of those, and you end up with the values for both combined. The second Xpath is more selective so that only sees the second one.
If you give the full path for both then you won't have that issue:
select XMLTYPE(t.xml).EXTRACT('/AppHdr/Fr/FIId/FinInstnId/BICFI/text()').getStringVal() fromBIC,
XMLTYPE(t.xml).EXTRACT('/AppHdr/Rltd/Fr/FIId/FinInstnId/BICFI/text()').getStringVal() RltdFromBIC
from xml_key t
where mycol=1653 ;
You can also use XMLQuery:
select
XMLQuery('/AppHdr/Fr/FIId/FinInstnId/BICFI/text()'
passing XMLTYPE(t.xml)
returning content).getStringVal() as fromBIC,
XMLQuery('/AppHdr/Rltd/Fr/FIId/FinInstnId/BICFI/text()'
passing XMLTYPE(t.xml)
returning content).getStringVal() as RltdFromBIC
from xml_key t
where mycol=1653;
or XMLTable:
select x.fromBIC, x.RltdFromBIC
from xml_key t
cross apply XMLTable(
'/AppHdr'
passing XMLTYPE(t.xml)
columns
fromBIC path 'Fr/FIId/FinInstnId/BICFI',
RltdFromBIC path 'Rltd/Fr/FIId/FinInstnId/BICFI'
) x
where mycol=1653;
FROMBIC
RLTDFROMBIC
CRESUS33
LOYDGB21002
All three return
db<>fiddle
If you're still on a version before 12c then you can use cross join instead of cross apply:
select x.fromBIC, x.RltdFromBIC
from xml_key t
cross join XMLTable(
'/AppHdr'
passing XMLTYPE(t.xml)
columns
fromBIC path 'Fr/FIId/FinInstnId/BICFI',
RltdFromBIC path 'Rltd/Fr/FIId/FinInstnId/BICFI'
) x
where mycol=1653;
They aren't exactly the same but shouldn't make a difference here.
db<>fiddle

Stuck in T-SQL : XML to temp table

I have a little challenge where I am currently stuck in.
This is my query:
DECLARE #xml XML
SET #xml =
'<beginmsg>
<refmsg>A1234567</refmsg>
<shipments>
<shipment>
<load>
<address>
<name>Loadname</name>
<street>Street</street>
<zipcode>ABCDE</zipcode>
<city>Munchen</city>
<countrycode>DE</countrycode>
</address>
<dateTime>
<date>2021-02-02</date>
<timeFrom>08:00:00</timeFrom>
</dateTime>
</load>
<unload>
<address>
<name>unloadname</name>
<street>Street unload</street>
<zipcode>1111</zipcode>
<city>Dresden</city>
<countrycode>DE</countrycode>
</address>
</unload>
<goods>
<good>
<description>Cookies</description>
<quantity>1</quantity>
</good>
<good>
<description>Cookies</description>
<quantity>3</quantity>
</good>
<good>
<description>Some food</description>
<quantity>2</quantity>
</good>
</goods>
<barcodes>
<barcode>7829348742910092309325</barcode>
<barcode>7829348742112344114414</barcode>
<barcode>0984746166149566188446</barcode>
</barcodes>
<references>
<reference>GBP-4362</reference>
</references>
</shipment>
<shipment>
<load>
<address>
<name>shipment 2 load</name>
<street>Street load2</street>
<zipcode>1234 RR</zipcode>
<city>Koln</city>
<countrycode>DE</countrycode>
</address>
<dateTime>
<date>2021-03-03</date>
<timeFrom>10:00:00</timeFrom>
</dateTime>
</load>
<unload>
<address>
<name>Shipment 2 unload</name>
<street>Street 2 unl</street>
<zipcode>1000 AA</zipcode>
<city>Amsterdam</city>
<countrycode>NL</countrycode>
</address>
</unload>
<goods>
<good>
<description>Televisions</description>
<quantity>2</quantity>
</good>
</goods>
<barcodes>
<barcode>0984746166149566188446</barcode>
</barcodes>
<references>
<reference>HBR-7211CX</reference>
</references>
</shipment>
</shipments>
</beginmsg>'
IF OBJECT_ID('tempdb..#Goods') IS NOT NULL DROP TABLE #Goods;
CREATE TABLE #Goods
(
row INT,
shipmentreference VARCHAR(100),
description VARCHAR(MAX),
quantity INT,
barcode VARCHAR(MAX)
);
INSERT INTO #Goods
SELECT
"row" = ROW_NUMBER() OVER (PARTITION BY shipment.shipment ORDER BY shipment.shipment),
"shipmentreference" = CAST(shipment.shipment.value('(references/reference)[1]', 'VARCHAR(100)') AS VARCHAR(MAX)) + '-' + CAST(ROW_NUMBER() OVER (PARTITION BY shipment.shipment ORDER BY shipment.shipment) AS VARCHAR(MAX)),
"description" = goods.goods.value('(description)[1]', 'VARCHAR(10)'),
"quantity" = goods.goods.value('(quantity)[1]', 'VARCHAR(10)'),
"barcode" = barcode.barcode.value('(barcode)[1]', 'VARCHAR(100)')
FROM
#Xml.nodes('beginmsg/shipments/shipment') shipment(shipment)
CROSS APPLY
shipment.shipment.nodes('goods/good') goods(goods)
CROSS APPLY
shipment.shipment.nodes('barcodes') barcode(barcode)
SELECT * FROM #Goods
The XML contains 2 shipments.
In the 1st shipment there are 3 goods segment with each 1 'good' segment. In the 2nd shipment there is one good segment.
I would like to merge each good, with their information and with the 1st barcode for the 1st good, 2nd barcode for the 2nd good etc. etc.
So I was thinking to create a TEMP table #goods and a temp table to store the barcodes in. In both temp tables I would like to add the "reference" so I can join both tables on that unique field.
But at this moment I am stuck. In my final query (as shown above) the output is as follow:
row
reference
description
quantity
barcode
1
GBP-4362-1
Cookies
1
7829348742910092309325
2
GBP-4362-1
Cookies
3
7829348742910092309325
3
GBP-4362-1
Some food
2
7829348742910092309325
1
HBR-7211CX-1
Television
2
0984746166149566188446
So the barcode is each first (what is actually what I am querying :))
How can I iterate through these barcodes nodes and how can I manage to merge it into the row?

XML Comparison(String) in SQL server

I have two XML strings in two separate tables.
**ROOT**
**Node ID=** 1
**name** vignesh **/name**
**street** 1211 **/street**
**/Node**
**Node ID=** 2
**name** ram **/name**
**street** 333 **/street**
**/Node**
**/ROOT**
**ROOT**
**Node ID=** 1
**name** newbie **/name**
**street** 121223 **/street**
**/Node**
**Node ID=** 2
**name** pro **/name**
**street** 445**/street**
**/Node**
**/ROOT**
Please help me to find the comparison between these two XMLs in SQL Server and give result as Old value and New value for both ID1 and ID2(Node).
I believe this will help you out . i build a proper XML structure by considering your XML data.
DECLARE #XMLString1 XML =
'<ROOT>
<Nodes>
<NodeID>1</NodeID>
<name>vignesh </name>
<street>1211</street>
</Nodes>
<Nodes>
<NodeID>2</NodeID>
<name>ram</name>
<street>333</street>
</Nodes>
</ROOT>' ,
#XMLString2 XML =
'<ROOT>
<Nodes>
<NodeID>1</NodeID>
<name>newbie </name>
<street>121223</street>
</Nodes>
<Nodes>
<NodeID>2</NodeID>
<name>pro</name>
<street>445</street>
</Nodes>
</ROOT>'
SELECT #XMLString1,#XMLString2
;WITH XMLDATA1 AS
(
SELECT T.X.value('NodeID[1]','varchar(50)') AS NODEID,
T.X.value('name[1]','varchar(50)') AS NAME,
T.X.value('street[1]','varchar(50)') AS STREET
FROM #XMLString1.nodes('/ROOT/Nodes')T(X)
)
,XMLDATA2 AS
(
SELECT T.X.value('NodeID[1]','varchar(50)') AS NODEID,
T.X.value('name[1]','varchar(50)') AS NAME,
T.X.value('street[1]','varchar(50)') AS STREET
FROM #XMLString2.nodes('/ROOT/Nodes')T(X)
)
SELECT X1.NODEID,X1.NAME AS OLD_NAME,X2.NAME AS NEW_NAME , X1.STREET AS OLD_STREET , X2.STREET AS NEW_STREET
FROM XMLDATA1 X1
INNER JOIN XMLDATA2 X2 ON X1.NODEID=X2.NODEID

How do I select data from a XML column on SQL Server?

How do I select data from a XML column in a SQL Server table in SQL Server Management Studio?
I would like to get this result:
orderDdate createdBby orderNo currency taxation inv customer mail
2019-09-05 storefront 000001 USD gross 0099999 Jonh Smith JonhSmith#gmail.com
<orders xmlns="www address">
<order>
<order-date>2019-09-05</order-date>
<created-by>storefront</created-by>
<original-order-no>000001</original-order-no>
<currency>USD</currency>
<taxation>gross</taxation>
<invoice-no>0099999</invoice-no>
<customer>
<customer-name>Jonh Smith</customer-name>
<customer-email>JonhSmith#gmail.com</customer-email>
</customer>
<notes>
<note>
<created-by>system</created-by>
<creation-date>2019-09-06T07:05:03.000Z</creation-date>
<subject>Fulfilment Status</subject>
<text>The order fulfilment status was changed from '01' to '02'.</text>
</note>
<note>
<created-by>system</created-by>
<creation-date>2019-09-06T07:05:03.000Z</creation-date>
<subject>Fulfilment Status</subject>
<text>The order fulfilment status was changed from '02' to '03'.</text>
</note>
<note>
<created-by>system</created-by>
<creation-date>2019-09-06T07:05:03.000Z</creation-date>
<subject>Fulfilment Status</subject>
<text>The order fulfilment status was changed from '03' to '03'.</text>
</note>
</notes>
<product-lineitems>
<product-lineitem>
<product-id>0001</product-id>
<quantity unit="BOX">1.0</quantity>
<tax-rate>0.23</tax-rate>
</product-lineitem>
<product-lineitem>
<product-id>0002</product-id>
<quantity unit="PCS">1.0</quantity>
<tax-rate>0.23</tax-rate>
</product-lineitem>
</product-lineitems>
</order>
I have the last problem (i hope) ;)
How to return data from product-lineitems. I mean: Quantity, Quantity-unit
Im trying like that: But it's return duplicate rows :(
WITH XMLNAMESPACES(DEFAULT N'www address')
SELECT
o.value(N'(current-order-no/text())[1]',N'varchar(10)') AS OrderNo
,n.value(N'(quantity/text())[1]',N'varchar(10)') AS Qty
,u.value(N'#unit',N'varchar(10)') AS Unit
FROM
dbSupply.dbo.MyXmlTable t
CROSS APPLY
t.XMLData.nodes(N'/orders/order') A(o)
OUTER APPLY
A.o.nodes(N'product-lineitems/product-lineitem') B(n)
OUTER APPLY
A.o.nodes(N'product-lineitems/product-lineitem/quantity') C(u)
'It looks like your post is mostly code; please add some more details'
'It looks like your post is mostly code; please add some more details'
'It looks like your post is mostly code; please add some more details'
You can try it like this:
DECLARE #mockupTable TABLE(ID INT IDENTITY, YourXml XML);
INSERT INTO #mockupTable VALUES
(N'<orders xmlns="www address">
<order order-no="000001">
<order-date>2019-09-05</order-date>
<created-by>storefront</created-by>
<original-order-no>000001</original-order-no>
<currency>USD</currency>
<taxation>gross</taxation>
<invoice-no>0099999</invoice-no>
<customer>
<customer-name>Jonh Smith</customer-name>
<customer-email>JonhSmith#gmail.com</customer-email>
</customer>
</order>
</orders>');
WITH XMLNAMESPACES(DEFAULT N'www address')
SELECT o.value(N'#order-no',N'varchar(10)') AS OrderNo
,o.value(N'(order-date/text())[1]',N'date') AS OrderDate
,o.value(N'(created-by/text())[1]',N'varchar(100)') AS CreatedBy
,o.value(N'(original-order-no/text())[1]',N'varchar(10)') AS OriginalOrderNo
,o.value(N'(currency/text())[1]',N'varchar(10)') AS Currency
,o.value(N'(taxation/text())[1]',N'varchar(10)') AS Taxation
,o.value(N'(invoice-no/text())[1]',N'varchar(10)') AS InvoiceNo
,o.value(N'(customer/customer-name/text())[1]',N'varchar(100)') AS CustomerName
,o.value(N'(customer/customer-email/text())[1]',N'varchar(100)') AS CustomerEMail
FROM #mockupTable t
CROSS APPLY t.YourXml.nodes(N'/orders/order') A(o);
Be aware of the need to declare your (default) name space correctly. I used .nodes() because "orders" sounds like plural. Your sample includes just one order, but there might be more...
UPDATE: Your additional "notes"
In any case of 1:n relationship (many nodes related to one partent node), you need .nodes() in order to get each fragment as a separate row:
WITH XMLNAMESPACES(DEFAULT N'www address')
SELECT o.value(N'#order-no',N'varchar(10)') AS OrderNo
,o.value(N'(order-date/text())[1]',N'date') AS OrderDate
,o.value(N'(created-by/text())[1]',N'varchar(100)') AS CreatedBy
,o.value(N'(original-order-no/text())[1]',N'varchar(10)') AS OriginalOrderNo
,o.value(N'(currency/text())[1]',N'varchar(10)') AS Currency
,o.value(N'(taxation/text())[1]',N'varchar(10)') AS Taxation
,o.value(N'(invoice-no/text())[1]',N'varchar(10)') AS InvoiceNo
,o.value(N'(customer/customer-name/text())[1]',N'varchar(100)') AS CustomerName
,o.value(N'(customer/customer-email/text())[1]',N'varchar(100)') AS CustomerEMail
,n.value(N'(created-by/text())[1]',N'nvarchar(100)') AS Note_CreatedBy
,n.value(N'(creation-date/text())[1]',N'datetime') AS Note_CreatedBy
,n.value(N'(subject/text())[1]',N'nvarchar(100)') AS Note_CreatedBy
,n.value(N'(text/text())[1]',N'nvarchar(1000)') AS Note_CreatedBy
FROM #mockupTable t
CROSS APPLY t.YourXml.nodes(N'/orders/order') A(o)
OUTER APPLY A.o.nodes(N'notes/note') B(n);
The last line will pick the current order and pass it into .nodes(). This will return a derived set with one row per <note>.
You can use OPENROWSET
CREATE DATABASE MyXmlDataBase
GO
USE MyXmlDataBase
GO
CREATE TABLE MyXmlTable
(
Id INT IDENTITY PRIMARY KEY,
Name NVARCHAR(50),
XMLData XML
)
INSERT INTO MyXmlTable(Name, XMLData)
SELECT CONVERT(XML, BulkColumn) AS BulkColumn, GETDATE()
FROM OPENROWSET(BULK 'D:\MyXmlFileOnDisk.xml', SINGLE_BLOB) AS x;
SELECT * FROM MyXmlTable

Select Multiple Columns from XML Type column in MS SQL Server

I have used XML column for store data which can have dynamic nodes and attributes i.e. a Account node can have N number of nodes, I would like to retrieve those all nodes in single select query as multiple columns i.e. Col1, Col2, Col3 etc.
I tried below query but its returning vertical rows, I would like to have horizontal rows. Below is expected output. Col1, Col2 ..Nth Col
TAX-1 Amount|TAX-2 Amount|TAX-3 Amount...Nth Columns
1232 |4050 |57
Sample Data
DECLARE #XML XML
SET #XML='<Info>
<Demand>
<Tax ID="1">
<Head>1232</Head>
<Int>10</Int>
<OtherFee>5</OtherFee>
</Tax>
<Tax ID="2">
<Head>4050</Head>
<Int>10</Int>
<OtherFee>5</OtherFee>
</Tax>
<Tax ID="3">
<Head>57</Head>
<Int>10</Int>
<OtherFee>5</OtherFee>
</Tax>
</Demand>
<Collection>
<Tax ID="1">
<Head>500</Head>
<Int>10</Int>
<OtherFee>5</OtherFee>
</Tax>
<Tax ID="2">
<Head>800</Head>
<Int>10</Int>
<OtherFee>5</OtherFee>
</Tax>
</Collection>
</Info>'
Current query which is returning vertical output
SELECT T.C.value('Head[1]', 'varchar(max)') as Amount,
T.C.value('#ID', 'varchar(max)') as Head
FROM #XML.nodes('/Info/Demand/Tax') as T(C)
See SQL Fiddle with Demo
Any suggestion or input are most welcome!
Thanks
Suresh