【问题标题】:Optimizing mysql query (likes/dislikes)优化 mysql 查询(喜欢/不喜欢)
【发布时间】:2014-03-30 01:21:51
【问题描述】:

我的网站包含用户可以投票的内容片段(喜欢/不喜欢类似于 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 ... ]

有没有更好的方法来实现这一点(组合这些子查询或其他方式)?

【问题讨论】:

标签: mysql sql optimization count subquery


【解决方案1】:

在性能方面,那些相关的子查询可以吃掉你的午餐。并且由于 MySQL 处理它们的方式,也吞噬了你的午餐盒,对于大型集。这些子查询中的每一个都会针对外部查询中返回的每一行执行。对于大型套装来说,这可能会变得非常昂贵。

另一种方法是使用内联视图来具体化所有内容的好恶,然后对其执行连接操作。

但是,这种方法也可能代价高昂,尤其是当您只需要对无数行中的几行内容进行“计数”时。通常,外部查询的谓词也可以合并到内联视图中,以限制需要检查和返回的行数。

我们想对该内联视图使用 OUTER 连接,因此它会返回与您的查询等效的结果;当vote 表中没有匹配的行时,从content 返回一行。

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。对于相关子查询,您希望它们使用索引范围扫描,而不是完整扫描。

【讨论】:

  • 您的回答真的很有帮助。非常感谢!
  • @arkona:我重新阅读了您的问题,我看到我错过了您确实说过您正在获得“单个内容”。如果您在查询中使用WHERE c.contentId = 'val',您希望在内联视图查询中包含一个等价物,例如WHERE vt.cId = 'val'.
【解决方案2】:

您的问题很简单,您当前的子查询正在针对返回的每一行运行。

您需要改为加入该数据。您需要更改我添加的代码以提供正确的计数,但这应该会为您指明正确的方向。

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

【讨论】:

    猜你喜欢
    • 2012-12-23
    • 1970-01-01
    • 1970-01-01
    • 2019-07-03
    • 2012-03-17
    • 2011-07-17
    • 1970-01-01
    • 2013-10-23
    • 2020-03-25
    相关资源
    最近更新 更多