A common model: lords
have peons
, and both lords
and peons
have things
. things
can be owned by one or more peons
and lords
. To display all things
owned directly or indirectly by a lord:
SELECT lords.id AS lord_id,
peons.id AS peon_id,
things.id AS thing_id
FROM lords
LEFT JOIN lords_things ON
lords.id = lords_things.lord_id
LEFT JOIN peons ON
lords.id = peons.lord_id
LEFT JOIN peons_things ON
peons.id = peons_things.peon_id
JOIN things ON
lords_things.thing_id = things.id OR
peons_things.thing_id = things.id
WHERE lords.id = 123
Now there are two problems:
The code will have to look at which of
lord_id
andpeon_id
is non-NULL
to determine which level the thing is connected to. There are lots of ways to solve this - For example, in Oracle you could saySELECT NVL2(things_lords.id, 'lord', 'peon') as level
and in SQL Server you should be able to say
SELECT CASE WHEN things_lords.id IS NULL THEN 'peon' ELSE 'lord' END AS level
but I don't think either of these are portable (at least to PostgreSQL). A different approach would use a
UNION
:SELECT lords.id AS owner_id, 'lord' AS level, things.id AS thing_id FROM lords JOIN lords_things ON lords.id = lords_things.lord_id JOIN things ON lords_things.thing_id = things.id WHERE lords.id = 123 UNION SELECT peons.id AS owner_id, 'peon' AS type, things.id AS thing_id FROM lords JOIN peons ON lords.id = peons.lord_id JOIN peons_things ON peons.id = peons_things.peon_id JOIN things ON peons_things.thing_id = things.id WHERE lords.id = 123
this is quite ugly since it duplicates almost all the code. Is there a more elegant portable solution for this?
- Some
things
may show up more than once. This is not a problem in my application, but mentioned for completeness.