views:

46

answers:

2

Say I have two tables

User
-----
id
first_name
last_name

User_Prefs
-----
user_id
pref

Sample data in User_Prefs might be

user_id | pref
  2     |  SMS_NOTIFICATION
  2     |  EMAIL_OPT_OUT
  2     |  PINK_BACKGROUND_ON_FRIDAYS

And some users might have no corresponding rows in User_Prefs.

I need to query for the first name and last name of any user who does NOT have EMAIL_OPT_OUT as one of their (possibly many, possibly none) User_Pref rows.

SELECT DISTINCT u.* from User u
LEFT JOIN User_Prefs up ON (u.id=up.user_id)
WHERE up.pref<>'EMAIL_OPT_OUT'

gets me everyone who has at least one row that isn't "EMAIL_OPT_OUT", which of course is not what I want. I want everyone with no rows that match "EMAIL_OPT_OUT".

Is there a way to have the join type and the join conditions filter out the rows I want to leave out here? Or do I need a sub-query?

+6  A: 

I personally think a "where not exists" type of clause might be easier to read, but here's a query with a join that does the same thing.

select distinct u.* from User u
left join User_Prefs up ON u.id = up.user_id and up.pref = 'EMAIL_OPT_OUT'
where up.user_id is null
Mayo
+1 The important thing to note here is that you can join on any criteria, and combine criteria. You're not limited to joining on matching keys.
Marcus Adams
+1 - I like this better than my answer, almost certainly faster as well.
Eric Petroelje
Absolutely what I was looking for. I was so close, too - just didn't have the brainwave of joining on "equals" instead of "not equals".
JacobM
For an explanation as to why the orginal did not work and this does, see:http://wiki.lessthandot.com/index.php/WHERE_conditions_on_a_LEFT_JOIN
HLGEM
A: 

Why not have your user preferences stored in the user table as boolean fields? This would simplify your queries significantly.

SELECT * FROM User WHERE EMAIL_OPT_OUT = false
Marcus Adams
Don't I wish I could change the schema. This isn't my database; I'm actually joining to a view coming out of a proprietary DB. Though I would point out that your approach has the downside that the set of possible options has to be hard-coded in the database schema.
JacobM
It would also make maintenance when you need to add a new type much more complicated. A related table as the poster has is the correct design.
HLGEM
@HLGEM, I disagree about the maintenance issue, but I use MVC, which abstracts all this stuff.
Marcus Adams