0

我有一个简单的用户偏好表,如下所示:

   id   |   user_id   |   preference_name   |   preference_value

使该表唯一的原因是如果 user_id 字段为空,则意味着它是该首选项的默认值。我正在尝试获取用户的所有首选项并仅在未为该用户指定实际值时才使用默认值。

所以基本上我需要:

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

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

有没有办法通过单个 SQL 查询来做到这一点,或者我应该在代码中做到这一点?

4

4 回答 4

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} ); 
于 2013-08-29T21:55:07.410 回答
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,并preference_name取自默认值或用户特定首选项,就像preference_value. 但是,第二个版本并不那么容易修改。

于 2013-08-29T21:58:59.857 回答
0

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

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

于 2013-08-29T21:58:48.177 回答
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 小提琴

于 2013-08-29T22:02:39.317 回答