When working with many-to-many relationships, you often have a pivot table linking two entities — for example, members and groups. Each record in this table represents a link between a member and a group.
Sometimes, you want to find all members not linked to a specific group. For instance, imagine you want the list of member IDs for all members that are not part of group ID 1.
The example setup
Let’s say you have the following pivot table:
CREATE TABLE member_groups (
member_id bigint,
group_id bigint
);
Each record represents one membership link.
The problem
You want to get all member_id values for members that don’t have a link with group_id = 1.
The cleanest solution: NOT EXISTS
SELECT DISTINCT mg.member_id
FROM member_groups mg
WHERE NOT EXISTS (
SELECT 1
FROM member_groups mg2
WHERE mg2.member_id = mg.member_id
AND mg2.group_id = 1
);
This query returns all distinct member_ids that never appear in a row with group_id = 1.
Why it’s good:
- Works efficiently on large datasets
- Avoids unnecessary grouping
- Reads clearly: “select members where no record exists for group 1”
Alternative using GROUP BY
SELECT member_id
FROM member_groups
GROUP BY member_id
HAVING SUM(CASE WHEN group_id = 1 THEN 1 ELSE 0 END) = 0;
Here, we group by each member and only keep those where the count of group 1 links is zero.
Another option with LEFT JOIN
SELECT DISTINCT mg.member_id
FROM member_groups mg
LEFT JOIN member_groups mg1
ON mg.member_id = mg1.member_id AND mg1.group_id = 1
WHERE mg1.member_id IS NULL;
This version is easy to understand but can be less efficient on large tables.
Conclusion
When filtering out associations in a many-to-many relation, the NOT EXISTS pattern is usually the most readable and performant option. It clearly expresses intent: “Give me all members that don’t belong to group 1.”
If this post was enjoyable or useful for you, please share it! If you have comments, questions, or feedback, you can email my personal email. To get new posts, subscribe use the RSS feed.