XMLQuery to get data - sql

I have the following SQL :
with q1 (Tdata) as (
SELECT XMLtype(transportdata, nls_charset_id('AL32UTF8'))
from bph_owner.paymentinterchange pint
where --PINT.INCOMING = 'T' and
PINT.TRANSPORTTIME >= to_date('2022-08-10', 'yyyy-mm-dd')
and pint.fileformat = 'pain.001.001.03'
)
--select XMLQuery('//*:GrpHdr/*:InitgPty/Nm/text()'
select tdata, XMLQuery('//*:GrpHdr/*:CtrlSum/text()'
passing Tdata
returning content).getstringval()
,
XMLQuery('//*:GrpHdr/*:MsgId/text()'
passing Tdata
returning content).getstringval()
from q1;
This works but for the InitgPty/Nm/ it doesn't - anybody know how I can extract this information?
Please be gentle as I don't work with XML much.
Thanks

Based on sample data from a previous question and other relating to this XML schema, it appears you just need to wildcard the namespace for Nm node, as well as its parents:
'//*:GrpHdr/*:InitgPty/*:Nm/text()'
so the query could become:
with q1 (Tdata) as (
SELECT XMLtype(transportdata, nls_charset_id('AL32UTF8'))
from paymentinterchange pint
where --PINT.INCOMING = 'T' and
PINT.TRANSPORTTIME >= to_date('2022-08-10', 'yyyy-mm-dd')
and pint.fileformat = 'pain.001.001.03'
)
select
XMLQuery('//*:GrpHdr/*:InitgPty/*:Nm/text()'
passing Tdata
returning content).getstringval() as nm
,
XMLQuery('//*:GrpHdr/*:CtrlSum/text()'
passing Tdata
returning content).getstringval() as ctrlsum
,
XMLQuery('//*:GrpHdr/*:MsgId/text()'
passing Tdata
returning content).getstringval() as msgig
from q1;
But if you're pulling multiple values out of the same document then it's simpler to use XMLTable rather than multiple XMLQuery calls:
select x.nm, x.ctrlsum, x.msgid
from paymentinterchange pint
cross apply XMLTable(
'//*:GrpHdr'
passing XMLtype(pint.transportdata, nls_charset_id('AL32UTF8'))
columns nm varchar2(50) path '*:InitgPty/*:Nm',
ctrlsum number path '*:CtrlSum',
msgid varchar2(50) path '*:MsgId'
) x
where pint.transporttime >= date '2022-08-10'
and pint.fileformat = 'pain.001.001.03';
db<>fiddle with data adapted from a previous question.
Before Oracle 12c you can use cross join instead of cross apply. I don't think it would make a difference in later versions either, that just seems to be my default now...
select x.nm, x.ctrlsum, x.msgid
from paymentinterchange pint
cross join XMLTable(
'//*:GrpHdr'
passing XMLtype(pint.transportdata, nls_charset_id('AL32UTF8'))
columns nm varchar2(50) path '//*:InitgPty/*:Nm',
ctrlsum number path '//*:CtrlSum',
msgid varchar2(50) path '//*:MsgId'
) x
where pint.transporttime >= date '2022-08-12'
and pint.fileformat = 'pain.001.001.03';
db<>fiddle showing it in 11.2.0.2, where it seems to need the // at the start of all the paths, including in the columns clause - those aren't needed in later versions, but here it returns nulls without them.

Related

How to fix SQL error SAP HANA while using aggregate function

If I run this sql in SAP HANA, It generates the following error.
(SQL Editor) Could not execute 'WITH l1 AS( SELECT REMOTE_SOURCE_NAME,OWNER,NAME,MAX(START_TIME) START_TIME,RESULT_STATE FROM ...'
Error: (dberror) 266 - inconsistent datatype: only numeric type is available for SUM/AVG/STDDEV/VAR function: line 43 col 7 (at pos 654)
If I exclude this part of sql select sum(LOADING_TIME) AS TOTAL_LOADING_TIME, then it works.
How can I solve the issue to get total_loading_time?
WITH l1 AS(
SELECT REMOTE_SOURCE_NAME,OWNER,NAME,MAX(START_TIME) START_TIME,RESULT_STATE
FROM "MEAG_EIM_SHARED"."meag.eim.shared::replication.Log"
WHERE TYPE = 'INITIAL' AND RESULT_STATE = 'COMPLETED'
AND REMOTE_SOURCE_NAME='RS_SDI_IMMO'
group by REMOTE_SOURCE_NAME,OWNER,NAME,RESULT_STATE
order by NAME ASC),
l2 AS
(SELECT REMOTE_SOURCE_NAME,OWNER,NAME,START_TIME,END_TIME,RESULT_STATE
FROM "MEAG_EIM_SHARED"."meag.eim.shared::replication.Log"
WHERE TYPE = 'INITIAL' AND RESULT_STATE = 'COMPLETED'
AND REMOTE_SOURCE_NAME='RS_SDI_IMMO'
group by REMOTE_SOURCE_NAME,OWNER,NAME,RESULT_STATE,START_TIME,END_TIME
order by NAME ASC)
select sum(LOADING_TIME) AS TOTAL_LOADING_TIME
from(
select layer1.REMOTE_SOURCE_NAME,layer1.OWNER,layer1.name,layer1.RESULT_STATE,layer1.start_time start_time,layer2.end_time end_time,
SECONDS_BETWEEN(layer1.START_TIME,layer2.END_TIME) || 's' LOADING_TIME
from l1 layer1 inner join l2 AS layer2
on layer1.start_time=layer2.start_time
order by name);
LOADING_TIME has the character „s“ concatenated to it - that turns the numeric value into a string.
And for strings there is no SUM() function.
If having the „s“ is important then adding it after the summation will fix the issue.
Otherwise, adding the unit denomination (I guess the s stands for seconds) to the column name would allow client tools to still work with the numeric value (I.e. when sorting)

Extract values from xmlType

I know this has been asked before, but I'm not able to monkey-see-monkey-do with my formatting
WITH TEST_XML_EXTRACT AS
(
SELECT XMLTYPE (
'<tns:Envelope xmlns:tns="http://schemas.xmlsoap.org/soap/envelope/">
<testLeaf> ValueIWant </testLeaf>
</tns:Envelope>') testField
FROM dual
)
SELECT EXTRACTVALUE(testField,'/testLeaf'), -- doesn't work
EXTRACTVALUE(testField,'/tns'), -- doesn't work
EXTRACTVALUE(testField,'/Envelope'), -- doesn't work
EXTRACTVALUE(testField,'/BIPIBITY') -- doesn't work
FROM TEST_XML_EXTRACT;
It just returns blank.
And I can't find any exactly similar examples from the Oracle docs.
Any thoughts?
There's only testLeaf node as a decently defined one in the XPath expression. So , only that could be extracted by using extractValue() function in such a way below :
with test_xml_extract( testField ) as
(
select
XMLType(
'<tns:Envelope xmlns:tns="http://schemas.xmlsoap.org/soap/envelope/">
<testLeaf> ValueIWant </testLeaf>
</tns:Envelope>'
)
from dual
)
select extractValue(value(t), 'testLeaf') as testLeaf,
extractValue(value(t), 'tns') as tns,
extractValue(value(t), 'Envelope') as Envelope,
extractValue(value(t), 'BIPIBITY') as BIPIBITY
from test_xml_extract t,
table(XMLSequence(t.testField.extract('//testLeaf'))) t;
TESTLEAF TNS ENVELOPE BIPIBITY
---------- ------- ---------- ----------
ValueIWant
Demo
You might do better passing the namespace to the extract process
WITH TEST_XML_EXTRACT AS
( SELECT XMLTYPE (
'<tns:Envelope xmlns:tns="http://schemas.xmlsoap.org/soap/envelope/">
<testLeaf> ValueIWant </testLeaf>
</tns:Envelope>') testField
FROM dual)
select t.testField.extract('/tns:Envelope/testLeaf',
'xmlns:tns="http://schemas.xmlsoap.org/soap/envelope/"').getstringval() val,
EXTRACTVALUE(t.testField,'/tns:Envelope/testLeaf', 'xmlns:tns="http://schemas.xmlsoap.org/soap/envelope/"') extval,
EXTRACTVALUE(t.testField,'/*/testLeaf', 'xmlns:tns="http://schemas.xmlsoap.org/soap/envelope/"') extval_wc
from TEST_XML_EXTRACT t;

Validating Phone Numbers in Batch with PostgreSQL

This is my SQL:
SELECT
countries.locl_ctry_id,
countries.icc,
countries.active,
networks.locl_ntwrk_id,
networks.locl_ctry_id,
numberings.locl_ntwrk_id,
numberings.ndc,
numberings.size
FROM countries
LEFT JOIN networks
ON networks.locl_ctry_id = countries.locl_ctry_id
LEFT JOIN numberings
ON numberings.locl_ntwrk_id = networks.locl_ntwrk_id
WHERE
countries.active = 'true'
AND numberings.locl_ntwrk_id NOTNULL
AND CONCAT(countries.icc, numberings.ndc)
LIKE LEFT('381645554330', CHAR_LENGTH(CONCAT(countries.icc, numberings.ndc)))
AND LENGTH('381645554330') = numberings.size
I would like to run this script for a batch of numbers, for example:
381645554330 ‭
381629000814‬‬
381644446555‬
‭38975300155‬
‭38975604099 ‭
38976330923‬‬ ‭
38977772090‬ ‭
38978250177‬ ‭
38970333730‬
‭38971388262‬
‭38972228855‬
Take a look at the database structure here: http://sqlfiddle.com/#!17/13ce29/27
It needs to validate the Prefix as well as the Length of the number.
Any suggestions how to achieve this?
Put the batch of numbers in a union all subquery.
SELECT
countries.locl_ctry_id,
countries.icc,
countries.active,
networks.locl_ntwrk_id,
networks.locl_ctry_id,
numberings.locl_ntwrk_id,
numberings.ndc,
numberings.size
FROM countries
LEFT JOIN networks
ON networks.locl_ctry_id = countries.locl_ctry_id
LEFT JOIN numberings
ON numberings.locl_ntwrk_id = networks.locl_ntwrk_id
JOIN ( select '381645554330' as num
union all
select '38976330923‬‬‬‬'
union all
select '38975300155‬‬‬' ) batch_numbers
ON CONCAT(countries.icc, numberings.ndc)
LIKE LEFT(batch_numbers.num, CHAR_LENGTH(CONCAT(countries.icc, numberings.ndc)))
AND LENGTH(batch_numbers.num) = numberings.size
WHERE
countries.active = 'true'
AND numberings.locl_ntwrk_id NOTNULL
It seems the objective is not ability to return the set of values currently returned by the single, but to make an evaluation of the of multiple values. The issue with the above it requires an a-priori knowledge of and a modification to the query for each set to evaluate. The follow will attempt to remove that requirement.
Let's begin by developing a base line query as an extension to Jakup's "union" solution.
--- create a baseline solution
with to_be_validated (test_num) as -- CTE used strictly as data generator fir query
( values ('381645554330')
, ('381629000814')
, ('381644446555')
, ('38975300155')
, ('38975604099')
, ('38976330923')
, ('38977772090')
, ('38978250177')
, ('38970333730')
, ('38971388262')
, ('38972228855')
, ('81771388262')
, ('55572228855')
)
--- base query
select test_num
, case when icc is not null then 'Valid' else 'Invalid' end validation
from to_be_validated
left join(
select countries.icc, numberings.ndc, numberings.size
from countries
join networks on networks.locl_ctry_id = countries.locl_ctry_id
join numberings on numberings.locl_ntwrk_id = networks.locl_ntwrk_id
) base on ( concat(base.icc, base.ndc) = left( test_num, char_length(concat(base.icc, base.ndc)))
and length(test_num) = base.size
)
;
Notes on Query and Modifications:
1. the column countries.active is defined as binary, thus already providing a True/False value. Thus checking for "= 'true' is unnecessary. Altered to just contries.active.
2. The column numberings.locl_ntwrk_id is restricted to being NOT NULL, so the Predicate "nullnumberings.locl_ntwrk_id NOTNULL" is always true. Removed predicate.
3. The LEFT JOIN on networks and numberings will generate a result set with all countries, all networks, all numberings, even when the combination is itself invalid. This results in validating each phone number against every combination of the 3 base tables. Alter these inner joins.
4. Finally, I added a couple extra numbers to your test data. These are intended to fail the desired validation. You should always test with considerable invalid data, otherwise you cannot know if procedure/query/whatever gracefully and properly handles it.
Now with a base query in hand it's possible to just end here. But to be generally useful you cannot edit the query each time it wanted. Therefore lets wrap a function definition around it. We'll do this by wrapping a function definition around that base query, and provide either an array or a delimited string containing the phone numbers to b evaluated.
In each the base query remains the same, and we keep the CTE, but the CTE is modified to build a row for each phone number provided.
-- SQL Function with and Array input
create or replace function validate_phone_numbers( phone_numbers text[])
returns table ( phone_number text
, validation_status text
)
language sql
as $$
with to_be_validated as
( select unnest (phone_numbers) test_num )
-- Insert base query here --
$$
-- Test with Array
select phone_number, validation_status
from validate_phone_numbers (ARRAY
[ ('381629000814')
, ('381644446555')
, ('38975300155')
, ('38975604099')
, ('38976330923')
, ('38977772090')
, ('38978250177')
, ('38970333730')
, ('38971388262')
, ('38972228855')
, ('81771388262')
, ('55572228855')
]
) ;
With a minor extension we a delimited string version.
create or replace function validate_phone_numbers_with_string( phone_numbers text, delimiter text default ',')
returns table ( phone_number text
, validation_status text
)
language sql
as $$
with to_be_validated as
( select unnest (string_to_array (phone_numbers, delimiter)) test_num)
-- Insert base query here --
$$ ;
-- test with string
select phone_number, validation_status
from validate_phone_numbers_with_string('381629000814,381644446555,38975300155,38975604099,38976330923,38977772090,38978250177,38970333730,38971388262,38972228855,81771388262,55572228855');

how to get data from different levels in a xmltable?

I'm trying to get the values of two attributes from table MVR_DTL in column VENDOR_XML. VENDOR_XML is of datatype clob and contains an xml that looks like this
<MVRCHPINFF_1.0>
<Routing ReplyToQMgr="PQ21" ReplyToQ="A4218QA.BIZTALK.REPLY.REPORT.PROD" CorelId="712393102361590" MsgType="8" Expiry="-1" MsgID="201904051632015"></Routing>
<MVRRecLoop>
<CLoop>
<CRec>
<C_MVRNumberAddr>ROMAN GENERAL</C_MVRNumberAddr>
</CRec>
<CRec>
<C_MVRNumberAddr>ROMAN ST</C_MVRNumberAddr>
</CRec>
<CRec>
<C_MVRNumberAddr>ROMAN CITY, ROME 111111</C_MVRNumberAddr>
</CRec>
</CLoop>
<HIJLoop>
<JRec>
<J_SVCDesc>MVR RECORD CLEAR</J_SVCDesc>
</JRec>
</HIJLoop>
</MVRRecLoop>
</MVRCHPINFF_1.0>
I tried running
SELECT c.J_SVCDesc, c.XMLDetails from MVR_DTL M,
XMLTABLE(
'string-join(/MVRCHPINFF_1.0/MVRRecLoop/CLoop/CRec/C_MVRNumberAddr, "|")'
passing XMLTYPE(M.VENDOR_XML)
columns XMLDetails varchar2(200) PATH '.',
J_SVCDesc varchar2(50) PATH './../../../HIJLoop/JRec/J_SVCDesc') c;
and i get this error
Error during Execute
S1000(19112)[Oracle][ODBC][Ora]ORA-19112: error raised during evaluation:
XVM-01020: [XPTY0020] The path step context item is not a node
I also tried
SELECT x1.J_SVCDesc, x2.XMLDetails from MVR_DTL M,
XMLTABLE('/MVRCHPINFF_1.0/MVRRecLoop'
passing XMLTYPE(M.VENDOR_XML)
columns
Address XMLTYPE path './CLoop/CRec/C_MVRNumberAddr',
J_SVCDesc varchar(50) PATH './HIJLoop/JRec/J_SVCDesc') x1
CROSS JOIN XMLTable(
'string-join(., "|")'
PASSING x1.Address
COLUMNS XMLDetails varchar2(200) PATH '.') x2;
but errored out with
Error during Execute
S1000(19279)[Oracle][ODBC][Ora]ORA-19279: XPTY0004 - XQuery dynamic type mismatch:
expected singleton sequence - got multi-item sequence
I'm trying to get
J_SVCDESC XMLDETAILS
MVR RECORD CLEAR ROMAN GENERAL|ROMAN ST|ROMAN CITY, ROME 111111
Could someone help me figure out what I'm missing.
You can move the string-join down to the columns clause:
select x.j_svcdesc, x.xmldetails
from mvr_dtl m
cross join xmltable (
'/MVRCHPINFF_1.0/MVRRecLoop'
passing xmltype(m.vendor_xml)
columns J_SVCDesc varchar2(50) path 'HIJLoop/JRec/J_SVCDesc',
xmldetails varchar2(200) path 'string-join(CLoop/CRec/C_MVRNumberAddr, "|")'
) x
Demo with your sample data in a CTE:
with mvr_dtl (vendor_xml) as (
select to_clob('<MVRCHPINFF_1.0>
<Routing ReplyToQMgr="PQ21" ReplyToQ="A4218QA.BIZTALK.REPLY.REPORT.PROD" CorelId="712393102361590" MsgType="8" Expiry="-1" MsgID="201904051632015"></Routing>
<MVRRecLoop>
<CLoop>
<CRec>
<C_MVRNumberAddr>ROMAN GENERAL</C_MVRNumberAddr>
</CRec>
<CRec>
<C_MVRNumberAddr>ROMAN ST</C_MVRNumberAddr>
</CRec>
<CRec>
<C_MVRNumberAddr>ROMAN CITY, ROME 111111</C_MVRNumberAddr>
</CRec>
</CLoop>
<HIJLoop>
<JRec>
<J_SVCDesc>MVR RECORD CLEAR</J_SVCDesc>
</JRec>
</HIJLoop>
</MVRRecLoop>
</MVRCHPINFF_1.0>')
from dual
)
select x.j_svcdesc, x.xmldetails
from mvr_dtl m
cross join xmltable (
'/MVRCHPINFF_1.0/MVRRecLoop'
passing xmltype(m.vendor_xml)
columns J_SVCDesc varchar2(50) path 'HIJLoop/JRec/J_SVCDesc',
xmldetails varchar2(200) path 'string-join(CLoop/CRec/C_MVRNumberAddr, "|")'
) x;
J_SVCDESC XMLDETAILS
-------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
MVR RECORD CLEAR ROMAN GENERAL|ROMAN ST|ROMAN CITY, ROME 111111
If the HIJLoop node name suggests there could be multiple JRec values too then you could concatenate those too:
columns J_SVCDesc varchar2(50) path 'string-join(HIJLoop/JRec/J_SVCDesc, "|")',
xmldetails varchar2(200) path 'string-join(CLoop/CRec/C_MVRNumberAddr, "|")'
which makes no difference to the output with the sample XML.
db<>fiddle
Incidentally, your second attempt sort of works; it doesn't error but doesn't get the right result either. The address you pass is an XML fragment with just sibling nodes, and the string-join only sees one value, consisting of the text() from those (I think... something like that). If you pass the CLoop down instead and expand the second XPath then it works:
select x1.j_svcdesc, x2.xmldetails
from mvr_dtl m
cross join xmltable (
'/MVRCHPINFF_1.0/MVRRecLoop'
passing xmltype(m.vendor_xml)
columns J_SVCDesc varchar(50) path 'HIJLoop/JRec/J_SVCDesc',
HIJLoop xmltype path 'CLoop'
) x1
cross join xmltable (
'string-join(CLoop/CRec/C_MVRNumberAddr, "|")'
passing x1.HIJLoop
columns xmldetails varchar2(200) path '.'
) x2;
db<>fiddle
But if you were actually getting "ORA-19279: XPTY0004 - XQuery dynamic type mismatch: expected singleton sequence - got multi-item sequence" then I suspect you have data that does actually have multiple JRec nodes; in which case see my second query above.
db<>fiddle showing that issue with this approach and my first query; and it working with my second query. So you probably need to use that:
select x.j_svcdesc, x.xmldetails
from mvr_dtl m
cross join xmltable (
'/MVRCHPINFF_1.0/MVRRecLoop'
passing xmltype(m.vendor_xml)
columns J_SVCDesc varchar2(50) path 'string-join(HIJLoop/JRec/J_SVCDesc, "|")',
xmldetails varchar2(200) path 'string-join(CLoop/CRec/C_MVRNumberAddr, "|")'
) x;
You might need to increase the returned column sizes, too.

Sql query with cross join XMLTABLE

Help needed to extract the data below from XML messages. I have table which contains the xml message in clob data type. I am trying using below query but it is not returning any data . I need to extract all the values from xml message.
<iORDERS:iORDERS xmlns:iORDERS="urn:iORDERS-abcdonline-com:Integration:v1">
<ORDER_NOTIFY>
<MESSAGE_DATETIME>2017-06-13T12:20:51+10:00</MESSAGE_DATETIME>
<MESSAGE_SEQ>1</MESSAGE_SEQ>
<MESSAGE_TYPE>PLACED</MESSAGE_TYPE>
<ORDER_HEAD>
<ORDER_ID>1111</ORDER_ID>
<DROP_SHIP_ORDER_NO></DROP_SHIP_ORDER_NO>
<CUSTOMER_ORDER_NO>22222</CUSTOMER_ORDER_NO>
<DISPATCH_LOCATION>
<SKU>2323234</SKU>
<UPC>4549432533626</UPC>
<REQUESTED_QTY>1</REQUESTED_QTY>
<DISPATCH_ASSIGNMENT>7777</DISPATCH_ASSIGNMENT>
<PROVIDER_ID>100</PROVIDER_ID>
<PKG_TYPE>SAT</PKG_TYPE>
</DISPATCH_LOCATION>
</ORDER_HEAD>
</ORDER_NOTIFY>
</iORDERS:iORDERS>
query :
select wor.batch_no,wor.web_service_no,x.*
from web_orders wo
cross join XMLTABLE (
XMLNAMESPACES(DEFAULT 'urn:iORDERS-abcdonline-com:Integration:v1'),
'iORDERS/ORDER_NOTIFY/ORDER_HEAD/DISPATCH_LOCATION'
passing xmltype(wo.xml_message)
columns
MESSAGE_TYPE varchar(120) path './../../../MESSAGE_TYPE') x;
You need to provide the named namespace identifier rather than a defealt, and your column path is going up one too many levels:
select wo.batch_no,wo.web_service_no,x.*
from web_orders wo
cross join XMLTABLE (
XMLNAMESPACES('urn:iORDERS-abcdonline-com:Integration:v1' as "iORDERS"),
'iORDERS:iORDERS/ORDER_NOTIFY/ORDER_HEAD/DISPATCH_LOCATION'
passing xmltype(wo.xml_message)
columns
MESSAGE_TYPE varchar(120) path './../../MESSAGE_TYPE') x;
BATCH_NO WEB_SERVICE_NO MESSAGE_TYPE
---------- -------------- ------------------------------------------------------------------------------------------------------------------------
1 2 PLACED
Presumably you're planning on getting for information that that from the XML, and/or expect to have multiple nodes; otherwise, to just get the message type you could simplify to:
select wo.batch_no,wo.web_service_no,x.*
from web_orders wo
cross join XMLTABLE (
XMLNAMESPACES('urn:iORDERS-abcdonline-com:Integration:v1' as "iORDERS"),
'iORDERS:iORDERS/ORDER_NOTIFY'
passing xmltype(wo.xml_message)
columns
MESSAGE_TYPE varchar(120) path 'MESSAGE_TYPE') x;
or even, with a single node:
select wo.batch_no,wo.web_service_no,XMLQuery(
'declare namespace iORDERS="urn:iORDERS-abcdonline-com:Integration:v1"; (: :)
iORDERS:iORDERS/ORDER_NOTIFY/MESSAGE_TYPE/text()'
passing xmltype(wo.xml_message)
returning content).getStringVal() as message_type
from web_orders wo;