仅当非空值不存在时才选择null?

时间:2013-08-29 21:49:21

标签: mysql sql sql-server

我有一个简单的用户首选项表,如下所示:

   id   |   user_id   |   preference_name   |   preference_value

这个表的唯一性是,如果user_id字段为null,则表示它是该首选项的默认值。我正在尝试获取用户的所有首选项,并且只有在没有为该用户指定实际值时才使用默认值。

所以基本上我需要:

SELECT * FROM user_preferences WHERE user_id = {userIdVar} OR user_id IS NULL;

但是,如果结果集中的另一行具有相同的preference_name和user_id的值,我想抛出user_id为null的结果。

有没有办法用单个SQL查询执行此操作,还是应该在代码中执行此操作?

4 个答案:

答案 0 :(得分:4)

使用NOT EXISTS

SELECT up1.* 
FROM   user_preferences up1 
WHERE  ( NOT EXISTS(SELECT 1 
                    FROM   user_preferences up2 
                    WHERE  user_id = {userIdVar}) 
         AND user_id IS NULL ) 
        OR ( user_id = {userIdVar} ); 

答案 1 :(得分:1)

您可以通过多种方式执行此操作,但如果所有首选项都有默认值,或者您在其他地方有完整的首选项列表,我会这样做:

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

你已经在MySQL和SQL Server上标记了你的问题,我不知道你正在寻找哪种方言。我知道SQL Server接受这种语法,但可能需要对MySQL进行一些调整。

编辑:funkwurm指出子查询使得这可能在MySQL上表现不佳。如果结果是一个问题,可以在没有子查询的情况下重写它

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

编辑2 :如果首选项没有默认值,则可以修改第一个版本以使用full join而不是left join,然后执行{{ 1}}来自默认值或用户特定的首选项,就像preference_name一样。但是,第二个版本不容易修改。

答案 2 :(得分:0)

COALESCE返回提供的参数的第一个非空值:http://dev.mysql.com/doc/refman/5.0/en/comparison-operators.html#function_coalesce

因此,如果您获取一组默认首选项并使用用户首选项加入它们,则可以使用列中的COALESCE填充正确的值。

答案 3 :(得分:0)

这应该可以选择第一行为NULL或者设置user_id变量,如果两者都设置了user_id变量,那么只显示每个preference_name一次。

SELECT
  *
FROM 
  (
    SELECT
      *
    FROM
      user_preferences
    WHERE
      user_id = {userIdVar} OR
      user_id IS NULL
    ORDER BY
      CASE WHEN user_id IS NULL THEN 1 ELSE 0 END
  ) sub_query
GROUP BY
  preference_name

<强> SQL FIDDLE