执行count()计算会减慢我的mysql查询吗?

时间:2010-01-08 19:12:03

标签: sql performance mysql count

我还在学习MySQL。我可能会犯一个非常基本的错误,我准备在这里受到惩罚......

此查询尝试做的是根据他们所做的书籍和食谱评论的数量从我们网站中选择最高成员。

我正在计算SQL查询本身的总数。考虑到目前为止我们只有400名成员和几千条评论并且它的增长速度非常快,因此查询速度很慢(9秒)并且肯定无法扩展。

我认为它在这里进行全表扫描,并且计算速度正在减慢,但我不知道另一种方法可以做到这一点,并且会喜欢一些智慧。

这是SQL语句:

SELECT users.*, COUNT( DISTINCT bookshelf.ID ) AS titles, COUNT( DISTINCT book_reviews.ID ) as bookreviews, COUNT( DISTINCT recipe_reviews.ID ) AS numreviews, COUNT( DISTINCT book_reviews.ID ) + COUNT( DISTINCT recipe_reviews.ID ) as reviewtotal
FROM users
LEFT OUTER JOIN recipe_reviews ON recipe_reviews.user_id = users.ID
LEFT OUTER JOIN book_reviews ON book_reviews.user_id = users.ID
LEFT OUTER JOIN bookshelf ON users.ID = bookshelf.user_id
GROUP BY users.ID
ORDER BY reviewtotal DESC
LIMIT 8

以下是说明:

+----+-------------+----------------+-------+-------------------+-------------------+---------+---------------------+------+---------------------------------+
| id | select_type | table          | type  | possible_keys     | key               | key_len | ref                 | rows | Extra                           |
+----+-------------+----------------+-------+-------------------+-------------------+---------+---------------------+------+---------------------------------+
|  1 | SIMPLE      | users          | index | NULL              | PRIMARY           | 4       | NULL                |  414 | Using temporary; Using filesort | 
|  1 | SIMPLE      | recipe_reviews | ref   | recipe_reviews_fk | recipe_reviews_fk | 5       | users.ID            |   12 |                                 | 
|  1 | SIMPLE      | book_reviews   | ref   | user_id           | user_id           | 5       | users.ID            |    4 |                                 | 
|  1 | SIMPLE      | bookshelf      | ref   | recipe_reviews_fk | recipe_reviews_fk | 5       | users.ID            |   13 |                                 | 
+----+-------------+----------------+-------+-------------------+-------------------+---------+---------------------+------+---------------------------------+

更新&解决:

我意识到,并且@recursive确认,查询是问题的根源。我从这里得到笛卡尔产品。我把它重写为一系列子查询,最终的工作代码在这里:

SELECT  *, bookreviews + recipereviews AS totalreviews
FROM (SELECT users.*,
            (SELECT count(*) FROM bookshelf WHERE bookshelf.user_id = users.ID) as titles,
            (SELECT count(*) FROM book_reviews WHERE book_reviews.user_id = users.ID) as bookreviews,
            (SELECT count(*) FROM recipe_reviews WHERE recipe_reviews.user_id = users.ID) as recipereviews
    FROM users) q

这给我一个毫秒的结果。还有一些方法可以使用JOIN来完成此操作。如果您想要跟进,请参阅How to add together the results of several subqueries?

7 个答案:

答案 0 :(得分:3)

对于这样的功能,使用某种缓存总是有帮助的......

可能已经有助于每晚为所有用户创建总和并将这些总和存储在用户中。这将有很大帮助并加快您的搜索速度。

您还应该以某种方式缓存此请求至少一分钟或五分钟,因为您将在登录时独立执行相同的请求。

答案 1 :(得分:2)

您可能会尝试查看删除DISTINCT修饰符是否有所改进。假设DISTINCT ed字段无论如何都是主键,这可能导致不必要的工作。

答案 2 :(得分:2)

索引user_id上的所有表格。如果还没有完成,这可以很容易地将这个查询提高几个数量级。

答案 3 :(得分:2)

您需要在user_id上创建索引(如果可能,最好是聚簇索引)。

你确定你做到了吗?请记住,拥有外键不会自动生成该键的索引。

如果要加入4个每行1k行的B树,这肯定不会花费9秒,而是几毫秒。

执行时间过长表示您正在为每个用户执行表扫描。

我非常确信这是正确答案。

您的查询没有问题,只是您要两次计算您的评价,将第二个计数替换为bookreviews和numreviews。

答案 4 :(得分:1)

您正在尝试使用此查询完成太多事情。我看到你的db / query设计有问题。为什么book_shelf中有user_id?如下表结构

CREATE TABLE users (
id INT NOT NULL AUTO_INCREMENT ,
name VARCHAR( 20 ) NOT NULL ,
PRIMARY KEY ( `id` )
)

CREATE TABLE recipe_reviews (
id INT NOT NULL AUTO_INCREMENT ,
review VARCHAR( 20 ),
user_id INT,
PRIMARY KEY (id),
FOREIGN KEY (user_id) references users(id)
)

CREATE TABLE bookshelf (
id INT NOT NULL AUTO_INCREMENT ,
name VARCHAR( 20 ) NOT NULL ,
PRIMARY KEY ( id )
)

CREATE TABLE book_reviews (
id INT NOT NULL AUTO_INCREMENT ,
review VARCHAR( 20 ),
user_id INT,
bookshelf_id INT,
PRIMARY KEY (id),
FOREIGN KEY (user_id) references users(id),
FOREIGN KEY (bookshelf_id) references bookshelf(id)
)

如果您想在用户上进行汇总,这是您的查询:

SELECT users.*, COUNT(book_reviews.ID ) as bookreviews, COUNT( recipe_reviews.ID ) AS recipereviews, bookreviews + recipereviews as reviewtotal
    FROM users
    LEFT OUTER JOIN recipe_reviews ON recipe_reviews.user_id = users.ID
    LEFT OUTER JOIN book_reviews ON book_reviews.user_id = users.ID
    GROUP BY users.ID
    ORDER BY reviewtotal DESC

您还可以在用户和书籍上进行汇总,然后包括recipe_reviews没有意义。

PS:你不需要DISTINCT,因为你有钥匙照顾它。

答案 5 :(得分:0)

我经常发现从较大的表创建一个较小的临时表将具有显着的速度优势。

所以基本过程:

  1. 将查询(带连接)存储到临时表
  2. 在临时表上运行计数/摘要查询

答案 6 :(得分:0)

为什么不将每个用户的评论数量存储为users表中的列?用户所做的每一次新审核都应该要求将用户记录审核次数增加一次。

例如:

user_id user_name number_of_reviews
1       bob       5
2       jane      10
鲍勃提出了一个新的评论,并将你的电话号码改为6:

review_id user_id review_text
16        1       "Great!"

user_id user_name number_of_reviews
1       bob       6
2       jane      10

现在你可以简单地得到这样的前5位评论家:

SELECT * FROM users ORDER BY number_of_reviews DESC LIMIT 5