Use a combination of XMLGET
and GET
functions to traverse nested objects in an XML document.
The former helps fetch an entire tag object found below the current tag, while the latter allows querying attributes and regular values within the current tag.
SELECT
-- <dept> (root)
GET(xmldata, '@dept_id')::integer as dept_id,
GET(xmldata, '@dept_name')::string as dept_name,
-- <dept>.<employee>
GET(XMLGET(xmldata, 'employee'), '@empname')::string as employee_name,
-- <dept>.<employee>.<address>
GET(XMLGET(XMLGET(xmldata, 'employee'), 'address'), '@addr1')::string as address_1
FROM test_XML_copy;
Which should yield:
+---------+-----------+---------------+-----------+
| DEPT_ID | DEPT_NAME | EMPLOYEE_NAME | ADDRESS_1 |
|---------+-----------+---------------+-----------|
| 1 | Marketing | a | 123 abc |
| 2 | Sales | b | 456 def |
| ... | ... | ... | ... |
+---------+-----------+---------------+-----------+
Your example data shows no repetition of child tags, but if they do repeat (such as multiple employee
in each dept
) then FLATTEN
can first be used to produce one employee
per row and the above approach can be reapplied. Alternatively, if it is a fixed form of tag structure and they are always ordered, you can use the instance number in XMLGET
to point to each one (implicit default is 0, the first object).
An example that explodes the document into one row per employee and per address inner tags:
SELECT
xmldata:"@dept_id"::integer as dept_id,
xmldata:"@dept_name"::string as dept_name,
emp.value:"@empname"::string as employee_name,
addr.value:"@addr1"::string as address_1
FROM
test_XML_copy,
LATERAL FLATTEN(xmldata:"$") emp,
LATERAL FLATTEN(emp.value:"$") addr
WHERE emp.value:"@" = 'employee' AND addr.value:"@" = 'address';
(This yields results similar to the above, for the example provided in OP's question)
Note: You can also use the path syntax with $
and @
characters to navigate the structure instead of nesting functions, but these rely on strict ordering of the input data structure:
-- See outer / inner structures in 'JSON' form
SELECT
xmldata:"@" dept_tag,
xmldata:"$" dept_tag_contents
FROM test_XML_copy;
-- Sample equivalent query using path expressions, relying on ordering:
SELECT
xmldata:"@dept_id",
xmldata:"@dept_name",
xmldata:"$"[1]."@empname",
xmldata:"$"[1]."$"[1]."@addr1"
FROM test_XML_copy;