我的MySQL数据库中有以下两个(为了示例而简化)表:
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表中有数千条记录,查询最多需要几分钟才能完成。
SELECT `appname_item`.`id`, `appname_item`.`name`, COUNT(`appname_favorite`.`id`) AS `num_favorites`
FROM `appname_item`
LEFT OUTER JOIN `appname_favorite` ON (`appname_item`.`id` = `appname_favorite`.`item_id`)
GROUP BY `appname_item`.`id`, `appname_item`.`name`
ORDER BY `num_favorites` DESC;
以下是EXPLAIN的结果,它提供了一些有关查询速度如此之慢的信息(类型为“ALL”,“使用临时”和“使用filesort”应该尽可能避免。)
+----+-------------+--------------------+------+-----------------------------+-----------------------------+---------+-------------------------------+------+---------------------------------+
| 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 | |
+----+-------------+--------------------+------+-----------------------------+-----------------------------+---------+-------------------------------+------+---------------------------------+
我知道优化查询的最简单方法是添加一个索引,但我似乎无法弄清楚如何为涉及JOIN和order_by的Count()查询添加索引。我还应该提一下,我通过Django ORM运行它,所以更愿意不更改sql查询,只是修复和微调数据库以最有效的方式运行查询。
我一直试图解决这个问题,所以任何帮助都会非常感激!
更新
以下是db中已有的索引:
+--------------------+------------+-----------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| 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 :(得分:2)
实际上你无法避免使用filesort,因为计数是在计算时确定的,并且在索引中是未知的。我能想象的唯一解决方案是为表 appname_item 创建一个复合索引,根据您的特定数据,这可能有所帮助或有所帮助:
ALTER TABLE appname_item ADD UNIQUE INDEX `item_id_name` (`id` ASC, `name` ASC);
答案 1 :(得分:1)
您的查询没有任何问题 - 看起来不错。
可能是优化器有关于该表的过时信息。试试这个:
ANALYZE TABLE <tableaname>;
所涉及的所有表格。
答案 2 :(得分:0)
首先,对于count()函数,您可以查看此答案以了解更多详细信息: https://stackoverflow.com/a/2710630/1020600
例如,使用MySQL,count(*)将在MyISAM表下快速完成 但在InnoDB下运行缓慢。在InnoDB下你应该使用count(1)或 计数(PK)
如果你的存储引擎是MYISAM,如果你想依靠行(我猜是这样),使用count(*)就足够了。
从你的EXPLAIN中,我发现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 |简单| appname_favorite |指数| item_id | item_id | 5 | NULL | 2312 |运用 指数;使用临时;使用filesort | | 1 |简单| appname_item | eq_ref |主要|主要| 4 | test.appname_favorite.item_id | 1 |使用何处 | + ---- + ------------- + ------------------ + -------- + - ------------- + --------- + --------- + ---------------- --------------- + ------ + --------------------------- ------------------- +
在我的计算机上,表appname_item有1686行,appname_favorite有2312行,旧sql需要15到23ms。新的sql需要3.7到5.3毫秒