在查询中使用GROUP BY时帮助MySQL表索引

时间:2011-01-10 16:38:46

标签: mysql indexing query-optimization

感谢您的关注。

有两个INNODB表:

作者

id        INT
nickname  VARCHAR(50) 
status    ENUM('active', 'blocked')
about     TEXT

图书

author_id  INT
title      VARCHAR(150)

我正在对这些表运行查询,以获取每位作者和他拥有的书籍数量:

SELECT a. * , COUNT( b.id ) AS book_count
FROM authors AS a, books AS b
WHERE a.status != 'blocked'
AND b.author_id = a.id
GROUP BY a.id
ORDER BY a.nickname

此查询非常慢(执行大约需要6秒)。我在books.author_id上有一个索引,它运行正常,但我不知道如何在authors表上创建索引,以便此查询可以使用它。

以下是当前EXPLAIN的外观:

id   select_type   table    type    possible_keys               key            key_len   ref     rows    Extra
1    SIMPLE        a        ALL     PRIMARY,id_status_nickname  NULL           NULL      NULL    3305    Using where; Using temporary; Using filesort
1    SIMPLE        b        ref     key_author_id               key_author_id  5         a.id    2       Using where; Using index

我查看了MySQL manual on optimizing queries with group by,但无法弄清楚如何将其应用于我的查询。

我会感谢任何关于此的帮助和提示 - 索引结构必须是什么,以便MySQL可以使用它?

修改

我试过了:

(id, status, nickname)
(status, nickname)

两者都导致了同样的情况。

2 个答案:

答案 0 :(得分:3)

我假设id_status_nickname是一个复合索引(id,status,nickname)。在您的查询中,您通过说a.status!= blocked来过滤行。这有以下问题:

  1. 您没有可用于此目的的索引。 (id,status,nickname)无法使用,因为status不是该索引的前缀
  2. 假设您有状态索引,使用时不能使用!=。你必须将其更改为status ='active'
  3. 此外,状态是只有两个值的枚举字段,基数将很低。所以mysql最终可能不会使用索引。
  4. 你可以试试这个:创建索引为(status,id,nickname)并使用status ='active'。我的猜测是,因为你使用'='并且status是索引的前缀,所以它应该选择这个索引,然后将它用于group by然后按顺序排序。希望这有帮助。

    更新: 当WHERE子句没有ORDER BY中使用的字段时,看起来无法避免使用filesort。

答案 1 :(得分:0)

我会尝试索引(状态,昵称)。这应该摆脱“使用filesort”的必要性。