Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/74.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
执行count()计算是否会降低mysql查询的速度?_Sql_Performance_Mysql_Count - Fatal编程技术网

执行count()计算是否会降低mysql查询的速度?

执行count()计算是否会降低mysql查询的速度?,sql,performance,mysql,count,Sql,Performance,Mysql,Count,我还在学习MySQL。我可能犯了一个非常基本的错误,我准备在这里接受惩罚 这个查询试图做的是根据他们所做的书评和食谱评论的数量,从我们的网站上选择顶级成员 我正在计算SQL查询本身中的总数。这个查询速度很慢(9秒),而且考虑到目前为止我们只有400个成员和几千条评论,而且它的增长速度非常快,因此肯定无法扩展 我假设它正在做一个完整的表格扫描,并且计算正在减慢速度,但是我不知道有什么替代方法可以做到这一点,我希望有一些智慧 下面是SQL语句: SELECT users.*, COUNT( DIST

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

这个查询试图做的是根据他们所做的书评和食谱评论的数量,从我们的网站上选择顶级成员

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

我假设它正在做一个完整的表格扫描,并且计算正在减慢速度,但是我不知道有什么替代方法可以做到这一点,我希望有一些智慧

下面是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

这将以毫秒为单位给出一个结果。还有一些方法可以通过连接实现这一点。看看你是否想跟进此事

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

它可能已经有助于每晚为所有用户创建总和,并与用户一起存储这些总和。这将大大有助于加快您的搜索速度


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

我经常发现,从较大的表创建较小的临时表将具有明显的速度优势

因此,基本过程是:

  • 将查询(带联接)存储到临时表中
  • 对临时表运行计数/摘要查询

  • 您可以尝试查看删除
    不同的
    修饰符是否有改进。假定
    DISTINCT
    字段是主键,这可能会导致不必要的工作。

    用户id
    上的所有表编制索引。如果查询尚未完成,则可以轻松地将查询速度提高几个数量级。

    为什么不将每个用户的评论数作为一列存储在用户表中?用户所做的每一次新审核都应该要求其用户记录审核计数的值增加1

    例如:

    user_id user_name number_of_reviews
    1       bob       5
    2       jane      10
    
    Bob提交了一份新的评论,你将他的数字增加到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
    

    您试图通过此查询完成的事情太多了。我发现您的数据库/查询设计存在问题。为什么书架上有用户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
    
    您还可以在用户和书籍上进行聚合,然后将菜谱和评论包括在内是没有意义的


    PS:您不需要使用DISTINCT,因为您有密钥来处理它。

    您需要在用户id上创建索引(如果可能,最好是聚集索引)

    你确定你这样做了吗?请记住,拥有外键不会自动在该键上生成索引

    如果要连接4个B树,每个B树的行数为1k,这肯定不会花费9秒,而是几毫秒

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

    我确信这是正确的答案


    您的查询很好,只是您要计算两次评论,将第二次计数替换为bookreviews和numreviews。

    我建议您在计算总量的同时,还要为该批计算总量配对一个“截止日期”。唉,每个user_id字段上都已存在索引。我尝试了此操作,结果每个count字段都有数千个索引。听起来您的数据库中可能有重复的记录。你检查过你的表了吗?我会检查这些表以确保-也许我需要将其中一些表的主键作为字段的组合,而不是直接的ID。例如,书架上有ID、user\u ID、cookbook\u ID。user\u ID和cookbook\u ID的组合应该是唯一的……一列“人工”主键不是一个坏主意,但它们应该已经被保证是唯一的,因此在删除不同的表后得到不同的结果这一事实向我建议了数据库问题。到目前为止,我没有发现任何其他数据库问题-使用这些表的每一个其他查询都能按预期工作。我将回顾一下这里的逻辑,看看我是否遗漏了什么。我还将尝试缓存/计算字段。我在设计网站的早期就考虑过类似的问题,并且被告知(如此)我不应该依赖于查询中的递增列。但这可能是一个更普遍的警告,因为我已经开始在很多事情上使用递增列。我想不出在您的设计中有什么问题的场景会有风险。如果它代表的是真实的实物库存或货币金额,我会建议你更谨慎一点。但除此之外,这就足够了。别让自己难堪!此外,如果您怀疑计数是否已关闭,您可以通过执行上述操作(使用联接计数*),重新计算“脱机”数据库副本上每个用户的评论数,以查看是否有任何差异。增加列通常是一个可怕的主意,这将不可避免地导致数据不一致。可以考虑在具有许多读取的非常大的数据库上使用它,但是您的数据库很小,因此您肯定应该始终避免使用它。您能否给出一些示例,说明在这种情况下它将如何“导致”数据不一致?谢谢您的想法。然而,bookshelf中有一个用户id,因为每个用户都有自己的书架,他们可以向其中添加网站上的任何书籍,因此必须与用户id关联,以确定每个用户的书架中有多少书籍。至于外键,我很抱歉