2013-08-29 56 views
0

我有一個簡單的用戶偏好表看起來像這樣:僅當非空值不存在時才選擇空位?

id | user_id | preference_name | preference_value 

是什麼讓這個獨特的表雖然是如果USER_ID字段爲空,這意味着它是針對偏好的默認值。我試圖獲得用戶的所有首選項,並且只有在沒有爲該用戶指定實際值的情況下才使用默認值。

所以基本上我需要:

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

,但我想拋出了一個user_id是空的結果,如果沒有在結果與同PREFERENCE_NAME和user_id的值設置另一行。

有沒有辦法做到這一點與單個SQL查詢,或者我應該這樣做的代碼?

回答

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

有你可以做到這一點不同的方式,但如果所有的喜好有一個默認值,或者你有其他地方的偏好的完整列表,我會做這樣的:

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。但是,第二個版本不太容易修改。

+0

在MySQL子查詢往往是資源密集型的,也許SQL-Server有優化,使得該子查詢是首選。我會選擇在一個子查詢代替直接選擇'FROM user_preferences'和走樣,所以你可以用自己加入它的默認值。 – funkwurm

+0

@funkwurm謝謝,會編輯。 – hvd

+0

這會返回錯誤的結果!看到我的答案和sql小提琴 – amaster

0

這應該選擇的第一行是NULL或設置在那裏如果同時設置user_ID的變量優先停留,然後顯示每個PREFERENCE_NAME只有一次user_ID的變量。

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

+0

聰明的主意,但這並不能保證在MySQL中工作,並且會在SQL Server和大多數其他數據庫系統中造成硬性錯誤。從[擴展到GROUP BY]引用(https://dev.mysql.com/doc/refman/5.7/en/group-by-extensions.html):「服務器可以自由選擇每個組的任何值,所以除非它們是相同的,否則所選的值是不確定的。此外,每個組中的值的選擇不會受到添加ORDER BY子句的影響。「 – hvd

+0

@ hvd我使用類似於MySQL的方式,它始終工作。我的理解是,MySQL會分組並使用第一個實例。如果子查詢的順序正確,那麼查詢將始終返回預期的結果......現在在sql-fiddle上工作 – amaster

+0

完全可能的是,對於特定版本的MySQL,對於特定的表,它總是會給你想要的結果,因爲您獲得的查詢計劃會導致您的查詢以您期望的方式執行。但是,如果文檔明確指出它將不確定地選擇一個值,並且您的ORDER BY'不足以使此查詢可靠,那麼我會非常謹慎地在其他數據庫或其他版本的MySQL上嘗試此操作。 – hvd