我有一张桌子,上面写着用户写的品酒笔记,另一张桌子上有其他用户给每张品酒笔记的评分。
显示您尚未评级的其他用户编写的所有笔记的查询如下所示:
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
但每次运行上述查询时仍会返回....
答案 0 :(得分:2)
MySQL允许您以一种相当特殊的方式使用group by
而不抱怨,请参阅documentation:
如果禁用ONLY_FULL_GROUP_BY,则对GROUP BY的标准SQL使用的MySQL扩展允许选择列表,HAVING条件或ORDER BY列表引用非聚合列,即使这些列在功能上不依赖于GROUP BY列。 [...] 在这种情况下,服务器可以自由选择每个组中的任何值,因此除非它们相同,否则所选的值是不确定的,这可能不是您想要的。
此行为是MySQL 5.7之前的默认行为。
在您的情况下,如果特定tasteNoteRate
的{{1}}中有多行,那么如果其他人已经投票支持该注释noteID
,那么使用没有聚合函数的userScored
将基于随机行 - 可能是错误的行。
您可以使用聚合修复此问题:
tasteNoteRate.userVoting
或者,因为比较结果(除select ...,
max(CASE
WHEN tasteNoteRate.userVoting = 1162 THEN 1
ELSE 0
END) AS userScored
from ...
之外的其他内容)是1或0,您还可以使用更短的版本:
null
要准备升级到MySQL 5.7(并启用select ...,
coalesce(max(tasteNoteRate.userVoting = 1162),0) AS userScored
from ...
),您还应ONLY_FULL_GROUP_BY
列出group by
列表中的所有非汇总列:{{1} }。
编写查询(以及其他方式)的另一种方法是在子查询中对select
进行分组,因此您不必group by tastingNotes.userID, tastingNotes.beerID, tastingNotes.noteID, tastingNotes.note
tastingNoteRates
group by
列tastingNotes
1}}:
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;
这也可以让您通过将rates.userScored = 0
- 子句中的on
更改为= 1
(或删除它以获取两者)来获取用户投票的注释。
答案 1 :(得分: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
添加另一个子句...&#34; and tasteNoteRate.userVoting = 1162
&#34;?
如果这些表不是1-1,看起来像是(给定sum()和&#34; group by&#34;),那么你将面临当前查询的爆炸性问题。如果每个音符可以有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
,这样您只能获得其他用户的提示信息。笔记。
也许这有帮助,也许不是,但是,是的,SCHEMA和具体的用例/示例数据会有所帮助。
通过这种&#34;等级评级&#34;,有时最好保持投票总数的汇总表,并只跟踪用户已投票的内容。例如不要在选择查询中将它们全部加起来。相反,在insert...on duplicate key update (total = total + 1)
中总结一下;至少这就是我如何处理一些用户排名表中的问题。他们的成长速度如此之快。