I have XML data where parent-child relationship was maintained based on the position in them XML (the child elements belong to the previous parent until the next parent is seen). Is it possible to use XMLTable()
to do this?
Consider the following sample XML
<myData>
<myParent>
<parentID>H1</parentID>
<parentName>Header 1</parentName>
</myParent>
<myChild>
<childID>D1</childID>
<childName>Detail D1</childName>
</myChild>
<myChild>
<childID>D2</childID>
<childName>Detail D2</childName>
</myChild>
<myParent>
<parentID>H2</parentID>
<parentName>Header 2</parentName>
</myParent>
<myChild>
<childID>D3</childID>
<childName>Detail D3</childName>
</myChild>
</myData>
I want is something like
PARENTID CHILDID CHILDNAME
----------- ----------- ----------------------
H1 D1 Detail D1
H1 D2 Detail D2
H2 D3 Detail D3
The most preferred way is to a select statement, though other methods (e.g., table function, ref cursor) are also helpful.
I can extract the parents and children as tables separately. However, due to the XML design, I can't find a key to join between them.
with xml as (
select xmltype(
'<myData>
<myParent>
<parentID>H1</parentID>
<parentName>Header 1</parentName>
</myParent>
<myChild>
<childID>D1</childID>
<childName>Detail D1</childName>
</myChild>
<myChild>
<childID>D2</childID>
<childName>Detail D2</childName>
</myChild>
<myParent>
<parentID>H2</parentID>
<parentName>Header 2</parentName>
</myParent>
<myChild>
<childID>D3</childID>
<childName>Detail D3</childName>
</myChild>
</myData>') data
from dual
)
select child.* from xml,
--XMLTable for header
--xmltable('myData/myParent' passing
--xml.data
--columns row_num for ordinality,
-- "parentID",
-- "parentName"
--) parent,
--XMLTable for detail
xmltable('myData/myChild' passing
xml.data
columns row_num for ordinality,
"childID",
"childName"
) child;
This outputs the only children as a table. (Commented out section is another XMLTable()
to extract the parent. However, there is no way to join the two)
You can use LAST_VALUE to get the nearest parent before the children (by row_num) :
with xml as (
select xmltype(
'<myData>
<myParent>
<parentID>H1</parentID>
<parentName>Header 1</parentName>
</myParent>
<myChild>
<childID>D1</childID>
<childName>Detail D1</childName>
</myChild>
<myChild>
<childID>D2</childID>
<childName>Detail D2</childName>
</myChild>
<myParent>
<parentID>H2</parentID>
<parentName>Header 2</parentName>
</myParent>
<myChild>
<childID>D3</childID>
<childName>Detail D3</childName>
</myChild>
</myData>') data
from dual
)
, expanded AS (
SELECT rn, childid, childname, parentid, parentname,
LAST_VALUE(parentid IGNORE NULLS)
OVER(ORDER BY RN ) AS latestparentid
FROM xml,
xmltable('myData/*' passing
xml.data
columns rn for ordinality,
childid PATH 'childID' ,
childname PATH 'childName',
parentid PATH 'parentID',
parentname PATH 'parentName'
) node
)
SELECT p.parentid, p.parentname, c.childid, c.childname
FROM expanded p
JOIN expanded c ON p.parentid = c.latestparentid
WHERE p.parentid IS NOT NULL
;
H1 Header 1
H1 Header 1 D1 Detail D1
H1 Header 1 D2 Detail D2
H2 Header 2
H2 Header 2 D3 Detail D3