How to pull XML key "value" from SQL CLOB - sql

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.

Related

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);

Extracting values from XML column in Oracle

I have some data in an Oracle table which is stored in an XML-format string (column response in table WS_LOG).
I would like to extract data from each different node below <MedicalProcedureOutput>, but I'm having some difficulties in getting to each of the nodes. Can you spot what I'm doing wrong?
Here's what I'm trying (here trying to retrieve the value for the icpcID tag):
SELECT a.id,
t1.icpcID FROM
GH.WS_LOG a,
xmltable(
xmlnamespaces(
'http://schemas.xmlsoap.org/soap/envelope/' as "soap",
'http://www.w3.org/2001/XMLSchema' as "xsd",
'http://www.w3.org/2001/XMLSchema-instance' as "xsi"),
'/soap:Envelope/soap:Body/createBillingSubmissionForAFEBSGResponse/createBillingSubmissionForAFEBSGResult/proceduresList/MedicalProcedureOutput' passing xmltype(a.response) columns
icpcID varchar2(50) path 'ipcdId') t1
And here's some example data
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soap:Body>
<createBillingSubmissionForAFEBSGResponse xmlns="urn:bcpcorp.net/ws/bsgFacets/FacetsBillingService">
<createBillingSubmissionForAFEBSGResult>
<status>0</status>
<outputMessage />
<statusElement />
<claimID>18E002021300</claimID>
<claimStatus>01</claimStatus>
<claimStatusReason>AGFP</claimStatusReason>
<totalChargeValue>35.0000</totalChargeValue>
<totalPayableValue>0.0000</totalPayableValue>
<paitentPaidvalue>17.5</paitentPaidvalue>
<totalDebitAmount>0</totalDebitAmount>
<proceduresList>
<MedicalProcedureOutput>
<ipcdId>011801</ipcdId>
<otherdisallowedAmountResponsibility>N</otherdisallowedAmountResponsibility>
</MedicalProcedureOutput>
</proceduresList>
</createBillingSubmissionForAFEBSGResult>
</createBillingSubmissionForAFEBSGResponse>
</soap:Body>
</soap:Envelope>
I would expect to be getting the value '011801'. Please note that multiple <MedicalProcedureOutput> nodes may occur, and they would be organized as follows:
<MedicalProcedureOutput>
<ipcdId>725013</ipcdId>
<otherdisallowedAmountResponsibility>N</otherdisallowedAmountResponsibility>
</MedicalProcedureOutput>
<MedicalProcedureOutput>
<ipcdId>725105</ipcdId>
<otherdisallowedAmountResponsibility>N</otherdisallowedAmountResponsibility>
</MedicalProcedureOutput>
You need to declare a default namespace for everything from within the body, since createBillingSubmissionForAFEBSGResponse has its own xmlns unnamed (therefore default) declaration which applies from that node onwards; so:
SELECT a.id,
t1.icpcID FROM
GH.WS_LOG a,
xmltable(
xmlnamespaces(
default 'urn:bcpcorp.net/ws/bsgFacets/FacetsBillingService',
'http://schemas.xmlsoap.org/soap/envelope/' as "soap",
'http://www.w3.org/2001/XMLSchema' as "xsd",
'http://www.w3.org/2001/XMLSchema-instance' as "xsi"
),
'/soap:Envelope/soap:Body/createBillingSubmissionForAFEBSGResponse/createBillingSubmissionForAFEBSGResult/proceduresList/MedicalProcedureOutput' passing xmltype(a.response) columns
icpcID varchar2(50) path 'ipcdId') t1
/
ID ICPCID
---------- --------------------------------------------------
1 011801
or with multiple nodes as you showed later int he question this will return:
ID ICPCID
---------- --------------------------------------------------
2 725013
2 725105
db<>fiddle
You should take a look into to solve it with the EXTRACTVALUE function, here You can find some information about it.

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

extracting all tags(duplicates also) with specified name from xmltype column in sql

