2012-05-10 33 views
4

我有三个表:类别,文章和article_events,具有下列结构分组的MySQL查询优化

categories: id, name      (100,000 rows) 
articles: id, category_id     (6000 rows) 
article_events: id, article_id, status_id (20,000 rows) 

每个物品列最高article_events.id描述了每篇文章的当前状态。

我返回类别的表,有多少文章是他们的“1”最近期的事件STATUS_ID。

我迄今为止的工作,但相当慢(10秒)和我的表的大小。想知道是否有办法让这个更快。据我所知,所有表格都有适当的索引。

SELECT c.id, 
     c.name, 
     SUM(CASE WHEN e.status_id = 1 THEN 1 ELSE 0 END) article_count 
FROM categories c 
LEFT JOIN articles a ON a.category_id = c.id 
LEFT JOIN (
    SELECT article_id, MAX(id) event_id 
    FROM article_events 
    GROUP BY article_id 
) most_recent ON most_recent.article_id = a.id 
LEFT JOIN article_events e ON most_recent.event_id = e.id 
GROUP BY c.id 

基本上我已经加入到事件表的两倍,因为要求与MAX(ID)沿STATUS_ID只返回找到的第一个STATUS_ID,而不是与MAX(ID)行相关的一个。

任何方式加以改善?或者我只需要10秒钟?谢谢!

编辑:

这里是我的解释查询:

ID | select_type | table   | type | possible_keys | key   | key_len | ref     | rows | Extra 
--------------------------------------------------------------------------------------------------------------------------- 
1 | PRIMARY  | c    | index | NULL   | PRIMARY  | 4  | NULL     | 124044 | Using index; Using temporary; Using filesort 
1 | PRIMARY  | a    | ref | category_id | category_id | 4  | c.id     | 3  | 
1 | PRIMARY  | <derived2>  | ALL | NULL   | NULL  | NULL | NULL     | 6351 | 
1 | PRIMARY  | e    | eq_ref | PRIMARY  | PRIMARY  | 4  | most_recent.event_id | 1  | 
2 | DERIVED  | article_events | ALL | NULL   | NULL  | NULL | NULL     | 19743 | Using temporary; Using filesort 
+3

请,张贴在这里的'输出EXPLAIN ...'为您的查询。 – vyegorov

回答

1

如果您可以使用JOIN消除子查询,那么它通常执行得更好,因为派生表不能使用索引。这是你的查询,而无需子查询:

SELECT c.id, 
     c.name, 
     COUNT(a1.article_id) AS article_count 
FROM categories c 
LEFT JOIN articles a ON a.category_id = c.id 
LEFT JOIN article_events ae1 
    ON ae1.article_id = a.id 
LEFT JOIN article_events ae2 
    ON ae2.article_id = a.id 
    AND ae2.id > a1.id 
WHERE ae2.id IS NULL 
GROUP BY c.id 

您将要进行实验,索引和使用EXPLAIN进行测试,但这里是我的猜测(我假设id字段是主键,你正使用InnoDB):

categories: `name` 
articles: `category_id` 
article_events: (`article_id`, `id`) 
+0

这个。谢谢。我已经看到过这种获取最近行的方法,但是已经读过大型表慢。显然不是这种情况。查询运行时间为.0058秒。 – Charles

0

没有尝试,但我想这会节省一点工作的数据库:

SELECT ae.article_id AS ref_article_id, 
    MAX(ae.id) event_id, 
    ae.status_id, 
    (select a.category_id from articles a where a.id = ref_article_id) AS cat_id, 
    (select c.name from categories c where c.id = cat_id) AS cat_name 
FROM article_events 
GROUP BY ae.article_id 

希望帮助

编辑:

顺便说......请记住,加入要经过的每一行,所以你应该从小月底开始您的选择,您的方式工作,如果你能帮助它。在这种情况下,查询已通过10万条记录运行,并加入每一个,然后再加入这些100000,又一次,又一次,即使值是零,但它仍然要经过这些。

希望这一切都有助于...

0

我不喜欢categories.id该索引使用,因为你选择了整个表。

尝试运行:

ANALYZE TABLE categories; 
ANALYZE TABLE article_events; 

,并重新运行该查询。