2011-08-21 65 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