Update
I launched four different ways to do this using SQL Server 2005 and included an execution plan.
-- 269 reads, 16 CPU SELECT * FROM Groups WHERE NOT EXISTS ( SELECT * FROM People WHERE People.GroupId = Groups.GroupId ); -- 249 reads, 15 CPU SELECT * FROM Groups WHERE ( SELECT COUNT(*) FROM People WHERE People.GroupId = Groups.GroupId ) = 0 -- 249 reads, 14 CPU SELECT * FROM Groups WHERE GroupId NOT IN ( SELECT DISTINCT GroupId FROM Users ) -- 10 reads, 12 CPU SELECT * FROM Groups LEFT JOIN Users ON Users.GroupId = Groups.GroupId WHERE Users.GroupId IS NULL
Thus, the latter, although perhaps the most widely read of the four, works best.
This is somehow unexpected for me, and to be honest, I still prefer the WHERE NOT EXISTS syntax, because I think it is more explicit - it reads exactly the way you are trying to do.
Matt hamilton
source share