mysql - 优化 mysql 查询(喜欢/不喜欢)

标签 mysql sql optimization count subquery

我的网站包含用户可以投票的内容片段(喜欢/不喜欢类似于 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 处理它们的方式,对于大集合也吞噬你的午餐盒。对于外部查询中返回的每一行,都会执行这些子查询中的每一个。对于大集合来说,这可能会变得非常昂贵。

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

但是,这种方法也可能很昂贵,特别是当您只需要对无数行中的少数内容行进行投票“计数”时。通常,外部查询中的谓词也可以合并到内联 View 中,以限制需要检查和返回的行数。

我们想对该内联 View 使用 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 ... ]

请注意,内联 View 查询(别名为 v)将查看 votes 表中的每一行。如果您只需要一个子集,则考虑添加适当的谓词(在 WHERE 子句中或作为另一个表的 JOIN)。您的查询中的 [... OTHER STUFF ...] 没有指示它是从 content 返回几行还是您需要所有行因为您是通过 likes 等方式订购的。

对于从 content 表中选择的少量行,使用相关子查询(如在您的查询中)实际上比具体化一个巨大的内联 View 并对其执行连接操作要快。

哦...对于这两个查询,不用说,votes 表上具有前导列 cId 的适当索引将提高性能。对于内联 View ,您不希望 MySQL 必须对所有这些行执行 filesort 操作来执行 GROUP BY 的开销。对于相关子查询,您希望它们使用索引范围扫描,而不是全扫描。

关于mysql - 优化 mysql 查询(喜欢/不喜欢),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22054530/

相关文章:

php - 函数 __construct 上的数据库参数

mysql - 表中不存在键列

Mysql子查询优化

python - 是否可以修改 `__init__` 内的方法?

mysql - 当没有记录存在时返回一些东西,infobright db

mysql - 退出 MySQL 而不在 El Capitan 上重新启动

sql - 如何使用 'group by'(或类似的)语句来聚合几个指定的行?

mysql - 按多列分组并标记唯一的子组

python - 内部列表中的第一项尽可能高效

php - 向日期添加月份并插入数据库