【问题标题】:How to count number of rows which is related to another table如何计算与另一个表相关的行数
【发布时间】:2013-06-04 12:48:33
【问题描述】:

我有两张桌子:

类别

category_id         int(10)          UNSIGNED  AUTO_INCREMENT
category_title      varchar(255)

产品

product_id          int(10)          UNSIGNED  AUTO_INCREMENT
product_category    int(10)          UNSIGNED 
product_title       varchar(255)

product_category 是与category_id 相关的外键。这是一些数据:

category_id    category_title
-----------    --------------
          3    Cellphone
          4    Motherboard
          5    Monitor

product_id    product_category    product_title
----------    ----------------    -------------
         3    3                   Samsung Galaxy SIII
         4    3                   Apple iPhone 5
         5    3                   HTC One X

如何通过产品数量获取所有类别?

category_id    category_title    products_count
-----------    --------------    --------------
          3    Cellphone         3
          4    Motherboard       9
          5    Monitor           7

我使用了这个查询:

SELECT 
    `category_id` AS  `id`,
    `category_title` AS  `title`,
    COUNT(  `product_id` ) AS  `count` 

FROM  `ws_shop_category` 
    LEFT OUTER JOIN  `ws_shop_product`
        ON  `product_category` =  `category_id` 

GROUP BY  `category_id` 
ORDER BY  `title` ASC 

但耗时太长:(总共 254 个,查询耗时 4.4019 秒)。 我怎样才能使这个查询更好?


DESC

在查询前添加DESC,给我这个结果:

id  select_type table               type    possible_keys   key     key_len ref     rows    Extra
1   SIMPLE      ws_shop_category    ALL     NULL            NULL    NULL    NULL    255     Using temporary; Using filesort
1   SIMPLE      ws_shop_product     ALL     NULL            NULL    NULL    NULL    14320   

显示创建表

CREATE TABLE `ws_shop_product` (
 `product_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 `product_category` int(10) unsigned DEFAULT NULL,
 `product_title` varchar(255) COLLATE utf8_general_ci DEFAULT NULL,
 PRIMARY KEY (`product_id`)
) ENGINE=MyISAM AUTO_INCREMENT=14499 DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;

CREATE TABLE `ws_shop_category` (
 `category_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 `category_title` varchar(255) COLLATE utf8_general_ci DEFAULT NULL,
 PRIMARY KEY (`category_id`)
) ENGINE=MyISAM AUTO_INCREMENT=260 DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;

【问题讨论】:

  • 为什么你使用左外连接而不是内连接?这显然会减慢查询速度。更不用说它甚至会计算那些没有匹配的人。
  • 您是如何定义表上的索引的?慢查询通常表明没有使用索引。您可以通过在查询开头添加DESC 并执行它来查看查询计划。
  • @Edper 是的,内连接给了我一个快速的结果,但我对没有产品的类别使用了左外连接(计数 = 0)。
  • @ErikSchierboom 我在查询之前添加了DESC 的结果。
  • @user2450111 是的,这表明没有使用索引。我会尽快发布并回复。

标签: mysql sql performance


【解决方案1】:

您的表没有定义任何索引。不过,通过使用以下 statememts 添加索引很容易解决这个问题:

ALTER TABLE `product` ADD INDEX `product_category` (`product_category`);
ALTER TABLE `category` ADD PRIMARY KEY(category_id);

现在,如果您再次运行查询,DESC 应该会告诉您查询使用键并且应该更快。

【讨论】:

  • category 表已经有 category_id 作为primary key。我添加了第一个索引和我的查询,繁荣。非常感谢。一个问题:所有外键都必须是索引?
  • @user2450111 如果您在问题中添加了SHOW CREATE TABLE category; 输出(product 表也是如此),这将有所帮助。然后我们会知道所有使用的约束、索引和引擎。您的 EXPLAIN 计划未显示在 category 中定义的任何索引。
  • @ypercube 感谢您的评论,我已将其添加到我的帖子中 :)
  • @user2450111 并非所有外键都必须是索引(除非您想强制这些值有效),但在加入时它有很大帮助。
【解决方案2】:
SELECT ws_shop_category.*, count(ws_shop_product.product_category) as products_count        
from ws_shop_category
left join ws_shop_product
on (ws_shop_category.category_id = ws_shop_product.product_category)
group by
    ws_shop_category.category_id
order by  
    ws_shop_category.category_title asc 

【讨论】:

  • 这是做什么用的?
  • 显示所有类别,并计算每个类别中存在多少产品。我错过了什么吗?
  • 现在你编辑了,和OP的查询一样。但问题是效率,他已经有了查询。
【解决方案3】:

如果您需要高性能,我建议将一个类别中的产品数量存储为 product_category 的列或单独的表中。您可以在每次产品插入/删除时更新相关计数器(手动或使用触发器),也可以使用计划作业每 N 分钟更新一次所有计数器。

您可以在此处找到使用触发器的示例:Speeding up row counting in MySQL

【讨论】:

    猜你喜欢
    • 2014-08-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-07
    • 2022-07-23
    • 2011-10-03
    • 2016-07-14
    • 1970-01-01
    相关资源
    最近更新 更多