我有三个表:categories、articles 和 article_events,结构如下
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描述了每篇文章的当前状态。
我正在返回一个类别表以及其中有多少篇最近事件 status_id 为“1”的文章。
我目前所做的工作有效,但对于我的表的大小来说相当慢(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
基本上我必须两次加入事件表,因为请求 status_id 和 MAX(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
最佳答案
如果您可以使用 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`)
关于MySQL分组查询优化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10539463/