2017-03-07 80 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

相关问题