Reputation: 2158
I have an SQL (Postgres) table named “Items” with the following schema.
id | parent_id | name
Where the parent_id
column is a ForeignKey to the same table. (E.g items have parent items and those items can have parent items)
This makes a simple branching hierarchy.
Currently I query for the specific record and use a for loop
to get each of its parents/ children.
This results in numerous queries being executed.
What I’m wanting to do is query this table using the id of one of the items and get all of its parents/ children returned using a single query.
I can accomplish this by using .select_related(“parent__parent”)
or .prefetch_related(“item_set”)
for the children, but both of these approaches would require knowing the depth of the hierarchy before hand.
Is there a way in Django to get all of the related rows in a single query? Raw SQL is fine too.
Upvotes: 1
Views: 738
Reputation: 795
This is a recursive solution for getting the parent and children hierarchy of a record.
select * from
(WITH RECURSIVE cte_items (id, name, parent_id) AS (
SELECT id, name, parent_id
FROM items
WHERE id = 1
UNION ALL
SELECT e.id, e.name, e.parent_id
FROM items e
INNER JOIN cte_items o ON o.id = e.parent_id)
SELECT id, name, parent_id FROM cte_items) child
union
select * from
(WITH RECURSIVE cte_items (id, name, parent_id) AS (
SELECT id, name, parent_id
FROM items
WHERE id = 1
UNION ALL
SELECT e.id, e.name, e.parent_id
FROM items e
INNER JOIN cte_items o ON o.parent_id = e.id)
SELECT id, name, parent_id FROM cte_items) parent
order by parent_id
Upvotes: 1
Reputation: 2158
OP Note
I have modified the great code provided by Nicola with the following.
The code is a little clearer and performs marginally fewer actions on the table.
The where id = 1
in each of the SELECTS is the actual id of the record you are wanting to query for.
WITH parents AS (
SELECT id, name, parent_id
FROM family
WHERE id = 1
UNION ALL
SELECT e.id, e.name, e.parent_id
FROM family e
INNER JOIN parents o ON e.id = o.parent_id)
, children as (
SELECT id, name, parent_id
FROM family
WHERE id = 1
UNION ALL
SELECT e.id, e.name, e.parent_id
FROM family e
INNER JOIN children o ON o.id = e.parent_id
)
SELECT id, name, parent_id FROM parents
UNION
SELECT id, name, parent_id FROM children;
Upvotes: 0