Selecting element from xml using xmlquery in Db2 (SQL/XML) - sql

I have created table in Db2 like this:
create table xml_file(data xml not null)
This is the exact structure of xml:
<?xml version="1.0" encoding="UTF-8" ?>
<student id="20140021">
<name>Tom</name>
<surname>Johnson</surname>
<birth_date>"05/11/1995"</birth_date>
<birth_place>"Miami"</birth_place>
<points>9.45</points>
</student>
I want to select id, name, surname, and points for all students whose names are Ben and birth places are Chicago.
I wrote something like this:
select xmlquery('$DATA/student/data(#id)') as ID,
xmlquery('$DATA/student/name/text()') as NAME,
xmlquery('$DATA/student/surname/text()') as SURNAME,
xmlquery('$DATA/student/points/text()') as POINTS
from xml_file
where xmlexists('$DATA/student[birth_place = "Chicago"]')
and xmlexists('$DATA/student[name = "Ben"]');
All I got is this message: "FETCHED 0 RECORDS, 0 RECORDS SHOWN" (this is in IBM Data Studio).
Can someone tell me what did I do wrong?

Try this:
/*
WITH xml_file (data) AS
(
VALUES
XMLPARSE
(DOCUMENT '<?xml version="1.0" encoding="UTF-8" ?>
<student id="20140021">
<name>Tom</name>
<surname>Johnson</surname>
<birth_date>"05/11/1995"</birth_date>
<birth_place>"Miami"</birth_place>
<points>9.45</points>
</student>'
)
)
*/
SELECT X.*
FROM
xml_file V
, XMLTABLE
('$doc/student' PASSING V.data AS "doc"
COLUMNS
ID INT PATH '#id'
, NAME VARCHAR(20) PATH 'name'
, SURNAME VARCHAR(20) PATH 'surname'
, POINTS DEC(5, 2) PATH 'points'
) X
WHERE XMLEXISTS('$doc/student[birth_place = """Miami""" and name = "Tom"]' PASSING V.data AS "doc");

Try replacing your two where xmlexistss with one (this is used with your sample xml in the question, not your code):
where xmlexists('$DATA//student[birth_place/text()['Miami']][name/text()["Tom"]]');
or, two are required:
where xmlexists('$DATA/student[birth_place/text()['Miami']]')
and xmlexists('$DATA/student[name/text()["Tom"]]');
and see if either works.

The birth-place element contains double quotations marks which lead to an XPath evaluation error. To avoid this, replace where xmlexists('$DATA/student[birth_place = "Chicago"]') with one the following XPath expression :
XPath 1.0 friendly :
where xmlexists('$DATA/student/birth_place[substring(.,2,string-length(/student/birth_place)-2)="Chicago"]')
XPath 2.0 friendly :
where xmlexists('$DATA/student/birth_place[translate(.,codepoints-to-string(34),"")="Chicago"]')
XPath used to test on your sample data :
/student/birth_place[substring(.,2,string-length(/student/birth_place)-2)="Miami"]
/student/birth_place[translate(.,codepoints-to-string(34),"")="Miami"]

Related

Trying to extract a namespace value from an xmltype

