There are various ways you can do this, but if all preferences have a default value, or you have a complete list of preferences somewhere else, I would do it like this:
select
default_preferences.preference_name,
coalesce(
real_user_preferences.preference_value,
default_preferences.preference_value) as preference_value
from
(select * from user_preferences where user_id is null)
as default_preferences
left join
(select * from user_preferences where user_id = @user_id)
as real_user_preferences
on
real_user_preferences.preference_name = default_preferences.preference_name
You've tagged your question both MySQL and SQL Server, I don't know which dialect you're looking for. I know SQL Server accepts this syntax, but it might need some tweaking for MySQL.
Edit: funkwurm points out that subqueries make this likely to perform poorly on MySQL. If that turns out to be a problem, it can be rewritten without subqueries as
select
default_preferences.preference_name,
coalesce(
real_user_preferences.preference_value,
default_preferences.preference_value) as preference_value
from
user_preferences as default_preferences
left join
user_preferences as real_user_preferences
on
real_user_preferences.preference_name = default_preferences.preference_name
and real_user_preferences.user_id = @user_id
where
default_preferences.user_id is null
Edit 2: if there are preferences that do not have a default value, the first version can be modified to use full join
instead of left join
, and take preference_name
from either the defaults or the user-specific preferences, just like preference_value
. However, the second version is not so easily modified.