i want to extract a tag from an xml and insert into another table.
this XML is having different name spaces hence i use local-name() to fetch the tag which i want.
but some times there are multiple tags with same name. hence its failing with EXACTFETCH RETURNS MULTIPLE NODES. when multiple tags are existed i want to consider both instead of ignoring the second occurence.
source_table(id, payload):
id : 10
payload :
<root>
<name>john</name>
<status>
<statuscode>1</statuscode>
<statusmessage>one</statusmessage>
<statuscode>2</statuscode>
<statusmessage>two</statusmessage>
</status>
</root>
i want to extract stauscode and message and insert into another table
destination_table(id,name,statuscode,message)
output
10,john,1,one
10,john,2,two
below is the query i used
select id,
extract(payload, '//*[local-name()="name"]'),
extract(payload, '//*[local-name()="statuscode"]'),
extract(payload, '//*[local-name()="statusmessage"]')
from source_table;
i can get first occurence or second occurence by specifying [1] or [2] but i need both the stauscodes to be displayed like below
10,john,1,one
10,john,2,two
any help here
Hope this is what you need: Just past this into an empty query window and execute. Adapt it for your needs:
This solution assumes, that the status codes are sorted (as in your example 1,2,...). If this could occur in random order, just ask again...
Short explanation: The CTE "NameAndCode" brings up the name and the statuscodes. The ROW_NUMBER-function give us the node's index. This index I use to fetch the right message.
One more hint: If you can change the XML's format, it would be better to make the message an attribut of statuscode or to have it as subnode...
DECLARE #xmlColumn XML='<root>
<name>john</name>
<status>
<statuscode>1</statuscode>
<statusmessage>one</statusmessage>
<statuscode>2</statuscode>
<statusmessage>two</statusmessage>
</status>
</root>';
WITH NameAndCode AS
(
SELECT #xmlColumn.value('(/root/name)[1]','varchar(max)') AS Name
,x.y.value('.','int') AS Statuscode
,x.y.query('..') XMLNode
,ROW_NUMBER() OVER(ORDER BY x.y.value('.','int')) AS StatusIndex
FROM #xmlColumn.nodes('/root/status/statuscode') AS x(y)
)
SELECT *
,XMLNode.value('(/status[1]/statusmessage[sql:column("StatusIndex")])[1]','varchar(max)')
FROM NameAndCode

How to extract element-path from XMLType Node?