my BLOB (converted to XMLType) contains the following :
<?xml version="1.0" encoding="UTF-8"?><S2SCTScf:SCTScfBlkCredTrf xsi:schemaLocation="urn:S2SCTScf:xsd:$SCTScfBlkCredTrf SCTScfBlkCredTrf.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:S2SCTScf="urn:S2SCTScf:xsd:$SCTScfBlkCredTrf"><S2SCTScf:SndgInst>EBAPFRPA</S2SCTScf:SndgInst><S2SCTScf:RcvgInst>NWBKGB2X</S2SCTScf:RcvgInst><S2SCTScf:SrvcId>SCT</S2SCTScf:SrvcId><S2SCTScf:TstCode>P</S2SCTScf:TstCode><S2SCTScf:FType>SCF</S2SCTScf:FType><S2SCTScf:FileRef>NSCT220610000729</S2SCTScf:FileRef><S2SCTScf:RoutingInd>IND</S2SCTScf:RoutingInd><S2SCTScf:FileBusDt>2022-06-10</S2SCTScf:FileBusDt><S2SCTScf:FileCycleNo>11</S2SCTScf:FileCycleNo><S2SCTScf:FIToFICstmrCdtTrf xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.02"><GrpHdr><MsgId>NSCT2206100007290000000000000000001</MsgId><CreDtTm>2022-06-10T09:52:54</CreDtTand
I wish to use SQL to extract the namespace value :
<S2SCTScf:FIToFICstmrCdtTrf xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.02">
How would I do that - I tried XMLQuery but couldn't get it to work.
Many thanks
You can use FLOWR to delete the child elements and just return that single element:
SELECT XMLQUERY(
'declare namespace S2SCTScf="urn:S2SCTScf:xsd:$SCTScfBlkCredTrf";
copy $e := /S2SCTScf:SCTScfBlkCredTrf/S2SCTScf:FIToFICstmrCdtTrf
modify (
for $i in $e/*
return delete node $i
)
return $e'
PASSING value RETURNING CONTENT
).getStringVal() AS xmlns_element
FROM table_name
Which, for the sample data:
CREATE TABLE table_name (value) AS
SELECT XMLTYPE('<?xml version="1.0" encoding="UTF-8"?>
<S2SCTScf:SCTScfBlkCredTrf xsi:schemaLocation="urn:S2SCTScf:xsd:$SCTScfBlkCredTrf SCTScfBlkCredTrf.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:S2SCTScf="urn:S2SCTScf:xsd:$SCTScfBlkCredTrf">
<S2SCTScf:SndgInst>EBAPFRPA</S2SCTScf:SndgInst>
<S2SCTScf:RcvgInst>NWBKGB2X</S2SCTScf:RcvgInst>
<S2SCTScf:SrvcId>SCT</S2SCTScf:SrvcId>
<S2SCTScf:TstCode>P</S2SCTScf:TstCode>
<S2SCTScf:FType>SCF</S2SCTScf:FType>
<S2SCTScf:FileRef>NSCT220610000729</S2SCTScf:FileRef>
<S2SCTScf:RoutingInd>IND</S2SCTScf:RoutingInd>
<S2SCTScf:FileBusDt>2022-06-10</S2SCTScf:FileBusDt>
<S2SCTScf:FileCycleNo>11</S2SCTScf:FileCycleNo>
<S2SCTScf:FIToFICstmrCdtTrf xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.02">
<GrpHdr>
<MsgId>NSCT2206100007290000000000000000001</MsgId>
<CreDtTm>2022-06-10T09:52:54</CreDtTm>
</GrpHdr>
</S2SCTScf:FIToFICstmrCdtTrf>
</S2SCTScf:SCTScfBlkCredTrf>') FROM DUAL;
Outputs:
XMLNS_ELEMENT
<S2SCTScf:FIToFICstmrCdtTrf xmlns:S2SCTScf="urn:S2SCTScf:xsd:$SCTScfBlkCredTrf" xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.02"/>
db<>fiddle here
As this and the XML in your previous question both have the GrpHdr node, and you were trying to the MsgId from it then, you might simply be trying to get the namespace so you can pass it back into another XMLQuery call.
If that is the case then you could ignore the namespaces and use wildcards:
with q1 (Tdata) as (
SELECT XMLtype(transportdata, nls_charset_id('AL32UTF8')) from paymentinterchange
)
select XMLQuery('//*:GrpHdr/*:MsgId/text()'
passing Tdata
returning content).getstringval()
from q1
which would get the message ID from this XML or the previous example. I've changed the character set to AL32UTF8 to match this XML's UTF-8 declaration, which could be a problem if you have a mix of encodings in your data.
db<>fiddle with the old and new XML.

Parse saved xml from table MSSQL server

I have a table with a column named xml. Table is text type but contains xml responses. I need 2 values from this column:
PL81300032102 from <ie801:Traderid>
Some Company sp. z o.o. from <ie801:TraderName>.
It is possible in SQL Server using a query?
<?xml version="1.0" encoding="UTF-8"?><EMCSToTrader xmlns="urn:publicid:-:PL:GOV:MF:EMCS:PHASE3:EMCS-TRADER:REQUEST:V1.00" xmlns:ie801="urn:publicid:-:EC:DGTAXUD:EMCS:PHASE3:IE801:V1.51" xmlns:tms="urn:publicid:-:EC:DGTAXUD:EMCS:PHASE3:TMS:V1.51" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Message><ie801:IE801>
<ie801:Header>
<tms:MessageSender>NDEA.PL</tms:MessageSender>
<tms:MessageRecipient>PL61300032004</tms:MessageRecipient>
<tms:DateOfPreparation>2018-07-17</tms:DateOfPreparation>
<tms:TimeOfPreparation>11:16:44.631</tms:TimeOfPreparation>
<tms:MessageIdentifier>PL#IE801#69474394</tms:MessageIdentifier>
</ie801:Header>
<ie801:Body>
<ie801:EADContainer>
<ie801:ConsigneeTrader language="pl">
<ie801:Traderid>PL81300032102</ie801:Traderid>
<ie801:TraderName>Some Company sp. z o.o.</ie801:TraderName> <...>
Table structure:
I was able to convert text data to xml type using:
SELECT TOP (10) * FROM (
SELECT CAST([xml] AS XML) AS xmlcontent
FROM [emcskomunikaty]
) det
Now trying to get value from xml.
I suppose you can do this:
SELECT
xmldata.value('declare namespace ns1="urn:publicid:-:EC:DGTAXUD:EMCS:PHASE3:IE801:V1.51"; (//ns1:Traderid)[1]', 'VARCHAR(100)') AS Traderid,
xmldata.value('declare namespace ns1="urn:publicid:-:EC:DGTAXUD:EMCS:PHASE3:IE801:V1.51"; (//ns1:TraderName)[1]', 'VARCHAR(100)') AS TraderName
FROM #t
CROSS APPLY (SELECT CAST(xml AS XML)) AS CA(xmldata)
The only tricky part here is handling the namespaces. If you choose ignore namespaces then just use //*:Traderid.

Reading XML Namespace using Oracle SQL

My XML Looks like below
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<wfm:Statement xmlns:wfm="http://example.org/sample/xsd/sampleStatement/2013/05" xmlns:wfmMerchant="http://www.eds.com/sample/xsd/wfmMerchant/2012/03"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<wfm:StatementParameters>
<wfmMerchant:HierarchyCd>012-12-002-107-050</wfmMerchant:HierarchyCd>
</wfm:StatementParameters>
<StatementAmount>27.140</StatementAmount>
</wfm:Statement>
I am trying to get the value of StatementAmount tag using Oracle query like below
select MS.MERCHANT,MS.CHAIN_HIERARCHY_CD,MS.CYCLE_DATE, X.StatementAmount
FROM CHAIN_STATMNT_HIST_XML MS
CROSS JOIN XMLTABLE(XMLNAMESPACES('http://example.org/sample/xsd/sampleStatement/2013/05' AS "wfm", 'http://www.eds.com/sample/xsd/wfmMerchant/2012/03' as wfmmerchant
default 'http://www.w3.org/2001/XMLSchema-instance')
,'/wfm:Statement/StatementAmount' passing xmltype(MS.XML_REPORT)
columns StatementAmount varchar(18) path '.')X
But, I am always getting NULL. I can able to successfully retrieve Hierarchy value from the XML which has namespace. But StatementAmount tag doesn't have any namespace and I have trouble retrieving it.
Can someone help with this issue ?
Your default namespace declaration seems to be causing the problem; without that (and ignoring wfmMerchant):
-- CTE for sample data
with CHAIN_STATMNT_HIST_XML (merchant, chain_hierarchy_cd, cycle_date, XML_REPORT) as (
select 1, 2, sysdate, '<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<wfm:Statement xmlns:wfm="http://example.org/sample/xsd/sampleStatement/2013/05" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<wfm:StatementParameters>
<!-- excluding this as namespace not provided -->
<!-- <wfmMerchant:HierarchyCd>012-12-002-107-050</wfmMerchant:HierarchyCd> -->
</wfm:StatementParameters>
<StatementAmount>27.140</StatementAmount>
</wfm:Statement>' from dual
)
-- actual query
select MS.MERCHANT,MS.CHAIN_HIERARCHY_CD,MS.CYCLE_DATE, X.StatementAmount
FROM CHAIN_STATMNT_HIST_XML MS
CROSS JOIN XMLTABLE(
XMLNAMESPACES('http://example.org/sample/xsd/sampleStatement/2013/05' AS "wfm"),
'/wfm:Statement/StatementAmount' passing xmltype(MS.XML_REPORT)
columns StatementAmount varchar(18) path '.'
) X
/
MERCHANT CHAIN_HIERARCHY_CD CYCLE_DATE STATEMENTAMOUNT
---------- ------------------ ---------- ------------------
1 2 2018-09-04 27.140
I'm not sure why you would use varchar2(18) as the datatype rather than number; and if there is only one statement amount per statement you could do:
select MS.MERCHANT,MS.CHAIN_HIERARCHY_CD,MS.CYCLE_DATE, X.StatementAmount
FROM CHAIN_STATMNT_HIST_XML MS
CROSS JOIN XMLTABLE(
XMLNAMESPACES('http://example.org/sample/xsd/sampleStatement/2013/05' AS "wfm"),
'/wfm:Statement' passing xmltype(MS.XML_REPORT)
columns StatementAmount number path 'StatementAmount'
) X

XML Parsing in Oracle

I am trying to parse an XML text. It is stored in a table t_testxml, in column xml_data which is CLOB type.
The xml looks like:
<?xml version="1.0" encoding="UTF-8"?>
<defaultmpftest:defaultmpftest xmlns:defaultmpftest="http://test.com"
test_id = "1231"
test_name = "name_test">
</mpftestdata:additionalLinkUrl xmlns:mpftestdata="http://test2.com"/>
</defaultmpftest:defaultmpftest>
How can I extract the values for test_id and test_name ?
I tried:
Select extract(xmltype.createxml(t.xml_data),'//defaultmpftest:defaultmpftest/#test_id').getStringVal() from t_testxml t;
But is not working. I get the following error:
ORA-31011: XML Parsing failed
LPX-00601: Invalid token in defaultmpftest:defaultmpftest/#test_id
Can you please give me some advices on this matter ?
Thank you !
The XML shown in the question is invalid, and would cause an "LPX-00231: invalid character" error if you passed it in to XMLType. So that isn't the string you're actually using. I'm assuming is a typo when posting the question, and you are actually getting the "LPX-00601: Invalid token" error you claimed. So I'll base this on that assumption, and on a string without that typo.
extract is deprecated; but even so, to use it here (with corrected raw XML) you need to specify the namespace with the optional third argument:
select extract(xmltype.createxml(t.xml_data),
'//defaultmpftest:defaultmpftest/#test_id',
'xmlns:defaultmpftest="http://test.com"').getStringVal()
from t_testxml t;
EXTRACT(XMLTYPE.CREATEXML(T.XML_DATA),'//DEFAULTMPFTEST:DEFAULTMPFTEST/#TEST_ID','XMLNS:DEFAULTMPFTEST="HTTP://TEST.COM"').GETSTRINGVAL()
-----------------------------------------------------------------------------------------------------------------------------------------
1231
Rather than using the deprecated function, you could use XMLQuery:
select xmlquery(
'declare namespace defaultmpftest="http://test.com"; (: :)
//defaultmpftest:defaultmpftest/#test_id'
passing xmltype.createxml(t.xml_data)
returning content).getStringVal()
from t_testxml t;
XMLQUERY('DECLARENAMESPACEDEFAULTMPFTEST="HTTP://TEST.COM";(::)//DEFAULTMPFTEST:DEFAULTMPFTEST/#TEST_ID'PASSINGXMLTYPE.CREATEXML(T.XML_DATA)RETURNINGCONTENT).GETSTRINGVAL()
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1231
You would need two XMLQuery clauses to get both values. I'd usually use XMLTable instead, shown here with the (fixed) XML-as-string provided via a CTE:
with t_testxml(xml_data) as (select '<?xml version="1.0" encoding="UTF-8"?>
<defaultmpftest:defaultmpftest xmlns:defaultmpftest="http://test.com"
test_id="1231"
test_name="name_test">
<mpftestdata:additionalLinkUrl xmlns:mpftestdata="http://test2.com"/>
</defaultmpftest:defaultmpftest>' from dual
)
select x.test_id, x.test_name
from t_testxml t
cross join xmltable(
xmlnamespaces('http://test.com' as "defaultmpftest"),
'//defaultmpftest:defaultmpftest'
passing xmltype(t.xml_data)
columns test_id number path '#test_id',
test_name varchar2(30) path '#test_name'
) x;
TEST_ID TEST_NAME
---------- ------------------------------
1231 name_test
Read more about using these functions.
The XML is not well-formed, try
<mpftestdata:additionalLinkUrl xmlns:mpftestdata="http://test2.com" />
or
<mpftestdata:additionalLinkUrl xmlns:mpftestdata="http://test2.com"></mpftestdata>
As Wernfried Domscheit correctly stated: Your XML is not well-formed. And for non-well-formed XMLs there's no way to extract information thereof in regular ways. Simply because regular ways are for XML; and your "XML" is not really an XML.
Let's try non-regular ways then...
with t_testxml as (
select q'{<?xml version="1.0" encoding="UTF-8"?>
<defaultmpftest:defaultmpftest xmlns:defaultmpftest="http://test.com"
test_id = "1231"
test_name = "name_test">
</mpftestdata:additionalLinkUrl xmlns:mpftestdata="http://test2.com"/>
</defaultmpftest:defaultmpftest>}' as xml_data
from dual
)
select xmlcast(xmlparse(content regexp_substr(T.xml_data, '<defaultmpftest:defaultmpftest[^>]+')||' />').extract('*/#test_id') as integer) as test_id
from t_testxml T
;

Check length of column in XMLTable

This is my XML in an XMLTYPE column:
<customers>
<customer>
<name>abc</name>
<surname>abc</surname>
<address>abc</address>
</customer>
<customer>
<name>abc</name>
<surname>abc</surname>
<address>abc</address>
</customer>
</customers>
I want that if the length of a field exceeds the allowed length, 15 charcaters, then use a default value instead.
In the guide for DB2 I found this example:
select ...from .., XMLTABLE
('$INFO/customerinfo*' passing
columns
city varchar(16) path a'addr/city(if(string-lenght(.)<=16) then . else "Error!")'
) ..
How can I do it in Oracle? This is my current XMLTABLE attempt:
select ..XMLTABLE
('customers/*' passing ..
columns
...
"address" varchar(15) path 'indirizzo/(if(lenght(.)<=15) then . else "Error!")'
) data;
But that gets:
ORA-19237. unable to resolve call to function -fn:length
You have typos and inconsistent naming so it's hard to tell exactly what you're really doing to get that error, but it works if you use the string-length() function from your DB2 example, rather than length() (or lenght()):
"address" varchar2(15) path 'address/(if(string-length(.)<=15) then . else "Error!")'
With modified data to trigger the error behaviour:
select *
from XMLTABLE ('customers/*'
passing xmltype('<customers>
<customer>
<name>abc</name>
<surname>abc</surname>
<address>abc def ghi jkl mno</address>
</customer>
<customer>
<name>abc</name>
<surname>abc</surname>
<address>abc</address>
</customer>
</customers>')
columns
"address" varchar(15) path 'address/(if(string-length(.)<=15) then . else "Error!")'
) data;
address
---------------
Error!
abc