Reputation: 5173
For example, if I have Balls and Boxes, balls can be in many boxes, and boxes can be locked, how can I select the balls that are not in a locked box?
balls
id name
== ====
1 in neither
2 in unlocked
3 in locked
4 in both
boxes
id locked
== ======
1 0
2 1
boxings
ball_id box_id
======= ======
2 1
3 2
4 1
4 2
I've come up with this using left joins, but it's returning the "in both" ball which I want to exclude.
SELECT balls.*
FROM balls
LEFT OUTER JOIN boxings ON boxings.ball_id = balls.id
LEFT OUTER JOIN boxes ON boxes.id = boxings.box_id
WHERE (boxings.box_id IS NULL or boxes.locked = 0)
AND boxes.id NOT IN (
SELECT id FROM boxes WHERE locked = 1
)
Desired results:
id name
== ====
1 in neither
2 in unlocked
SQL Fiddle: http://sqlfiddle.com/#!9/c26ab/4
Upvotes: 2
Views: 38
Reputation: 57023
I find it interesting that both answers already posted both take the same slightly odd approach of putting some of the join criteria in the WHERE
clause:
inner join boxes on boxes.id = boxings.box_id
where boxes.locked = 1
What is most odd is that I'm sure the same people advise elsewhere to not put join conditions in WHERE
clauses.
Anyway, here's a different variation on the anti-join approach:
SELECT *
FROM balls
WHERE id NOT IN ( SELECT ball_id
FROM boxings
NATURAL JOIN
( SELECT id AS box_id FROM boxes WHERE locked = 1 )
NATURAL JOIN
( SELECT id AS ball_id FROM boxings ) );
Upvotes: 0
Reputation: 65537
The most straightforwad way to do this is probably with NOT EXISTS
:
select id, name
from balls
where not exists (
select NULL
from boxings
inner join boxes on boxes.id = boxings.box_id
where boxes.locked = 1
and boxings.ball_id = balls.id
)
Upvotes: 0
Reputation: 1269553
I assume you mean: choose balls that are never in a locked box.
If so, a not exists
query comes to mind:
select b.*
from balls b
where not exists (select 1
from boxings bxb join
boxes bo
on bxb.box_id = bo.id
where bxb.ball_id = b.id and bo.locked = 1
);
Upvotes: 2