How do I add values from cetain nodes - xslt-1.0

I have the following structure in a source XML-file:
<Measurements>
<Measurement>
<quantity>0.8</quantity>
<unit>flm</unit>
</Measurement>
<Measurement>
<quantity>2</quantity>
<unit>pl</unit>
</Measurement>
<Measurement>
<quantity>0</quantity>
<unit>st</unit>
</Measurement>
</Measurements>
I've created a template for mapping these into the target XML in various target nodes, however I need to add a node in the target that includes a total of the value "quantity" in the "measurement" with element "unit" "st" and "pl"
Is there a simple way to do this?

To sum all quantity values of measurements whose units are either st or pl you can use:
<xsl:value-of select="sum(/Measurements/Measurement[unit='st' or unit='pl']/quantity)"/>

Related

How to extract attribute value from XML in SQL Server 2019 (v15)?

I would need to extract elements from this XML into a tabular form, but I can't seem to get my head around how this would work on SQL Server via something like XQuery.
I have all the data in a temporary table called "#1" and the XML itself lies in a field called "Message" in that temporary table. How can I extract the values "Test1" and "2,2 %" into separate fields called "W08003" and "W1A081", respectively? The attribute names and the schema will remain the same over time? I would also need to do this on a row by row basis for each XML in the current temporary table.
<Individual xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Content>
<status xmlns:d3p1="http://www.uc.se/schemas/ucOrderReply/" xmlns="http://www.uc.se/schemas/ucOrderReply/" d3p1:result="ok" />
<uc xmlns="http://www.uc.se/schemas/ucOrderReply/">
<xmlReply>
<reports xmlns:d5p1="http://www.uc.se/schemas/ucOrderReply/" d5p1:lang="eng">
<d5p1:report d5p1:id="7605089247" d5p1:name="Test1 Test2" d5p1:styp="K39" d5p1:index="0">
<d5p1:group d5p1:id="W080" d5p1:index="0" d5p1:key="" d5p1:name="ID particulars">
<d5p1:term d5p1:id="W08001">9760508923</d5p1:term>
<d5p1:term d5p1:id="W08002">7605089277</d5p1:term>
<d5p1:term d5p1:id="W08003">Test1</d5p1:term>
<d5p1:term d5p1:id="W08004">Test2</d5p1:term>
</d5p1:group>
<d5p1:group d5p1:id="W1A0" d5p1:index="0" d5p1:key="" d5p1:name="UC RPB">
<d5p1:term d5p1:id="W1A003">000000000000000022</d5p1:term>
<d5p1:term d5p1:id="W1A081">2,2 %</d5p1:term>
<d5p1:term d5p1:id="W1A082">2,18839</d5p1:term>
</d5p1:group>
</d5p1:report>
</reports>
</xmlReply>
</uc>
</Content>
</Individual>
Current SQL code:
WITH XMLNAMESPACES('http://www.uc.se/schemas/ucOrderReply/' AS ns,'http://www.uc.se/schemas/ucOrderReply/' AS d5p1)
SELECT ok.*
,X.g.value('(#d5p1:id)','varchar(20)') AS id
,X.g.value('(text())[1]','varchar(20)') AS term
into #2
FROM #1 as ok
CROSS APPLY(ok.[Message].nodes('Individual/Content/ns:uc/ns:xmlReply/ns:reports/ns:report/ns:group/ns:term') X(g)
With no expected results, perhaps this is enough to get you started.
As you define a default namespace only once you get to status, you can't use a DEFAULT namespace in XMLNAMESPACES, so I name it ns and reference that instead. This gives you the value of all the terms and their id attribute:
DECLARE #XML xml = '<Individual xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Content>
<status xmlns:d3p1="http://www.uc.se/schemas/ucOrderReply/" xmlns="http://www.uc.se/schemas/ucOrderReply/" d3p1:result="ok" />
<uc xmlns="http://www.uc.se/schemas/ucOrderReply/">
<xmlReply>
<reports xmlns:d5p1="http://www.uc.se/schemas/ucOrderReply/" d5p1:lang="eng">
<report d5p1:id="7605089247" d5p1:name="Test1 Test2" d5p1:styp="K39" d5p1:index="0">
<group d5p1:id="W080" d5p1:index="0" d5p1:key="" d5p1:name="ID particulars">
<term d5p1:id="W08001">9760508923</term>
<term d5p1:id="W08002">7605089277</term>
<term d5p1:id="W08003">Test1</term>
<term d5p1:id="W08004">Test2</term>
</group>
<group d5p1:id="W1A0" d5p1:index="0" d5p1:key="" d5p1:name="UC RPB">
<term d5p1:id="W1A003">000000000000000022</term>
<term d5p1:id="W1A081">2,2 %</term>
<term d5p1:id="W1A082">2,18839</term>
</group>
</report>
</reports>
</xmlReply>
</uc>
</Content>
</Individual>';
WITH XMLNAMESPACES('http://www.uc.se/schemas/ucOrderReply/' AS ns,'http://www.uc.se/schemas/ucOrderReply/' AS d5p1)
SELECT X.g.value('(#d5p1:id)','varchar(20)') AS id,
X.g.value('(text())[1]','varchar(20)') AS term
FROM #XML.nodes('Individual/Content/ns:uc/ns:xmlReply/ns:reports/ns:report/ns:group/ns:term') X(g);
I note that the XML has been changed since the initial version I used to write this answer. This answer has not been (read "won't be") adjusted for that.

Query XML data from a table - Use schema collection?

I need to re-write an old DTS/SQL2000 process that exports xml data stored as a varchar from a table to an xml file only to read it back and populate some tables such as contract/customer/business activity. I want to query the xml column directly instead but always get Blank or NULL depending on the method I use and I am having a really having a hard time. My latest quick version based on another answer is....
SELECT
XML_MESSAGE.value('(Transaction_Id/text())[1]','varchar(100)') AS TransactionID
FROM dbo.XML_REPOSITORY t
OUTER APPLY t.XML_MESSAGE.nodes('Contract_Interface') AS bm(XMLData)
--OUTER APPLY bm.XML_MESSAGE.nodes('food') AS f(XMLData)
However, I have created an Schema collection based on the old xsd files for each collection (contract/customer etc) so the real question is how can I use that to extract the relevant data? A cut down version of the xml is below..
<Contract_Interface xmlns="http://company.com/backoffice/types/common" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://comapny.com/backoffice/types/common http://dataplace/xmlschemas/Policy_Transaction_Interface_1_1_9.xsd">
<Transaction_Id>48244272268</Transaction_Id>
<Source_System_Cd>SystemName</Source_System_Cd>
<Business_Activity>
<Contract_Id>169929</Contract_Id>
<Business_Activity_Type_Cd>01</Business_Activity_Type_Cd>
<Business_Activity_Type_Desc>New Binder</Business_Activity_Type_Desc>
<Out_Of_Sequence_Ind>N</Out_Of_Sequence_Ind>
<Effective_Dt>4/18/2021 12:00:00 AM</Effective_Dt>
<Expiration_Dt>4/18/2022 12:00:00 AM</Expiration_Dt>
<Premium_Amt>123678.00</Premium_Amt>
<Contract>
<Customer>
<Customer_Id>45678</Customer_Id>
<ODS_Customer_Id>6789</ODS_Customer_Id>
<Insured_Info_Overriden_Cd>N</Insured_Info_Overriden_Cd>
<Insured_Info_Overriden_Desc>No</Insured_Info_Overriden_Desc>
<Insured_Nm>Make Money PLC</Insured_Nm>
<Insured_Address>
<Address_Line_1>Alex Jones Terrace</Address_Line_1>
<Address_Line_2 />
<City_Nm>Houston</City_Nm>
<State_Cd>TX</State_Cd>
<State_Desc>Texas</State_Desc>
<Postal_Cd>77002</Postal_Cd>
<Country_Cd>US</Country_Cd>
<Country_Desc>United States</Country_Desc>
</Insured_Address>
</Customer>
<Producer>
<Producer_Id>33333</Producer_Id>
<ODS_Producer_Id>1234</ODS_Producer_Id>
<Producer_No>1234</Producer_No>
<Producer_Nm>Brokerage Limited</Producer_Nm>
<Billing_Address_Overridden_Ind>false</Billing_Address_Overridden_Ind>
<Producer_Billing_Address>
<Address_Line_1>Suite 1000</Address_Line_1>
<Address_Line_2>Free Road</Address_Line_2>
<City_Nm>Hamilton</City_Nm>
<State_Cd />
<State_Desc />
<Postal_Cd>HM11</Postal_Cd>
<Country_Cd>BM</Country_Cd>
<Country_Desc>Bermuda</Country_Desc>
</Producer_Billing_Address>
</Producer>
</Contract>
</Business_Activity>
</Contract_Interface>
As I mentioned, you need to define your Namespace. This uses a variable, instead of your table, but gives you the overlying idea:
DECLARE #XML xml = '<Contract_Interface xmlns="http://company.com/backoffice/types/common" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://comapny.com/backoffice/types/common http://dataplace/xmlschemas/Policy_Transaction_Interface_1_1_9.xsd">
<Transaction_Id>48244272268</Transaction_Id>
<Source_System_Cd>SystemName</Source_System_Cd>
<Business_Activity>
<Contract_Id>169929</Contract_Id>
<Business_Activity_Type_Cd>01</Business_Activity_Type_Cd>
<Business_Activity_Type_Desc>New Binder</Business_Activity_Type_Desc>
<Out_Of_Sequence_Ind>N</Out_Of_Sequence_Ind>
<Effective_Dt>4/18/2021 12:00:00 AM</Effective_Dt>
<Expiration_Dt>4/18/2022 12:00:00 AM</Expiration_Dt>
<Premium_Amt>123678.00</Premium_Amt>
<Contract>
<Customer>
<Customer_Id>45678</Customer_Id>
<ODS_Customer_Id>6789</ODS_Customer_Id>
<Insured_Info_Overriden_Cd>N</Insured_Info_Overriden_Cd>
<Insured_Info_Overriden_Desc>No</Insured_Info_Overriden_Desc>
<Insured_Nm>Make Money PLC</Insured_Nm>
<Insured_Address>
<Address_Line_1>Alex Jones Terrace</Address_Line_1>
<Address_Line_2 />
<City_Nm>Houston</City_Nm>
<State_Cd>TX</State_Cd>
<State_Desc>Texas</State_Desc>
<Postal_Cd>77002</Postal_Cd>
<Country_Cd>US</Country_Cd>
<Country_Desc>United States</Country_Desc>
</Insured_Address>
</Customer>
<Producer>
<Producer_Id>33333</Producer_Id>
<ODS_Producer_Id>1234</ODS_Producer_Id>
<Producer_No>1234</Producer_No>
<Producer_Nm>Brokerage Limited</Producer_Nm>
<Billing_Address_Overridden_Ind>false</Billing_Address_Overridden_Ind>
<Producer_Billing_Address>
<Address_Line_1>Suite 1000</Address_Line_1>
<Address_Line_2>Free Road</Address_Line_2>
<City_Nm>Hamilton</City_Nm>
<State_Cd />
<State_Desc />
<Postal_Cd>HM11</Postal_Cd>
<Country_Cd>BM</Country_Cd>
<Country_Desc>Bermuda</Country_Desc>
</Producer_Billing_Address>
</Producer>
</Contract>
</Business_Activity>
</Contract_Interface>';
WITH XMLNAMESPACES(DEFAULT 'http://company.com/backoffice/types/common')
SELECT bm.XMLData.value('(Transaction_Id/text())[1]','varchar(100)') AS TransactionID --Should this not be a int or bigint?
FROM #XML.nodes('Contract_Interface') AS bm(XMLData);

How to pull XML key "value" from SQL CLOB

I am attempting to extract information from XML stored in a CLOB column. I've searched the forums and thus far have been unable to get the data to pull as needed. I have a basic understanding of SQL but this is beyond me.
The XML is similar to the following:
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Header>
<OrderNum value="12354321"/>
<ExtractDate value="11-30-2012"/>
<RType value="Status"/>
<Company value="Company"/>
</Header>
<Body>
<Status>
<Order>
<ActivityType value="ValidateRequest"/>
<EndUser>
<Name value="Schmo, Joe"/>
<Address>
<SANO value="12345"/>
<SASN value="Mickey Mouse"/>
<SATH value="Lane"/>
<SASS value="N"/>
<City value="Orlando"/>
<State value="FL"/>
<Zip value="34786"/>
<Number value="5550000"/>
</Address>
</EndUser>
<COS value="1"/>
<TOS value="3"/>
<MainNumber value="5550000"/>
</Order>
<ErrorCode value="400"/>
<ErrorMessage value="RECEIVED"/>
</Status>
</Body>
</Response>
I want to get the values under "Address".
I've tried the following but it returns "NULL".
SELECT EXTRACTVALUE(XMLTYPE(RESPONSE_CLOB),'/Response/Body/Status/Order/EndUser/Address/SANO') AS SANO
FROM RESPONSE_TABLE
WHERE ROWNUM < 2
I am trying to get it so I can pull the "12345" assigned as "value" in "SANO" (ultimately getting the value for other fields, but want to at least get the one working first).
You're currently retrieving the text value of the node, but 12345 is the value attribute of the element rather than its text content. So you would need to use the #attribute syntax, i.e.:
SELECT EXTRACTVALUE(XMLTYPE(RESPONSE_CLOB),'/Response/Body/Status/Order/EndUser/Address/SANO/#value') AS SANO
FROM RESPONSE_TABLE
WHERE ROWNUM < 2;
SANO
--------------------
12345
But extractvalue is deprecated; assuming you're on a recent version of Oracle it would be better to use an XMLQuery:
SELECT XMLQUERY(
'/Response/Body/Status/Order/EndUser/Address/SANO/#value'
PASSING XMLTYPE(RESPONSE_CLOB)
RETURNING CONTENT
) AS SANO
FROM RESPONSE_TABLE
WHERE ROWNUM < 2;
You may find it even easier to use an XMLTable - necessary if an XML document has multiple Address nodes, but even with just one pulling the values out as columns is less repetitive, and it makes it easier to retrieve suitable data types:
select x.*
from response_table rt
cross join xmltable(
'/Response/Body/Status/Order/EndUser/Address'
passing xmltype(rt.response_clob)
columns sano number path 'SANO/#value',
sasn varchar2(30) path 'SASN/#value',
sath varchar2(10) path 'SATH/#value'
-- etc.
) x
where rownum < 2;
SANO SASN SATH
-------------------- ------------------------------ ----------
12345 Mickey Mouse Lane
Read more about using these functions to query XML data.

SQL Server - Querying XML DataType

I've imported several XML files whilst gathering data into a SQL Server table.
I need to query this XML data column to transform the single XML column into a more structured table.
Can someone please help with the syntax here, the XML structure has a pattern hence once understood in SQL XML.query format it should be easy to repeat for all elements I want.
Select TOP 1
[XMLFILE].value('(/Objs/Obj[0]/TN)[0]','VARCHAR(MAX)') as TEST,
[XMLFILE].query('/Objs/Obj') as HEADERTEST,
[XMLFILE].query('/Ref[#id="0"]') as Property1
FROM
SQL_Table
The above just returns an empty string for every pattern I try.
I need to extract the name and value of each obj [] element.
XML structure example i.e. objs header followed by obj nodes 0 through to 'x' nodes.
<?xml version="1.0"?>
<Objs xmlns="xxxx" Version="1.1.0.1">
<Obj RefId="0">
<TN RefId="0">
<T>xxxx/T>
<T>xxxxx</T>
<T>xxxx</T>
</TN>
<MS>
<S N="Name">xxxx</S>
<S N="Value">xxxx</S>
</MS>
</Obj>
<Obj RefId="1">
<TNRef RefId="0"/>
<MS>
<S N="Name">xxxxxx</S>
<S N="Value"/>
</MS>
</Obj>
<Obj RefId="2">
<TNRef RefId="0"/>
<MS>
<S N="Name">xxxxx</S>
<S N="Value"/>
</MS>
</Obj>
<Obj RefId="3">
<TNRef RefId="0"/>
<MS>
<S N="Name">xxxx</S>
<S N="Value"/>
</MS>
</Obj>
......
......
</Objs>
Couple of problems:
you're not respecting the XML namespace defined on your XML top level node
you're not getting the appropriate nodes from your XML
Try this:
-- replace the 'xxx' with whatever is really defined in your <Objs xmlns=....>
;WITH XMLNAMESPACES(DEFAULT 'xxxx')
SELECT
RefId = xc.value('#RefId', 'int'),
Name = xc.value('(MS/S[#N="Name"])[1]', 'varchar(20)'),
Value = xc.value('(MS/S[#N="Value"])[1]', 'varchar(20)')
FROM
SQL_Table
CROSS APPLY
XmlFile.nodes('/Objs/Obj') AS XT(XC)
The CROSS APPLY generates an on-the-fly virtual table of XML fragments for each XML node that matches the XPath expression - here, for each <Obj> node under <Objs>.
The XQuery method .value() then "reaches" into that XML fragment (for each of the fragements in that virtual table) and pulls out the necessary desired info - here the RefId attribute on the <Obj> node, as well as the textual value of the <MS>/<S N="Name"> and <MS>/<S N="Value"> subnodes.

Extracting XML data in SQL - too many cross apply statements

I have an xml document containing details from a Statement:
<Statement>
<Id />
<Invoices>
<Invoice>
<Id />
<Date />
<AmountDue />
etc.
</Invoice>
<Invoice>
<Id />
<Date />
<AmountDue />
etc.
</Invoice>
<Invoice>
<Id />
<Date />
<AmountDue />
etc.
</Invoice>
</Invoices>
</Statement>
This works fine for the Statement specific details:
SET #statementId = #xml.value('(Id)[1]', 'UNIQUEIDENTIFIER');
but it requires a singleton, and only returns the first value. I need ALL of the values for the invoices, not just the first so a singleton won't work.
I am able to get the information out using cross apply statements like this:
SELECT
#statementId AS STATEMENT_ID
Id.value('.', 'uniqueidentifier') AS INVOICE_ID
Date.value('.', 'smalldatetime') AS INVOICE_DATE
Due.value('.', 'decimal') AS INVOICE_AMOUNT_DUE
FROM #xml.nodes('Statement') A(S)
cross apply S.nodes('Invoices/Invoice') B(InvoiceD)
cross apply InvoiceD.nodes('Id') C(Id)
cross apply InvoiceD.nodes('Date') D(Date)
cross apply InvoiceD.nodes('AmountDue') E(Due)
This returns an Id, date, and amount from each Invoice in the Statement - perfect.
My problem comes when I try to extract all of the invoice details. I currently have seven cross apply statements and I got the following message:
"The query processor ran out of internal resources and could not
produce a query plan. This is a rare event and only expected for
extremely complex queries or queries that reference a very large
number of tables or partitions. Please simplify the query. If you
believe you have received this message in error, contact Customer
Support Services for more information."
What I want to do is have one cross apply for the Invoice and narrow down the exact field in the select statement, but unless I use '.' I must make the statement return a singleton and I don't get all of the data that I need.
I have done some research about specifying a namespace within the select statement, but all of the examples set the namespace to be an http address instead of a node in an xml document and I haven't gotten anything to return yet using this approach.
The result I'm looking for is something like this, but with more Invoice Details:
STATEMENT_ID INVOICE_ID INVOICE_DATE INVOICE_AMOUNT_DUE ...
Statement-1-Id Invoice-1-Id Invoice-1-Date Invoice-1-AmountDue ...
Statement-1-Id Invoice-2-Id Invoice-2-Date Invoice-2-AmountDue ...
Statement-1-Id Invoice-3-Id Invoice-3-Date Invoice-3-AmountDue ...
Where should I go from here?
EDIT: I removed some unnecessary information. Getting all of the invoice-specific details is my goal here.
select #XML.value('(Statement/Id/text())[1]', 'uniqueidentifier') as StatementId,
T.N.value('(Id/text())[1]', 'uniqueidentifier') as InvoiceId,
T.N.value('(Date/text())[1]', 'smalldatetime') as InvoiceDate,
T.N.value('(AmountDue/text())[1]', 'decimal') as AmountDue
from #XML.nodes('/Statement/Invoices/Invoice') as T(N)
.nodes will shred your XML to rows so that each row T.N is pointing to an Invoice node of its own. On that node there is only a single Id node so fetching the value specifying a singleton Id[1] works.
You can use Id[1] or (Id/text())[1] but the latter will give you a more efficient execution plan.