2011-08-21 90 views
1

我有以下两个(简化例子的缘故)表在我的MySQL数据库:MySQL:如何使用“JOIN”和“order_by”加速“Count()”查询?

DESCRIBE appname_item; 

-----------------+---------------+------+-----+---------+----------------+ 
| Field   | Type   | Null | Key | Default | Extra   | 
+-----------------+---------------+------+-----+---------+----------------+ 
| id    | int(11)  | NO | PRI | NULL | auto_increment | 
| name   | varchar(200) | NO |  | NULL |    | 
+-----------------+---------------+------+-----+---------+----------------+ 

DESCRIBE appname_favorite; 

+---------------+----------+------+-----+---------+----------------+ 
| Field   | Type  | Null | Key | Default | Extra   | 
+---------------+----------+------+-----+---------+----------------+ 
| id   | int(11) | NO | PRI | NULL | auto_increment | 
| user_id  | int(11) | NO | MUL | NULL |    | 
| item_id  | int(11) | NO | MUL | NULL |    | 
+---------------+----------+------+-----+---------+----------------+ 

我试图获得通过的收藏数量排序的项目列表。下面的查询工作正常,但是Item表中有成千上万条记录,查询需要几分钟才能完成。

这里有解释的结果,它提供了一些见解,为什么查询是如此缓慢(类型为“ALL”,“使用临时”和“使用文件排序”都应该,如果可能避免。)

+----+-------------+--------------------+------+-----------------------------+-----------------------------+---------+-------------------------------+------+---------------------------------+ 
| id | select_type | table    | type | possible_keys    | key       | key_len | ref       | rows | Extra       | 
+----+-------------+--------------------+------+-----------------------------+-----------------------------+---------+-------------------------------+------+---------------------------------+ 
| 1 | SIMPLE  | appname_item  | ALL | NULL      | NULL      | NULL | NULL       | 574 | Using temporary; Using filesort | 
| 1 | SIMPLE  | appname_favorite | ref | appname_favorite_67b70d25 | appname_favorite_67b70d25 | 4  | appname.appname_item.id  | 1 |         | 
+----+-------------+--------------------+------+-----------------------------+-----------------------------+---------+-------------------------------+------+---------------------------------+ 

我知道,优化查询的最简单的方法是添加一个索引,但我似乎无法弄清楚如何添加一个索引,涉及加入一个计数()查询和ORDER_BY 。我还应该提到,我通过Django ORM运行它,所以宁愿不更改SQL查询,只是修复和微调数据库以最有效的方式运行查询。

我一直在试图弄清楚这一点,所以任何帮助将不胜感激!

UPDATE

下面是已经在数据库索引:

+--------------------+------------+-----------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| Table    | Non_unique | Key_name     | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | 
+--------------------+------------+-----------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| appname_favorite |   0 | PRIMARY      |   1 | id   | A   |   594 |  NULL | NULL |  | BTREE  |   | 
| appname_favorite |   1 | appname_favorite_fbfc09f1 |   1 | user_id  | A   |   12 |  NULL | NULL |  | BTREE  |   | 
| appname_favorite |   1 | appname_favorite_67b70d25 |   1 | item_id  | A   |   594 |  NULL | NULL |  | BTREE  |   | 
+--------------------+------------+-----------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
+0

分析来自两个表appname_item,appname_favorite – Prix

+0

做ID的表'appname_favorite'有什么差距? – Karolis

回答

1

实际上,您无法避免使用文件夹,因为在计算时确定了计数并且在索引中未知。我可以想像,唯一的解决方案是创建一个复合索引表appname_item,这可以帮助很少或没有,这取决于您的具体数据:

ALTER TABLE appname_item ADD UNIQUE INDEX `item_id_name` (`id` ASC, `name` ASC); 
+0

谢谢@Karolis。我试图添加上面的索引,但它似乎没有任何影响。要回答你的问题,“appname_favorite”表中的id确实存在差距(用户可能会对他们之前收藏的物品产生不满)。 –

1

没有什么不对您的查询 - 它看起来不错。

它可能是优化器有关于表的过期信息。尝试运行此:

ANALYZE TABLE <tableaname>; 

所涉及的所有表。

+0

谢谢@Bohemian。我为所涉及的所有表执行了这个操作,重新运行了EXPLAIN,并获得了与上面发布的结果相同的结果。 –

0

首先,对count()函数,您可以检查这个答案,了解更多的细节: https://stackoverflow.com/a/2710630/1020600

例如,使用MySQL,COUNT(*)会快速MyISAM表 但慢下一个InnoDB下。在InnoDB的,你应该使用count(1)或 计数(PK)

如果你的存储引擎是MyISAM和,如果你想指望行(我猜的话),使用COUNT(*)就足够了。

从你的解释,我发现没有钥匙的appname_item,如果我尝试添加一个条件,那么“键”出现

where `appname_item`.`id` = `appname_favorite`.`item_id` 

。如此有趣,但它的工作。

最终这样的SQL

explain SELECT `appname_item`.`id`, `appname_item`.`name`, COUNT(*) AS `num_favorites` 
FROM `appname_item` 
LEFT OUTER JOIN `appname_favorite` ON (`appname_item`.`id` = `appname_favorite`.`item_id`) 
where `appname_item`.`id` = `appname_favorite`.`item_id` 
GROUP BY `appname_item`.`id`, `appname_item`.`name` 
ORDER BY `num_favorites` DESC; 

+ ---- + ------------- + ------------ ------ + -------- + -------- + --------- + -------- - + ------------------------------- + ------ + --------- ------------------------------------- + | id | select_type |表| |键入| possible_keys |键
| key_len | ref |行|额外
| + ---- + ------------- + ------------------ + -------- + - -------------- + --------- + --------- + --------------- ---------------- + ------ + -------------------------- -------------------- + | 1 | SIMPLE | appname_favorite |索引| item_id | item_id | 5 | NULL | 2312 |使用 索引;使用临时;使用filesort | | 1 | SIMPLE | appname_item | eq_ref | PRIMARY | PRIMARY | 4 | test.appname_favorite.item_id | 1 |在哪里使用
| + ---- + ------------- + ------------------ + -------- + - -------------- + --------- + --------- + --------------- ---------------- + ------ + -------------------------- -------------------- +

在我的电脑上,表appname_item有1686行,appname_favorite有2312行,旧sql需要15到23ms。新的SQL需要3.7〜5.3ms