Reputation: 175646
I have XML that looks like:
<root>
<uid>789</uid>
<element>
<uid>123</uid>
<sub>
<text>XYZ</text>
</sub>
</element>
</root>
The only constant thing is <text>
node and the fact that <uid>
node could be 2 level up. Rest of nodes could have any name so I cannot use fully qualified paths.
Based of <text>
node I need to find <uid>
node which is the nearest or for simplicity 2 levels up in the tree.
I tried:
WITH cte("XML") AS (
SELECT '<root>
<uid>789</uid>
<element>
<uid>123</uid>
<sub>
<text>XYZ</text>
</sub>
</element>
</root>'
FROM dual
)
SELECT x.*, c.*
FROM cte c,XMLTable('//text'
PASSING XMLTYPE(c."XML")
COLUMNS
text VARCHAR2(4000) PATH '.'
--,guid VARCHAR2(40) PATH '../../uid' -- unsupported XQuery expression
--,guid VARCHAR2(40) PATH 'ancestor::node()[2]/uid'
-- unsupported XQuery expression\
) x
WHERE text IS NOT NULL;
I am looking for solution similar to SQL Server:
WITH cte("XML") AS (
SELECT CAST('<root>
<uid>789</uid>
<element>
<uid>123</uid>
<sub>
<text>XYZ</text>
</sub>
</element>
</root>' AS XML)
)
SELECT x.value('../../uid[1]', 'VARCHAR(10)') AS uid
,s.x.value('.', 'VARCHAR(10)') AS "text"
FROM cte c
CROSS APPLY c."XML".nodes('//text') s(x)
Upvotes: 0
Views: 2256
Reputation: 6346
You should use preceding
- it will return all nodes except any ancestor.
Order of preceding collection is from the beginning to the end.
If you do this preceding::uid
or more general preceding::*
result will be (789, 123).
Combining everything together:
WITH cte("XML") AS (
SELECT '<root>
<uid>789</uid>
<element>
<uid>123</uid>
<sub>
<text>XYZ</text>
</sub>
</element>
</root>'
FROM dual
)
SELECT x.*, c.*
FROM cte c,XMLTable('//text'
PASSING XMLTYPE(c."XML")
COLUMNS
text VARCHAR2(4000) PATH '.'
,guid VARCHAR2(40) PATH '(preceding::uid)[last() -1]/data(.)' -- 2 -levelup
) x
WHERE text IS NOT NULL;
Upvotes: 1
Reputation: 29022
One working solution is the following:
SELECT x.*, c.*
FROM cte c,XMLTable('//text/../..'
PASSING XMLTYPE(c."XML")
COLUMNS
text VARCHAR2(4000) PATH 'uid',
guid VARCHAR2(40) PATH 'sub/text'
) x
WHERE text IS NOT NULL;
Its result consists of the two columns 123
and XYZ
.
Upvotes: 1