2017-03-07 77 views
0

我有一張桌子,裏面填充了用戶寫的品酒筆記,另一張桌子上還有其他用戶給每個品酒筆記的評分。mySQL帶回結果它不應該

,它提出了由您還沒有評分看起來像這樣其他用戶寫入的所有票據查詢:

SELECT tastingNotes.userID, tastingNotes.beerID, tastingNotes.noteID, tastingNotes.note, COALESCE(sum(tasteNoteRate.Score), 0) as count, 
CASE 
WHEN tasteNoteRate.userVoting = 1162 THEN 1 
ELSE 0 
END AS userScored 
FROM tastingNotes 
left join tasteNoteRate on tastingNotes.noteID = tasteNoteRate.noteID 
WHERE tastingNotes.userID != 1162 
Group BY tastingNotes.noteID 
HAVING userScored < 1 
ORDER BY count, userScored 

用戶1162寫了張便條做筆記113在tasteNoteRate表就說明起來就是:

noteID | userVoting | score 
    113  1162  0 

,但它仍然是返回上述各運行查詢時....

+2

請仔細閱讀關於使用SQL92兼容的'GROUP BY' – Kermit

+0

我究竟應該在那裏尋找什麼@Kermit – Mike

+0

我認爲它也可能對您嘗試實現以及嘗試的解釋有用這不符合你的期望。這種特定的方法可能完全被誤導,並且可能有一種更簡單的方法來做你正在嘗試做的事情。 – moreON

回答

0

變更爲內連接。

tasteNoteRate表正在加入到tastingNotes中,這意味着返回完整的tastingNotes表(匹配where),然後通過tasteNoteRate表中的匹配字段進行擴展。如果tasteNoteRate不滿意,它不會阻止tastingNotes返回匹配的字段。內部連接將取交點。

看到這裏的各類加入的更多的解釋:

What's the difference between INNER JOIN, LEFT JOIN, RIGHT JOIN and FULL JOIN?

確保創建兩個表中或noteID索引這個查詢和使用情況將很快發生爆炸。

注意:根據你寫的用例,我仍然不能100%確定你想加入noteID。事實上,它會嘗試爲所有用戶提供所有用戶的連接表,併爲所有用戶提供所有用戶的評分。我認爲CASE ... END會干擾查詢優化器,並將其變爲完整掃描+連接。爲什麼不只是在where ...「and tasteNoteRate.userVoting = 1162」中添加另一個子句?

如果這些表格不是1-1,因爲它看起來像(給出sum()和「group by」),那麼您將面臨當前查詢的爆炸性問題。如果每個音符可以有10個不同的評分,並且有10個音符,則有100個候選結果行。如果它增長到1000和1000,則會快速耗盡內存。消除userID未投票的幾行將從最終的1,000,000+中刪除10行,然後對它們進行求和和分組?

你可以做的另一種方式是扭轉左連接:

select ...,sum()... from tasteNoteRate ... left join tastingNotes using (noteID) where userID != xxx group by noteID,這樣,你只能得到其他用戶的筆記tastingNotes信息。

也許這有助於,也許不會,但是,SCHEMA和具體的用例/示例數據會有所幫助。

有了這種「收視率評級」,有時候它最好保留投票總數的總結表,並且跟蹤用戶已經投票的內容。例如不要將它們總結在選擇查詢中。相反,總結它在insert...on duplicate key update (total = total + 1);至少這就是我在一些用戶排名表中處理問題的方式。它們變得如此之快如此之大。

2

MySQL允許你在一個比較特殊的方式使用group by沒有抱怨,看到documentation

如果ONLY_FULL_GROUP_BY被禁用,一個MySQL擴展到標準SQL使用GROUP BY的允許選擇列表,HAVING條件或ORDER BY列表來引用非聚合列,即使這些列在功能上不依賴於GROUP BY列。 在這種情況下,服務器可以自由選擇每個組中的任何值,因此除非它們相同,否則所選的值是不確定的,這可能不是您想要的

此行爲是MySQL 5.7之前的默認行爲。

在你的情況,這意味着,如果在tasteNoteRate多行特定noteID,因此,如果其他人已經投了這一點,userScored,這是使用tasteNoteRate.userVoting沒有聚合函數,將基於一個隨機的行 - 可能是錯誤的。

您可以修復使用的骨料:

select ..., 
    max(CASE 
    WHEN tasteNoteRate.userVoting = 1162 THEN 1 
    ELSE 0 
    END) AS userScored 
from ... 

,或者因爲比較(比null其他的東西)的結果是1或0,你也可以使用一個較短的版本:

select ..., 
    coalesce(max(tasteNoteRate.userVoting = 1162),0) AS userScored 
from ... 

爲升級到MySQL 5.7準備(並啓用ONLY_FULL_GROUP_BY),你應該在你select -list也已經group by所有非聚合列:group by tastingNotes.userID, tastingNotes.beerID, tastingNotes.noteID, tastingNotes.note

寫你的查詢(其中包括)以不同的方式是做的tastingNoteRates分組中的子查詢,所以你不必group bytastingNotes所有列:

select tastingNotes.*, 
     coalesce(rates.count, 0) as count, 
     coalesce(rates.userScored,0) as userScored 
from tastingNotes 
left join (
    select tasteNoteRate.noteID, 
     sum(tasteNoteRate.Score) as count, 
     max(tasteNoteRate.userVoting = 1162) as userScored 
    from tasteNoteRate 
    group by tasteNoteRate.noteID 
) rates 
on tastingNotes.noteID = rates.noteID and rates.userScored = 0 
where tastingNotes.userID != 1162 
order by count; 

這也允許您通過將on-clause中的rates.userScored = 0更改爲= 1(或將其刪除以獲取兩者),來獲取用戶投票的備註。

+0

感謝您的幫助和很好的解釋! – Mike

相關問題