I have a table history (id int, content xml) in postgreSQL. XML content for one of the id as as following
<history-data>
<history recorded-date="20110601">
<assignees>
<assignee>
<last-name>CIENA LUXEMBOURG</last-name>
</assignee>
</assignees>
<assignors>
<assignor execution-date="20110517">
<last-name>NORTEL NETWORKS LIMITED</last-name>
</assignor>
</assignors>
</history>
<history recorded-date="20110601">
<assignees>
<assignee>
<last-name>CIENA CORPORATION</last-name>
</assignee>
</assignees>
<assignors>
<assignor execution-date="20110527">
<last-name>CIENA LUXEMBOURG</last-name>
</assignor>
</assignors>
</history>
<history recorded-date="20090430">
<assignees>
<assignee>
<last-name>NORTEL NETWORKS</last-name>
</assignee>
</assignees>
<assignors>
<assignor execution-date="20090424">
<last-name>MAK, GARY</last-name>
</assignor>
<assignor execution-date="20090424">
<last-name>VELEZ, EDGAR</last-name>
</assignor>
</assignors>
</history>
</history-data>
Here, i want to get last-name & it's respective execution-date. For the above example, i want the following output
last-name execution-date
================ ==============
CIENA LUXEMBOURG 20110517
CIENA CORPORATION 20110527
NORTEL NETWORKS 20090424
I am able to generate all possible combinations using the following SQL query but not able to get output like above
SELECT id, unnest(CAST(xpath('/history-data/history/assignees/assignee/last-name/text()',content) AS text)::text[]) AS last-name,
unnest(CAST(xpath('/history-data/history/assignors/assignor/@execution-date',content) AS text)::text[]) AS execution-date
FROM history
WHERE id = 10
any suggestions on how can this be done ?
You need to iterate over all history
nodes and get appropriate elements with xpath()
function. By default, result of xpath extraction returns array of xml, that's why we need to get actual value with array index (...)[1]
; sample query may be the following:
SELECT
(xpath('//assignee/last-name/text()',xml_element))[1] AS "last-name",
(xpath('//assignor/@execution-date',xml_element))[1] AS "execution-date"
FROM (
SELECT unnest(xpath('//history',content)) AS xml_element FROM history
WHERE id = 10
) t;
Result is:
last-name | execution-date
-------------------+----------------
CIENA LUXEMBOURG | 20110517
CIENA CORPORATION | 20110527
NORTEL NETWORKS | 20090424
(3 rows)
When assignees
has multiple assagnee
nodes query should use unnest()
to get all array elements:
SELECT
unnest(xpath('//assignee/last-name/text()',xml_element)) AS "last-name",
unnest(xpath('//assignor/@execution-date',xml_element)) AS "execution-date"
FROM (
SELECT unnest(xpath('//history',content)) AS xml_element FROM history
WHERE id = 10
) t;
What your request does is find all the assignees, and, independently, find all the execution dates, and returns the cartesian product, which is probably not what you actually want.
What you want is:
history
elementshistory
element, find the text/attribute you're interested in.This means using a subquery:
SELECT
unnest(xpath('./assignees/assignee/last-name/text()',item))::text,
unnest(xpath('./assignors/assignor/@execution-date',item))::text
FROM (
SELECT
unnest(xpath('/history-data/history',content)) AS item
FROM history
WHERE id = 10
) s
GROUP BY 1,2;
Note that you will probably get weird results if you have several assignee
s in a single history
element. Also, not sure if you want all execution-date
s, or just the first, or last, or...
EDIT
To get all assignee
s, but only the first execution-date
listed:
SELECT
unnest(xpath('./assignees/assignee/last-name/text()',item))::text,
(xpath('./assignors/assignor/@execution-date',item))[1]::text
FROM (
SELECT
unnest(xpath('/history-data/history',content)) AS item
FROM history
WHERE id = 10
) s
GROUP BY 1,2;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With