1

我的数据库中有五个表。成员、项目、评论、投票和国家。我想买 10 件商品。我想计算每个项目的评论和投票数。我还想要提交每个项目的成员,以及他们来自的国家。

在这里和其他地方发布后,我开始使用子选择来获取计数,但是这个查询需要 10 秒或更长时间!

SELECT `items_2`.*, 
   (SELECT COUNT(*) 
   FROM `comments` 
   WHERE (comments.Script = items_2.Id) 
   AND (comments.Active = 1)) 
  AS `Comments`, 
   (SELECT COUNT(votes.Member) 
   FROM `votes` 
   WHERE (votes.Script = items_2.Id) 
   AND (votes.Active = 1)) 
  AS `votes`, 
  `countrys`.`Name` AS `Country` 
FROM `items` AS `items_2` 
INNER JOIN `members` ON items_2.Member=members.Id AND members.Active = 1 
INNER JOIN `members` AS `members_2` ON items_2.Member=members.Id 
LEFT JOIN `countrys` ON countrys.Id = members.Country 
GROUP BY `items_2`.`Id` 
ORDER BY `Created` DESC 
LIMIT 10

我的问题是这是否是正确的方法,是否有更好的方法来编写此语句,或者是否有一种完全不同的方法会更好。我应该单独运行子选择并汇总信息吗?

4

1 回答 1

2

是的,您可以将子查询重写为聚合连接(见下文),但我几乎可以肯定,速度缓慢是由于缺少索引而不是查询本身。用于EXPLAIN查看可以添加哪些索引以使查询在几分之一秒内运行。

作为记录,这里是聚合连接等价物。

SELECT `items_2`.*,
  c.cnt AS `Comments`,
  v.cnt AS `votes`,
  `countrys`.`Name` AS `Country` 
FROM `items` AS `items_2` 
INNER JOIN `members` ON items_2.Member=members.Id AND members.Active = 1 
INNER JOIN `members` AS `members_2` ON items_2.Member=members.Id 
LEFT JOIN (
  SELECT Script, COUNT(*) AS cnt 
   FROM `comments` 
   WHERE Active = 1
   GROUP BY Script
) AS c
ON c.Script = items_2.Id 
LEFT JOIN ( 
  SELECT votes.Script, COUNT(*) AS cnt 
   FROM `votes` 
   WHERE Active = 1
   GROUP BY Script
) AS v
ON v.Script = items_2.Id 
LEFT JOIN `countrys` ON countrys.Id = members.Country 
GROUP BY `items_2`.`Id` 
ORDER BY `Created` DESC 
LIMIT 10

但是,因为您正在使用LIMIT 10,所以几乎可以肯定,您当前拥有的子查询与我在上面提供的聚合连接等效项一样好(或更好)以供参考。

这是因为在聚合连接查询的情况下,一个糟糕的优化器(MySQL 的优化器远非一流)最终可能会为and表COUNT(*)的全部内容执行聚合工作,然后浪费掉除 10 个值(你的)之外的所有内容,而在原始查询的情况下,从一开始,就和表而言,它只会查看严格的最小值。CommentsVotesLIMITCommentsVotes

更准确地说,以原始查询的方式使用子查询通常会导致所谓的带有索引查找的嵌套循环。使用聚合连接通常会导致合并散列连接与索引扫描或表扫描。当循环数量很少(在您的情况下为 10)时,前者(嵌套循环)比后者(合并和散列连接)更有效。但是,当前者会导致太多循环时,后者会变得更有效(数万/数十万或更多),尤其是在磁盘速度较慢但内存很大的系统上。

于 2010-03-23T02:44:18.113 回答