Oracle raises ORA-01722: invalid number during merge into with nested xmltable - oracle

I'm runnning the code that should parse xml and add it to my table. XML code is loaded from variable "s". Sample of my xml is:
<?xml version="1.0" encoding="utf-8"?><response>
<item>
<id>7294478</id>
<type>ad</type>
<stats list="true">
<period>
<day>2012-12-26</day>
<spent>132.00</spent>
<impressions>93315</impressions>
<clicks>4</clicks>
<reach>38039</reach>
</period>
<period>
<day>2012-12-27</day>
<impressions>7237</impressions>
<reach>4332</reach>
</period>
<period>
<day>2012-12-28</day>
<impressions>571</impressions>
<reach>452</reach>
</period>
</stats>
</item>
</response>
That's the part of code i'm trying to run:
MERGE INTO VK_AD_STATS r
USING (SELECT hmn.STATS_ID,
hmn.STATS_type,
items.STATS_day,
items.STATS_spent,
items.STATS_impressions,
items.STATS_clicks,
items.STATS_reach,
items.STATS_video_views,
items.STATS_video_views_half,
items.STATS_video_views_full,
items.STATS_video_clicks_site,
items.STATS_join_rate
FROM XMLTABLE('response/item' passing (SELECT xmltype(s) resp FROM dual)
COLUMNS STATS_ID NUMBER path '/item/id',
STATS_type VARCHAR2(2000) path '/item/type',
STATS_XML xmltype path '/item/stats/period'
)hmn ,
XMLTABLE('period' passing hmn.STATS_XML
COLUMNS STATS_day VARCHAR2(2000) path '/period/day',
STATS_spent NUMBER path '/period/spent',
STATS_impressions NUMBER path '/period/impressions',
STATS_clicks NUMBER path '/period/clicks',
STATS_reach NUMBER path '/period/reach',
STATS_video_views NUMBER path '/period/video_views',
STATS_video_views_half NUMBER path '/period/video_views_half',
STATS_video_views_full NUMBER path '/period/video_views_full',
STATS_video_clicks_site NUMBER path '/period/_video_clicks_site ',
STATS_join_rate NUMBER path '/period/join_rate'
) items) proc
ON (r.STATS_ID = proc.STATS_ID and r.STATS_day = proc.STATS_day)
WHEN MATCHED THEN UPDATE SET r.STATS_type = proc.STATS_type,
r.STATS_spent = proc.STATS_spent,
r.STATS_impressions = proc.STATS_impressions,
r.STATS_clicks = proc.STATS_clicks,
r.STATS_reach = proc.STATS_reach,
r.STATS_video_views = proc.STATS_video_views,
r.STATS_video_views_half = proc.STATS_video_views_half,
r.STATS_video_views_full = proc.STATS_video_views_full,
r.STATS_video_clicks_site = proc.STATS_video_clicks_site,
r.STATS_join_rate = proc.STATS_join_rate
WHEN NOT MATCHED THEN INSERT (r.STATS_ID,
r.STATS_type,
r.STATS_day,
r.STATS_spent,
r.STATS_impressions,
r.STATS_clicks,
r.STATS_reach,
r.STATS_video_views,
r.STATS_video_views_half,
r.STATS_video_views_full,
r.STATS_video_clicks_site,
r.STATS_join_rate
)
VALUES (proc.STATS_ID,
proc.STATS_type,
proc.STATS_day,
proc.STATS_spent,
proc.STATS_impressions,
proc.STATS_clicks,
proc.STATS_reach,
proc.STATS_video_views,
proc.STATS_video_views_half,
proc.STATS_video_views_full,
proc.STATS_video_clicks_site,
proc.STATS_join_rate
);
COMMIT;
But it raises ORA-01722: invalid number

Extract the spent column as text and then convert it specifying the NLS_NUMERIC_CHARCTERS to use as decimal separator:
SELECT hmn.STATS_ID,
hmn.STATS_type,
items.STATS_day,
TO_NUMBER(
items.STATS_spent,
'99999999999999999999D99',
'NLS_NUMERIC_CHARACTERS='',.'''
) AS stats_spent,
items.STATS_impressions,
items.STATS_clicks,
items.STATS_reach,
items.STATS_video_views,
items.STATS_video_views_half,
items.STATS_video_views_full,
items.STATS_video_clicks_site,
items.STATS_join_rate
FROM XMLTABLE(
'/response/item'
PASSING XMLTYPE(s)
COLUMNS STATS_ID NUMBER path '/item/id',
STATS_type VARCHAR2(2000) path '/item/type',
STATS_XML xmltype path '/item/stats/period'
) hmn,
XMLTABLE(
'/period'
PASSING hmn.STATS_XML
COLUMNS STATS_day VARCHAR2(2000) path '/period/day',
STATS_spent VARCHAR2(23) path '/period/spent',
STATS_impressions NUMBER path '/period/impressions',
STATS_clicks NUMBER path '/period/clicks',
STATS_reach NUMBER path '/period/reach',
STATS_video_views NUMBER path '/period/video_views',
STATS_video_views_half NUMBER path '/period/video_views_half',
STATS_video_views_full NUMBER path '/period/video_views_full',
STATS_video_clicks_site NUMBER path '/period/_video_clicks_site',
STATS_join_rate NUMBER path '/period/join_rate'
) items

