我的网站包含用户可以投票的内容(喜欢/不喜欢类似于reddit upvotes)。在选择单个内容时,我运行以下子查询来获取喜欢的数量,不喜欢的数量以及当前用户的投票。
投票存储在单独的表{contentId,userId,vote}
中SELECT
[... BUNCH OF FIELDS ...]
(SELECT COUNT(*) FROM votes vt WHERE vt.cId = c.contentId AND vote = '.Constants::LIKE.') AS likes,
(SELECT COUNT(*) FROM votes vt WHERE vt.cId = c.contentId AND vote = '.Constants::DISLIKE.') AS dislikes,
COALESCE((SELECT vote FROM votes vt WHERE vt.cId = c.contentId AND userId = '.USER_ID.'), '.Constants::NO_VOTE.') AS myVote
FROM content
[... OTHER STUFF ... ]
有没有更好的方法来实现这一点(结合这些子查询或其他方式)?
答案 0 :(得分:3)
就性能而言,那些相关的子查询可以吃你的午餐。因为MySQL处理它们的方式,所以也可以吞食你的午餐盒。对于外部查询中返回的每一行,都会执行每个子查询。对于大型套装而言,这可能会变得非常昂贵。
另一种方法是使用内联视图来实现所有内容的喜欢和不喜欢,然后对其进行连接操作。
但是,这种方法也可能很昂贵,特别是当您只需要对几行内容中的几个内容行进行“计数”投票时。通常,外部查询中有一个谓词也可以合并到内联视图中,以限制需要检查和返回的行数。
我们希望对该内联视图使用OUTER连接,因此它返回与查询等效的结果;当content
表中没有匹配的行时,从vote
返回一行。
SELECT [... BUNCH OF FIELDS ...]
, COALESCE(v.likes,0) AS likes
, COALESCE(v.dislikes,0) AS dislikes
, COALESCE(v.myvote,'.Constants::NO_VOTE.') AS myvote
FROM content c
LEFT
JOIN ( SELECT vt.cId
, SUM(vt.vote = '.Constants::LIKE.') AS likes
, SUM(vt.vote = '.Constants::DISLIKE.') AS dislikes
, MAX(IF(vt.userId = '.USER_ID.',vt.vote,NULL)) AS myvote
FROM votes vt
GROUP
BY vt.cId
) v
ON v.cId = c.contentId
[... OTHER STUFF ... ]
请注意,内联视图查询(别名为v
)将查看votes
表中的每一行。如果您只需要一个子集,那么考虑添加一个适当的谓词(在WHERE子句中或作为JOIN到另一个表)。查询中的[... OTHER STUFF ...]
没有任何迹象表明它是从content
返回几行还是因为您按likes
等排序而需要所有行。
对于从content
表中选择的少量行,使用相关子查询(如查询中)实际上比实现大型内联视图并对其执行连接操作更快。
哦......对于这两个查询,不言而喻,votes
表上具有前导列cId
的适当索引将有利于提高性能。对于内联视图,您不希望MySQL的开销必须对所有这些行执行filesort
操作来执行GROUP BY。对于相关子查询,您希望它们使用索引范围扫描,而不是完整扫描。
答案 1 :(得分:0)
您的问题很简单,您返回的每一行都会运行当前的子查询。
您需要加入该数据。您需要更改我添加的代码,以便为您提供正确的计数,但这应该指向正确的方向。
SELECT
BLAH
Likes,
Dislikes
FROM CONTENT as C
INNER JOIN (
SELECT
cID,
COUNT(votes) as Likes, --you will need to alter this
COUNT(votes) as Dislikes --to count your up and downvotes
FROM Votes
GROUP BY cID
) AS V
ON V.cID = C.ContentID