I would like to have a select statement on an XML document and one column should return me the path of each node.
For example, given the data
SELECT *
FROM TABLE(XMLSequence(
XMLTYPE('<?xml version="1.0"?>
<users><user><name>user1</name></user>
<user><name>user2</name></user>
<group>
<user><name>user3</name></user>
</group>
<user><name>user4</name></user>
</users>').extract('/*//*[text()]'))) t;
Which results in
column_value
--------
<user><name>user1</name></user>
<user><name>user2</name></user>
<user><name>user3</name></user>
<user><name>user4</name></user>
I'd like to have a result like this:
path value
------------------------ --------------
/users/user/name user1
/users/user/name user2
/users/group/user/name user3
/users/user/name user4
I can not see how to get to this. I figure there are two thing that have to work together properly:
Can I extract the path from an XMLType with a single operation or method, or do I have to do this with string-magic?
What is the correct XPath expression so that I do get the whole element path (if thats possible), eg. <users><group><user><name>user3</name></user></group></user> insead of <user><name>user3</name></user>?
Maybe I am not understanding XMLType fully, yet. It could be I need a different approach, but I can not see it.
Sidenotes:
In the final version the XML document will be coming from CLOBs of a table, not a static document.
The path column can of course also use dots or whatever and the initial slash is not the issue, any representation would do.
Also I would not mind if every inner node also gets a result row (possibly with null as value), not only the ones with text() in it (which is what I am really interested in).
In the end I will need the tail element of path separate (always "name" in the example here, but this will vary later), i.e. ('/users/groups/user', 'name', 'user3'), I can deal with that separately.
You can achieve that with help of XMLTable function from Oracle XML DB XQuery function set:
select * from
XMLTable(
'
declare function local:path-to-node( $nodes as node()* ) as xs:string* {
$nodes/string-join(ancestor-or-self::*/name(.), ''/'')
};
for $i in $rdoc//name
return <ret><name_path>{local:path-to-node($i)}</name_path>{$i}</ret>
'
passing
XMLParse(content '
<users><user><name>user1</name></user>
<user><name>user2</name></user>
<group>
<user><name>user3</name></user>
</group>
<user><name>user4</name></user>
</users>'
)
as "rdoc"
columns
name_path varchar2(4000) path '//ret/name_path',
name_value varchar2(4000) path '//ret/name'
)
For me XQuery looks at least more intuitive for XML data manipulation than XSLT.
You can find useful set of XQuery functions here.
Update 1
I suppose that you need totally plain dataset with full data at last stage.
This target can be reached by complicated way, constructed step-by-step below, but this variant is very resource-angry. I propose to review final target (selecting some specific records, count number of elements etc.) and after that simplify this solution or totally change it.
Update 2
All steps deleted from this Update except last because #A.B.Cade proposed more elegant solution in comments.
This solution provided in Update 3 section below.
Step 1 - Constructing dataset of id's with corresponding query results
Step 2 - Aggregating to single XML row
Step 3 - Finally get full plain dataset by querying constracted XML with XMLTable
with xmlsource as (
-- only for purpose to write long string only once
select '
<users><user><name>user1</name></user>
<user><name>user2</name></user>
<group>
<user><name>user3</name></user>
</group>
<user><name>user4</name></user>
</users>' xml_string
from dual
),
xml_table as (
-- model of xmltable
select 10 id, xml_string xml_data from xmlsource union all
select 20 id, xml_string xml_data from xmlsource union all
select 30 id, xml_string xml_data from xmlsource
)
select *
from
XMLTable(
'
for $entry_user in $full_doc/full_list/list_entry/name_info
return <tuple>
<id>{data($entry_user/../#id_value)}</id>
<path>{$entry_user/name_path/text()}</path>
<name>{$entry_user/name_value/text()}</name>
</tuple>
'
passing (
select
XMLElement("full_list",
XMLAgg(
XMLElement("list_entry",
XMLAttributes(id as "id_value"),
XMLQuery(
'
declare function local:path-to-node( $nodes as node()* ) as xs:string* {
$nodes/string-join(ancestor-or-self::*/name(.), ''/'')
};(: function to construct path :)
for $i in $rdoc//name return <name_info><name_path>{local:path-to-node($i)}</name_path><name_value>{$i/text()}</name_value></name_info>
'
passing by value XMLParse(content xml_data) as "rdoc"
returning content
)
)
)
)
from xml_table
)
as "full_doc"
columns
id_val varchar2(4000) path '//tuple/id',
path_val varchar2(4000) path '//tuple/path',
name_val varchar2(4000) path '//tuple/name'
)
Update 3
As mentioned by #A.B.Cade in his comment, there are really simple way to join ID's with XQuery results.
Because I don't like external links in answers, code below represents his SQL fiddle, a little bit adapted to the data source from this answer:
with xmlsource as (
-- only for purpose to write long string only once
select '
<users><user><name>user1</name></user>
<user><name>user2</name></user>
<group>
<user><name>user3</name></user>
</group>
<user><name>user4</name></user>
</users>' xml_string
from dual
),
xml_table as (
-- model of xmltable
select 10 id, xml_string xml_data from xmlsource union all
select 20 id, xml_string xml_data from xmlsource union all
select 30 id, xml_string xml_data from xmlsource
)
select xd.id, x.* from
xml_table xd,
XMLTable(
'declare function local:path-to-node( $nodes as node()* ) as xs:string* {$nodes/string-join(ancestor-or-self::*/name(.), ''/'') }; for $i in $rdoc//name return <ret><name_path>{local:path-to-node($i)}</name_path>{$i}</ret> '
passing
XMLParse(content xd.xml_data
)
as "rdoc"
columns
name_path varchar2(4000) path '//ret/name_path',
name_value varchar2(4000) path '//ret/name'
) x
This is not perfect but can be a start:
Here is a sqlfiddle
with xslt as (
select '<?xml version="1.0" ?><xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<records>
<xsl:apply-templates/>
</records>
</xsl:template>
<xsl:template match="//name">
<columns>
<path>
<xsl:for-each select="ancestor-or-self::*">
<xsl:call-template name="print-step"/>
</xsl:for-each>
</path>
<value>
<xsl:value-of select="."/>
</value>
<xsl:apply-templates select="*"/>
</columns>
</xsl:template>
<xsl:template name="print-step">
<xsl:text>/</xsl:text>
<xsl:value-of select="name()"/>
<xsl:text>[</xsl:text>
<xsl:value-of select="1+count(preceding-sibling::*)"/>
<xsl:text>]</xsl:text>
</xsl:template>
</xsl:stylesheet>'
xsl from dual)
, xmldata as
(select xmltransform(xmltype('<?xml version="1.0"?>
<users><user><name>user1</name></user>
<user><name>user2</name></user>
<group>
<user><name>user3</name></user>
</group>
<user><name>user4</name></user>
</users>'), xmltype(xsl)) xd from xslt)
select XT.*
from xmldata c,
xmltable('$x//columns' passing c.xd
as "x"
columns
path_c VARCHAR2(4000) PATH 'path',
value_c VARCHAR2(4000) PATH 'value'
) as XT
This is what I tried to do:
Since you want the "path" I had to use xslt (credits to this post)
Then I used xmltransform for transforming your original xml with the xsl to the
desired output (path, value)
Then I used xmltable to read it as a table
This improves on above answer by A.B.Cade:
<xsl:template name="print-step">
<xsl:variable name="name" select="name()" />
<xsl:text>/</xsl:text>
<xsl:value-of select="$name"/>
<xsl:text>[</xsl:text>
<xsl:value-of select="1+count(preceding-sibling::*[name()=$name])"/>
<xsl:text>]</xsl:text>
</xsl:template>
With result:
/users[1]/user[1]/name[1] user1
/users[1]/user[2]/name[1] user2
/users[1]/group[1]/user[1]/name[1] user3
/users[1]/user[3]/name[1] user4
Instead of:
/users[1]/user[1]/name[1] user1
/users[1]/user[2]/name[1] user2
/users[1]/group[3]/user[1]/name[1] user3
/users[1]/user[4]/name[1] user4