Your problem isn't XML related at all - this:
<spent>132.00</spent>
doesn't parse as a number if your NLS settings use , as a decimal separator.
Change your NLS settings to use . as decimal separator, e.g. by running this PL/SQL block:
begin
DBMS_SESSION.SET_NLS ('NLS_LANGUAGE' ,'AMERICAN');
DBMS_SESSION.SET_NLS ('NLS_TERRITORY','AMERICA');
end;
and your code should work.

Related

Accessing values in XML from a variable in Oracle SQL

I have a XML data as below in a variable p_val:
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:wsa="http://www.w3.org/2005/08/addressing">
<env:Header>
<wsa:MessageID>urn:bda29066-5961-11ec-87ec-0242c5d8b376</wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>
<wsa:ReferenceParameters>
<instra:tracking.ecid xmlns:instra="http://xmlns.oracle.com/sca/tracking/1.0">9b657011-42e4-4cf4-a78e-537551de4cc0-0057ca4a</instra:tracking.ecid>
<instra:tracking.FlowEventId xmlns:instra="http://xmlns.oracle.com/sca/tracking/1.0">27818206</instra:tracking.FlowEventId>
<instra:tracking.FlowId xmlns:instra="http://xmlns.oracle.com/sca/tracking/1.0">169007626</instra:tracking.FlowId>
<instra:tracking.CorrelationFlowId xmlns:instra="http://xmlns.oracle.com/sca/tracking/1.0">0000NqYCMWFDGfdLxeG7yW1X_Nsm0003Yk</instra:tracking.CorrelationFlowId>
<instra:tracking.quiescing.SCAEntityId xmlns:instra="http://xmlns.oracle.com/sca/tracking/1.0">1830036</instra:tracking.quiescing.SCAEntityId>
</wsa:ReferenceParameters>
</wsa:ReplyTo>
<wsa:FaultTo>
<wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>
</wsa:FaultTo>
</env:Header>
<env:Body>
<LaunchSpreadSheetWorkFlowResponse xmlns="http://xmlns.oracle.com/bpmn/bpmnCloudProcess/ISV_ROYALTIES/ISVSpreadSheetMainProcess">
<instanceNumber xmlns:def="http://www.w3.org/2001/XMLSchema" xsi:type="def:long"
xmlns=""
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">79630772</instanceNumber>
</LaunchSpreadSheetWorkFlowResponse>
</env:Body>
</env:Envelope>
I want to access the value of the tag: instanceNumber which is 79630772.
If it is a direct payload instead of a variable I tried accessing it using:
select *
FROM XMLTABLE('/Envelope/Body/LaunchSpreadSheetWorkFlowResponse'
PASSING
xmltype('
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:wsa="http://www.w3.org/2005/08/addressing">
<env:Header>
<wsa:MessageID>urn:bda29066-5961-11ec-87ec-0242c5d8b376</wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>
<wsa:ReferenceParameters>
<instra:tracking.ecid xmlns:instra="http://xmlns.oracle.com/sca/tracking/1.0">9b657011-42e4-4cf4-a78e-537551de4cc0-0057ca4a</instra:tracking.ecid>
<instra:tracking.FlowEventId xmlns:instra="http://xmlns.oracle.com/sca/tracking/1.0">27818206</instra:tracking.FlowEventId>
<instra:tracking.FlowId xmlns:instra="http://xmlns.oracle.com/sca/tracking/1.0">169007626</instra:tracking.FlowId>
<instra:tracking.CorrelationFlowId xmlns:instra="http://xmlns.oracle.com/sca/tracking/1.0">0000NqYCMWFDGfdLxeG7yW1X_Nsm0003Yk</instra:tracking.CorrelationFlowId>
<instra:tracking.quiescing.SCAEntityId xmlns:instra="http://xmlns.oracle.com/sca/tracking/1.0">1830036</instra:tracking.quiescing.SCAEntityId>
</wsa:ReferenceParameters>
</wsa:ReplyTo>
<wsa:FaultTo>
<wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>
</wsa:FaultTo>
</env:Header>
<env:Body>
<LaunchSpreadSheetWorkFlowResponse xmlns="http://xmlns.oracle.com/bpmn/bpmnCloudProcess/ISV_ROYALTIES/ISVSpreadSheetMainProcess">
<instanceNumber xmlns:def="http://www.w3.org/2001/XMLSchema" xsi:type="def:long"
xmlns=""
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">79630772</instanceNumber>
<name>XYZ</name>
</LaunchSpreadSheetWorkFlowResponse>
</env:Body>
</env:Envelope>
')
COLUMNS
temp2 varchar2(20) PATH './instanceNumber'
) xmlt
;
The above query returned an empty value.
How should I access the value from a variable?
You need to specify and supply the namespaces:
select *
FROM XMLTABLE(
XMLNAMESPACES(
default 'http://xmlns.oracle.com/bpmn/bpmnCloudProcess/ISV_ROYALTIES/ISVSpreadSheetMainProcess',
'http://schemas.xmlsoap.org/soap/envelope/' AS "env"
),
'/env:Envelope/env:Body/LaunchSpreadSheetWorkFlowResponse'
PASSING
...
COLUMNS
temp2 varchar2(20) PATH '*:instanceNumber'
) xmlt
;
The Envelope and Body come under the SOAP env namespace. In the XML document the LaunchSpreadSheetWorkFlowResponse node doesn't have a namespace prefix but defines a default namespace, so that default needs to be supplied too. Then it gets slightly awkward for instanceNumber as that node redefines (or clears) the default, and defines additional namespaces, but doesn't use them - which puts that node in limbo a bit. I've taken the easy route and wildcarded that with the *: prefix.
The less lazy way, I think, is to treat the LaunchSpreadSheetWorkFlowResponse as a separate XML fragment with a seconf XMLTable call:
select xmlt2.instanceNumber
FROM XMLTABLE(
XMLNAMESPACES(
default 'http://xmlns.oracle.com/bpmn/bpmnCloudProcess/ISV_ROYALTIES/ISVSpreadSheetMainProcess',
'http://schemas.xmlsoap.org/soap/envelope/' AS "env"
),
'/env:Envelope/env:Body/LaunchSpreadSheetWorkFlowResponse'
PASSING
...
COLUMNS
LaunchSpreadSheetWorkFlowResponse xmlType PATH '.'
) xmlt1
CROSS JOIN XMLTABLE(
'.'
PASSING
xmlt1.LaunchSpreadSheetWorkFlowResponse
COLUMNS
instanceNumber varchar2(20) PATH 'instanceNumber'
) xmlt2
;
I'm not sure it's worth it here, and I'd be tempted to stick to the wildcard version.
db<>fiddle showing both.

ODP.NET, Managed reading LONG column results in ORA-01009

I'm trying to get the source of a view in my .net app.
To do this, I query DBA_VIEWS: it has a column TEXT with exactly what I need. The type is LONG.
If I do it using the Unmanaged driver, everything works as expected.
The same code with Managed driver results in ORA-01009: missing mandatory parameter.
Adding a transaction around the command and using breakpoint and "slow" steps sometimes results in the same code working.
ODP.NET version is 19, Oracle DB is 18c Express Edition. Strangely enough, the same code works just fine with Oracle Database 12c regardless of driver type.
Is there maybe some setting I need to change on the database or in code? I'm completely lost here.
Code I'm using for testing:
Imports System.Data.Common
Imports Oracle.ManagedDataAccess
'Imports Oracle.DataAccess
Module Views
Function CreateCommand(Connection As DbConnection) As System.Data.Common.DbCommand
Dim cmd As Data.Common.DbCommand = Connection.CreateCommand()
With CType(cmd, Client.OracleCommand)
.BindByName = True
.FetchSize = &H100000 '1 Mb
.InitialLONGFetchSize = -1 'the entire LONG or LONG RAW data is prefetched and stored in the fetch array.
.InitialLOBFetchSize = -1 'the entire LOB data is prefetched and stored in the fetch array.
End With
Return cmd
End Function
Sub query()
Try
Using DBConnection = New Client.OracleConnection("User ID=TESTUSER;Password=TESTPWD;Data Source=TESTDB;Pooling=True")
DBConnection.Open()
Using DBConnection.BeginTransaction()
Using cmdSQL = CType(CreateCommand(DBConnection), Client.OracleCommand)
cmdSQL.CommandText = "select TEXT from DBA_VIEWS where VIEW_NAME = :0"
Dim p = cmdSQL.CreateParameter()
p.ParameterName = "0"
p.Value = "TEST_VIEW"
cmdSQL.Parameters.Add(p)
Dim sw = Stopwatch.StartNew
Using rdr = cmdSQL.ExecuteReader
rdr.FetchSize = 2 ^ 20
While rdr.Read
Dim row(rdr.FieldCount - 1) As Object
rdr.GetProviderSpecificValues(row)
Dim x = row(0)
Console.WriteLine($"{x.ToString.Length} bytes")
End While
End Using
Console.WriteLine($"{sw.ElapsedMilliseconds} ms")
End Using
End Using
End Using
Catch ex As Exception
Console.WriteLine(ex.ToString)
End Try
End Sub
End Module
You can use an anonymous block with output parameter and a call to ExecuteNonQuery. Your command text will be
"begin select TEXT into :0 from DBA_VIEWS where VIEW_NAME = :1; end;"
Add 2 parameters. Make sure that
' Parameter #1 has
p.Direction = ParameterDirection.Output
p.OracleDbType = OracleDbType.Long
p.Size = 1000000
And use command cmd.ExecuteNonQuery(). Then, when parameter is retrieved, just use its value
Dim txt As String = cmd.Parametersp[0].Value.ToString()
It's a pity, Oracle deprecated LONG data type for ages but LONG data is still used many times for internal data.
You could write a function and then get the data by calling the function:
create or replace function GetViewText(v in varchar2) return clob is
ret CLOB;
BEGIN
FOR aRow IN (SELECT TEXT FROM DBA_VIEWS WHERE VIEW_NAME = v) LOOP
ret := aRow.TEXT;
-- or ret := TO_CLOB(aRow.TEXT);
END LOOP;
RETURN ret;
END;
Yet another way from this answer is to (ab)use dbms_xmlgen.getxml.
We can either use it to query a single view's code (as in my original question)
with input as (
select
:0 as VIEW_NAME
from dual
)
SELECT
substr(
text_xml,
instr(text_xml, '<LONGCOL>') + length('<LONGCOL>'),
instr(text_xml, '</LONGCOL>', -1) - (instr(text_xml, '<LONGCOL>') + length('<LONGCOL>'))
) as TEXT
from
(
-- getxml can return malformed xml (which is good in this case)
-- while getxmltype can not.
select dbms_xmlgen.getxml(q'{
SELECT TEXT as LONGCOL
FROM SYS.DBA_VIEWS
WHERE VIEW_NAME = '}' || input.VIEW_NAME || q'{'
}') as text_xml
from input
)
or create our own DBA_VIEWS version.
create or replace view APP_SCHEMA.DBA_VIEWS
as
select
OWNER, VIEW_NAME, TEXT_LENGTH,
case
when (TEXT_VC is not null and TEXT_LENGTH <= 4000)
then to_clob(TEXT_VC)
when TEXT is NULL
then NULL
else (
SELECT
substr(
text_xml,
instr(text_xml, '<LONGCOL>') + length('<LONGCOL>'),
--instr(text_xml, '</LONGCOL>', -1) - (instr(text_xml, '<LONGCOL>') + length('<LONGCOL>'))
TEXT_LENGTH
) as TEXT
from
(
-- getxml can return malformed xml (which is good in this case)
-- while getxmltype can not.
select dbms_xmlgen.getxml(q'{
SELECT TEXT as LONGCOL
FROM SYS.DBA_VIEWS
WHERE OWNER = '}' || OWNER || q'{'
and VIEW_NAME = '}' || VIEW_NAME || q'{'
}') as text_xml
from dual
)
)
end as TEXT,
TEXT_VC, TYPE_TEXT_LENGTH, TYPE_TEXT, OID_TEXT_LENGTH, OID_TEXT, VIEW_TYPE_OWNER, VIEW_TYPE, SUPERVIEW_NAME, EDITIONING_VIEW, READ_ONLY, CONTAINER_DATA, BEQUEATH, ORIGIN_CON_ID, DEFAULT_COLLATION, CONTAINERS_DEFAULT, CONTAINER_MAP, EXTENDED_DATA_LINK, EXTENDED_DATA_LINK_MAP, HAS_SENSITIVE_COLUMN
from sys.dba_views
;

Oracle XML Parsing - ORA-19202 and LPX-00601

I am new to XML parsing in oracle, I am getting ORA-19202: Error occurred in XML processing and LPX-00601: Invalid token for below query. Can anyone please help ?
TABLENAME = PARSEXML
COLUMN1('ID') = VARCHAR |COLUMN2('XML') = CLOB
<?xml version="1.0" encoding="UTF-8"?>
<ns4:TSTCLOB xmlns:ns4="http://example.org/test" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<ns4:Rqst>
<ns2:Initiation>
<ns2:InitiationDetail>
<ns3:Channel>WEB</ns3:Channel>
<ns3:Unit>ABCD</ns3:Unit>
</ns2:InitiationDetail>
</ns2:Initiation>
</ns4:Rqst>
</ns4:TSTCLOB>
SELECT ID, EXTRACT(xmltype(XML), '\ns4:TSTCLOB\ns4:Rqst\ns2:Initiation\ns2:InitiationDetail\ns3:Unit') "unit"
FROM PARSEXML WHERE ID = '123'
1) Your example xml is not valid. It is using 4 namespace but only 2 are declared. You have to add this xmlns:ns2="Namespace2" xmlns:ns3="Namespace3"
<ns4:TSTCLOB xmlns:ns4="http://example.org/test" xmlns:ns2="Namespace2" xmlns:ns3="Namespace3" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<ns4:Rqst>
<ns2:Initiation>
<ns2:InitiationDetail>
<ns3:Channel>WEB</ns3:Channel>
<ns3:Unit>ABCD</ns3:Unit>
</ns2:InitiationDetail>
</ns2:Initiation>
</ns4:Rqst>
</ns4:TSTCLOB>
2) In xpath you have to use slash instead of backslash \ -> / = '/ns4:TSTCLOB/ns4:Rqst/ns2:Initiation/ns2:InitiationDetail/ns3:Unit'
3) Extract method has 3 parameters, the last one is namespace string. If your xml is using other namespace except default one you have to specify this in extract method.
Extract(xml, '/ns4:TSTCLOB/ns4:Rqst/ns2:Initiation/ns2:InitiationDetail/ns3:Unit','xmlns:ns4="http://example.org/test" xmlns:ns2="Namespace2" xmlns:ns3="Namespace3" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"')
4*) Extract,ExtractValue are deprecated , you should use xmltable or xmlquery
Try this approach (simplified the XML for testing purpose):
with tmp as(
select
xmltype(to_clob('<?xml version="1.0" encoding="UTF-8"?>
<TSTCLOB>
<Rqst>
<Initiation>
<InitiationDetail>
<Channel>WEB</Channel>
<Unit>ABCD</Unit>
</InitiationDetail>
</Initiation>
</Rqst>
</TSTCLOB>')) tmp_xml
from dual)
select
xml_tbl.Channel,
xml_tbl.Unit
from
tmp,
xmltable(
'/TSTCLOB/Rqst/Initiation/InitiationDetail' passing tmp.tmp_xml
columns
Channel varchar2(6) path 'Channel',
Unit varchar2(6) path 'Unit') xml_tbl

Oracle JPG metadata xpath from XMLTYPE column

I have a tavle storing photos. With a pl/sql script i extract the image metadata to the exifmetadata xmltype column.
I would like to select the elements one by one, but i always get null back and i don't know what is the issue with my xpath expression.
<exifMetadata xmlns="http://xmlns.oracle.com/ord/meta/exif" xsi:schemaLocation="http://xmlns.oracle.com/ord/meta/exif http://xmlns.oracle.com/ord/meta/exif" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<TiffIfd>
<Make tag="271">Apple</Make>
<Model tag="272">iPhone 4S</Model>
<Orientation tag="274">top left</Orientation>
<XResolution tag="282">72.0</XResolution>
<YResolution tag="283">72.0</YResolution>
<ResolutionUnit tag="296">inches</ResolutionUnit>
<Software tag="305">6.0.1</Software>
<DateTime tag="306">2012-11-16T13:31:15</DateTime>
<YCbCrPositioning tag="531">centered</YCbCrPositioning>
</TiffIfd>
<ExifIfd tag="34665">
<ExposureTime tag="33434">0.004405286</ExposureTime>
<FNumber tag="33437">2.4</FNumber>
<ExposureProgram tag="34850">Normal program</ExposureProgram>
<ExifVersion tag="36864">0221</ExifVersion>
<DateTimeOriginal tag="36867">2012-11-16T12:49:58</DateTimeOriginal>
<DateTimeDigitized tag="36868">2012-11-16T12:49:58</DateTimeDigitized>
<ComponentsConfiguration tag="37121">Y</ComponentsConfiguration>
<ShutterSpeedValue tag="37377">7.82697</ShutterSpeedValue>
<ApertureValue tag="37378">2.5260692</ApertureValue>
<BrightnessValue tag="37379">6.641153</BrightnessValue>
<MeteringMode tag="37383">Pattern</MeteringMode>
<Flash tag="37385">
<Fired>No</Fired>
</Flash>
<FocalLength tag="37386">4.28</FocalLength>
<FlashpixVersion tag="40960">0100</FlashpixVersion>
<ColorSpace tag="40961">sRGB</ColorSpace>
<PixelXDimension tag="40962">2902</PixelXDimension>
<PixelYDimension tag="40963">1938</PixelYDimension>
<SensingMethod tag="41495">One-chip color area</SensingMethod>
<ExposureMode tag="41986">Auto exposure</ExposureMode>
<WhiteBalance tag="41987">Auto</WhiteBalance>
<FocalLengthIn35mmFilm tag="41989">35</FocalLengthIn35mmFilm>
<SceneCaptureType tag="41990">Standard</SceneCaptureType>
</ExifIfd>
</exifMetadata>
And the SQL:
SELECT
p.metaexif.extract('//exifMetadata/TiffIfd/Make/text()').getStringVal() "Model"
FROM scott.photos p
WHERE id=22
;
The id 22 record exist that is sure.
you have a default namespace set so you have to cope with this.
eg use this:
select extractvalue(p.metaexif,'/exifMetadata/TiffIfd/Make',
'xmlns="http://xmlns.oracle.com/ord/meta/exif"')

How to return multiple results with XMLTABLE?

I want to do a query in Oracle using xmltable.
Everything works fine, but there are multiple (n) results for xml node "article_title". For each row the result "<string>Article Name1</string><string>Article Name 2</string>... is returned. But I want every article name to be returned as a single row.
How can I realize this?
SELECT
X.*
FROM
myTable C,
xmltable (
'$cust//member' PASSING C.STAT_XML as "cust"
COLUMNS
name VARCHAR(25) PATH '/member/name',
article_title XMLTYPE PATH '//string/text()'
) as X
WHERE X.name = 'articles';
I'm having a problem with this as well. I have an XML that's supposed to send shipment data from our warehouse management system back to our order management system, and it has various different things that have multiples. The entire XML message has a single ShipConfirmHeader section, so that's easy enough to pull out. Where I run into trouble is that it has a ShipConfirmDetail/Orders section, and there could be any number of orders listed. Within each order, there could be any number of order lines. I can pull the ShipConfirmHeader and the ShipConfirmDetail/Orders together OR I can pull the ShipConfirmHeader and the ShipConfirmDetail/Orders/OrderLineItem together, but when I try pulling the Orders together with the OrderLineItem, there's no way that I can see to join those, so I end up with a cartesian product. To complicate matters even more, each Order could have many Cartons associated with it, and each Carton could contain multiple OrderLineItems, and each Carton could have multiple CartonDetails.
I've included a sample of my XML below. In this example, there's only one Order, one OrderLine, and one Carton (called an LPN in the XML), because I've stripped out all the others (the original XML is over 4000 lines long).
Pulling stuff from the ShipConfirmHeader is relatively easy, like this:
xmltable('/tXML/Message/ShipConfirm/ShipConfirmSummary/ShipConfirmHeaderInfo/'
passing xmltype(msg_xml.full_xml)
columns
invoice_batch varchar2(20) path 'InvcBatchNbr'
) sc_hdr
But when I want to include any of the multiples, it gives me problems. I've tried a variety of things:
-- This gives the error "ORA-22950: cannot ORDER objects without MAP or ORDER method"
xmltable('/tXML/Message/ShipConfirm'
passing xmltype(msg_xml.full_xml)
columns
invoice_batch varchar2(20) path 'ShipConfirmSummary/ShipConfirmHeaderInfo/InvcBatchNbr',
order_dtl xmltype path 'ShipConfirmDetails/Orders'
) sc_hdr
-- This doesn't like the "../" in the XPATH
xmltable('/tXML/Message/ShipConfirm/ShipConfirmDetails/Orders/OrderLineItem'
passing xmltype(msg_xml.full_xml)
columns
invoice_batch varchar2(20) path '../../../ShipConfirmSummary/ShipConfirmHeaderInfo/InvcBatchNbr',
order_id varchar2(20) path '../TcOrderId',
order_line_id varchar2(20) path 'TcOrderLineId',
item_name varchar2(20) path 'ItemName'
) sc_hdr
-- This gives a cartesian product.
xmltable('/tXML/Message/ShipConfirm/ShipConfirmSummary/ShipConfirmHeaderInfo'
passing xmltype(msg_xml.full_xml)
columns
invoice_batch varchar2(20) path 'InvcBatchNbr'
) sc_hdr,
xmltable('/tXML/Message/ShipConfirm/ShipConfirmDetails/Orders'
passing xmltype(msg_xml.full_xml)
columns
order_id varchar2(20) path 'TcOrderId'
) sc_ord_hdr,
xmltable('/tXML/Message/ShipConfirm/ShipConfirmDetails/Orders/OrderLineItem'
passing xmltype(msg_xml.full_xml)
columns
order_line_id varchar2(20) path 'TcOrderLineId',
item_name varchar2(20) path 'ItemName'
) sc_ord_dtl
Here's the sample XML:
<?xml version="1.0" encoding="UTF-8"?>
<tXML>
<Header>
<Source>warehouse management system</Source>
<Action_Type></Action_Type>
<Sequence_Number></Sequence_Number>
<Batch_ID></Batch_ID>
<Reference_ID></Reference_ID>
<User_ID>CRONUSER</User_ID>
<Password></Password>
<Message_Type>ShipConfirm</Message_Type>
<Company_ID>1</Company_ID>
<Msg_Locale>English (United States)</Msg_Locale>
<Msg_Time_Zone>America/Denver</Msg_Time_Zone>
<Version>2018</Version>
</Header>
<Message>
<ShipConfirm>
<ShipConfirmSummary>
<CompanyName>Blah</CompanyName>
<FacilityName>Blah</FacilityName>
<ShipConfirmHeaderInfo>
<InvcBatchNbr>123456</InvcBatchNbr>
<LastInvcDttm>5/27/21 05:45</LastInvcDttm>
<ShippedDttm>5/27/21 05:45</ShippedDttm>
<DateCreated>5/27/21 05:45</DateCreated>
<StoreNbr></StoreNbr>
<ShipVia>ST</ShipVia>
<SchedDeliveryDate></SchedDeliveryDate>
<ProNbr></ProNbr>
<AppointmentNbr></AppointmentNbr>
<ManifestNbr></ManifestNbr>
<SealNbr></SealNbr>
<AppointmentDate></AppointmentDate>
<PartialShipConfirmStatus>5</PartialShipConfirmStatus>
<PreBillStatus>0</PreBillStatus>
<ApptMadeByID></ApptMadeByID>
<BillOfLading></BillOfLading>
<CancelQuantity>0.0</CancelQuantity>
<NbrOfLpns>26</NbrOfLpns>
<NbrOfPlts>0</NbrOfPlts>
<NbrOfOrders>26</NbrOfOrders>
<TotalWt>61.72</TotalWt>
<UserID>USER</UserID>
</ShipConfirmHeaderInfo>
</ShipConfirmSummary>
<ShipConfirmDetails>
<Orders>
<BatchCtrlNbr>123456</BatchCtrlNbr>
<DistributionShipVia>ST</DistributionShipVia>
<DoType>Customer Order</DoType>
<DsgShipVia>ST</DsgShipVia>
<OriginalShipVia>ST</OriginalShipVia>
<IncotermLocAvaTimeZoneId>America/New_York</IncotermLocAvaTimeZoneId>
<InvcBatchNbr>123456</InvcBatchNbr>
<IsBackOrdered>1</IsBackOrdered>
<MajorOrderCtrlNbr></MajorOrderCtrlNbr>
<OrderType>ECOMM_ORDER</OrderType>
<ShipDate>5/27/21 05:45</ShipDate>
<OrderStatus>Unplanned</OrderStatus>
<DoStatus>Shipped</DoStatus>
<TcCompanyId>1</TcCompanyId>
<TcOrderId>MYORDERID</TcOrderId>
<TotalNbrOfLpn>1</TotalNbrOfLpn>
<TotalNbrOfPlt>0</TotalNbrOfPlt>
<TotalNbrOfUnits>1</TotalNbrOfUnits>
<LineHaulShipVia>ST</LineHaulShipVia>
<PartialShipConfirmStatus>5</PartialShipConfirmStatus>
<PreBillStatus>0</PreBillStatus>
<OrderBillToInfo>
<BillToAddress1>Snip</BillToAddress1>
<BillToAddress2></BillToAddress2>
<BillToAddress3></BillToAddress3>
<BillToCity>Snip</BillToCity>
<BillToContact></BillToContact>
<BillToContactName></BillToContactName>
<BillToCountryCode>CA</BillToCountryCode>
<BillToCounty></BillToCounty>
<BillToFacilityName></BillToFacilityName>
<BillToName>Snip</BillToName>
<BillToPhoneNumber>Snip</BillToPhoneNumber>
<BillToPostalCode>Snip</BillToPostalCode>
<BillToStateProv>ON</BillToStateProv>
</OrderBillToInfo>
<OrderDestInfo>
<DestAddress1>Snip</DestAddress1>
<DestAddress2></DestAddress2>
<DestAddress3></DestAddress3>
<DestCity>Snip</DestCity>
<DestContact>Snip</DestContact>
<DestCountryCode>CA</DestCountryCode>
<DestCounty></DestCounty>
<DestDockDoorId>0</DestDockDoorId>
<DestFacilityAliasId></DestFacilityAliasId>
<DestFacilityId>0</DestFacilityId>
<DestFacilityName></DestFacilityName>
<DestName>Snip</DestName>
<DestPhoneNumber>Snip</DestPhoneNumber>
<DestPostalCode>Snip</DestPostalCode>
<DestStateProv>ON</DestStateProv>
</OrderDestInfo>
<OrderOriginInfo>
<OriginAddress1>Snip</OriginAddress1>
<OriginAddress2></OriginAddress2>
<OriginAddress3></OriginAddress3>
<OriginCity>Snip</OriginCity>
<OriginContact></OriginContact>
<OriginCountryCode>CA</OriginCountryCode>
<OriginFacilityAliasId>Snip</OriginFacilityAliasId>
<OriginFacilityId>1</OriginFacilityId>
<OriginFacilityName>Snip</OriginFacilityName>
<OriginPhoneNumber></OriginPhoneNumber>
<OriginPostalCode>Snip</OriginPostalCode>
<OriginStateProv>AB</OriginStateProv>
</OrderOriginInfo>
<OrderInfoFields>
<SplInstrCode1>MW</SplInstrCode1>
<SplInstrCode2>MW</SplInstrCode2>
</OrderInfoFields>
<OrderLineItem>
<InvcBatchNbr>123456</InvcBatchNbr>
<ItemId>159331</ItemId>
<ItemName>MYITEMNAME</ItemName>
<LineItemId>12053970</LineItemId>
<OrderQty>1</OrderQty>
<OrderQtyUom>Unit</OrderQtyUom>
<OrigItemId>159331</OrigItemId>
<OrigItemName>MYITEMNAME</OrigItemName>
<OrigOrderLineItemId>1</OrigOrderLineItemId>
<OrigOrderQty>1</OrigOrderQty>
<OrigOrderQtyUom>Unit</OrigOrderQtyUom>
<OutptOrderLineItemId>3782033</OutptOrderLineItemId>
<Price>15.39</Price>
<PriceTktType></PriceTktType>
<RetailPrice>0.0</RetailPrice>
<ShippedQty>1</ShippedQty>
<TcCompanyId>1</TcCompanyId>
<TcOrderLineId>1</TcOrderLineId>
<UnitVol>0.0744</UnitVol>
<UnitWt>0.58</UnitWt>
<Uom>Unit</Uom>
<UserCanceledQty>0</UserCanceledQty>
<OrderLineItemDefn>
<ItemStyle>Snip</ItemStyle>
<ItemStyleSfx>Snip</ItemStyleSfx>
</OrderLineItemDefn>
</OrderLineItem>
<Lpn>
<BillOfLadingNumber></BillOfLadingNumber>
<CFacilityAliasId>Snip</CFacilityAliasId>
<EstimatedWeight>0.58</EstimatedWeight>
<FinalDestFacilityAliasId></FinalDestFacilityAliasId>
<InvcBatchNbr>123456</InvcBatchNbr>
<LoadedDttm></LoadedDttm>
<ManifestNbr></ManifestNbr>
<MasterBolNbr></MasterBolNbr>
<NonInventoryLpnFlag>0</NonInventoryLpnFlag>
<NonMachineable></NonMachineable>
<OutptLpnId>730888</OutptLpnId>
<PackerUserid>USER</PackerUserid>
<ProcDttm>5/27/21 05:45</ProcDttm>
<ProcStatCode>0</ProcStatCode>
<QtyUom>Unit</QtyUom>
<ServiceLevel></ServiceLevel>
<ShipVia>ST</ShipVia>
<ShippedDttm>5/27/21 05:45</ShippedDttm>
<StaticRouteId></StaticRouteId>
<TcCompanyId>1</TcCompanyId>
<TcLpnId>98765</TcLpnId>
<TcOrderId>Snip</TcOrderId>
<TcParentLpnId></TcParentLpnId>
<TcShipmentId></TcShipmentId>
<TotalLpnQty>1</TotalLpnQty>
<TrackingNbr>Snip</TrackingNbr>
<VolumeUom>cu ft</VolumeUom>
<Weight>0.58</Weight>
<WeightUom>Lbs</WeightUom>
<LoadSequence>0</LoadSequence>
<oLPNXRefNbr></oLPNXRefNbr>
<LpnDetail>
<InvcBatchNbr>123456</InvcBatchNbr>
<ItemId>159331</ItemId>
<ItemName>MYITEMNAME</ItemName>
<LpnDetailId>20153787</LpnDetailId>
<OutptLpnDetailId>3689518</OutptLpnDetailId>
<QtyUom>Unit</QtyUom>
<SizeValue>1</SizeValue>
<TcCompanyId>1</TcCompanyId>
<TcLpnId>98765</TcLpnId>
<DistroNumber></DistroNumber>
<TcOrderLineId>1</TcOrderLineId>
<MinorOrderNbr>Snip</MinorOrderNbr>
<MinorPoNbr></MinorPoNbr>
</LpnDetail>
</Lpn>
</Orders>
</ShipConfirmDetails>
</ShipConfirm>
</Message>
</tXML>
Try something like this:
SELECT X.*
FROM my_table C,
xmltable('for $i in $cust//string , $j in $cust//member[./string=$i]/name return <member>{$j}{$i}</member>'
passing c.stat_xml AS "cust"
columns name varchar2(25) path '/member/name',
article_title xmltype path '//string') AS X
WHERE X.name = 'articles';
Here is a fiddle
I assumed that for every member you have one name but might have many strings

Resources