We have a Projects table where projects can be nested by projectA.parent_id = projectB.id.
When selecting all projects that meet a given criteria, how can we select only the parent if both meet it (or the parent meets it) and only the child if the child meets it?
| id | parent_id | is_chosen |
|---|---|---|
| 1 | null | false |
| 2 | 1 | true |
| 3 | 2 | true |
| 4 | 1 | false |
| 5 | 4 | false |
| 6 | 1 | false |
| 7 | 6 | true |
| 8 | 1 | true |
| 9 | 8 | false |
SELECT p.id
FROM "Projects" p
JOIN "Projects" parent
ON p.parent_id = p.id
WHERE is_chosen = true
AND ...
The result should be 2,7,8 and not 2,3,7,8. 3 would be excluded because its parent 2 was selected.
What should be included in the AND to accomplish this, or should it be restructured?
CodePudding user response:
You can union two queries, one for the parents, one for the children. For example:
select distinct *
from (
select p.* -- finding parents
from projects p
join projects c on c.parent_id = p.id
where p.is_chosen
union all
select c.* -- finding children
from projects p
join projects c on c.parent_id = p.id
where not p.is_chosen and c.is_chosen
) x
Result:
id parent_id is_chosen
--- ---------- ---------
2 1 t
8 1 t
7 6 t
See example at db<>fiddle.
CodePudding user response:
Should be able to simplify to this:
SELECT p.id
FROM parents p
WHERE is_chosen = 'true'
AND NOT EXISTS (SELECT *
FROM parents p2
WHERE p2.is_chosen = 'true'
AND p2.id = p.parent_id)
CodePudding user response:
You can use CTE like this
WITH CTE AS (
SELECT *
FROM tab
WHERE is_chosen = TRUE
)
SELECT
id
FROM CTE
WHERE parent_id NOT IN (
SELECT id
FROM CTE
)
