内部联接不会使用索引

时间:2011-10-31 02:47:43

标签: mysql

为什么此查询(以及许多类似的变体)不在'tags'表中使用ASIN的索引?它坚持进行全表扫描,即使A只包含几行。由于生产中的“标签”表包含近一百万个条目,因此它会严重破坏查询。

SELECT C.tag, count(C.tag) AS total
FROM
(
    SELECT B.*
    FROM
    (
        SELECT ASIN FROM requests WHERE user_id=9
    ) A
    INNER JOIN tags B USING(ASIN)
) C
GROUP BY C.tag ORDER BY total DESC 

EXPLAIN显示没有使用索引(在测试数据库上运行,因此'tags'中的行很低,但仍然是全表扫描):

|  1 | PRIMARY     | <derived2>         | system | NULL          | NULL        | NULL    | NULL |    0 | const row not found            |
|  2 | DERIVED     | <derived3>         | ALL    | NULL          | NULL        | NULL    | NULL |   28 |                                |
|  2 | DERIVED     | B                  | ALL    | NULL          | NULL        | NULL    | NULL | 2593 | Using where; Using join buffer |
|  3 | DERIVED     | borrowing_requests | ref    | idx_user_id   | idx_user_id | 5       |      |   27 | Using where        

索引:

| book_tags |          1 | asin           |            1 | ASIN        | A         |         432 |     NULL | NULL   |      | BTREE      |         |
| book_tags |          1 | idx_tag        |            1 | tag         | A         |        1296 |     NULL | NULL   |      | BTREE      |         |
| book_tags |          1 | idx_updated_on |            1 | updated_on  | A         |         518 |     NULL | NULL   |      | BTREE

查询是从具有相同问题的INNER JOIN重写的:

SELECT tag, count(tag) AS total 
FROM tags 
INNER JOIN requests ON requests.ASIN=tags.ASIN 
WHERE user_id=9 
GROUP BY tag 
ORDER BY total DESC

说明:

|  1 | SIMPLE      | tags          | ALL  | NULL                 | NULL     | NULL    | NULL | 2593 | Using temporary; Using filesort |
|  1 | SIMPLE      | requests | ref  | idx_ASIN,idx_user_id | idx_ASIN | 33      | func |    3 | Using where 

我明白这是一个我缺少的真正基本点,但大约4个小时的工作让我无处可去。欢迎任何建议。

编辑:

我可以看到使用子查询的第一个查询由于某些回复而不会使用索引,但是这被使用,因为它的运行速度是只有INNER JOIN的底部查询的两倍。

例如,请求中有70k行(全部带有索引的ASIN),标签中有700k行,标签中有95k个不同的ASIN,每个都有少于10个不同的标记记录。

如果用户有10个请求,我只希望列出并计算来自这10个ASIN的标签。在我看来,这应该使用tags.idx_ASIN,并且应该从tags表中查找最多100行(10个ASIN,每个最多包含10个标签)。

我错过了什么......我只是看不出来。

编辑:

请求CREATE TABLE:

CREATE TABLE IF NOT EXISTS `requests` (
  `bid` int(40) NOT NULL AUTO_INCREMENT,
  `user_id` int(20) DEFAULT NULL,
  `ASIN` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL,
  `status` enum('active','inactive','pending','deleted','completed') COLLATE     utf8_unicode_ci NOT NULL,
  `added_on` datetime NOT NULL,
  `status_changed_on` datetime NOT NULL,
  `last_emailed` datetime DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY (`bid`),
  KEY `idx_ASIN` (`ASIN`),
  KEY `idx_status` (`status`),
  KEY `idx_added_on` (`added_on`),
  KEY `idx_user_id` (`user_id`),
  KEY `idx_status_changed_on` (`status_changed_on`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=149380 ;

标签CREATE TABLE

CREATE TABLE IF NOT EXISTS `tags` (
  `ASIN` varchar(10) NOT NULL,
  `tag` varchar(50) NOT NULL,
  `updated_on` datetime NOT NULL,
  KEY `idx_tag` (`tag`),
  KEY `idx_updated_on` (`updated_on`),
  KEY `idx_asin` (`ASIN`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

标签上没有主键。我通常没有没有主键的表,但没有看到这个需要。这可能是一个问题吗?

AHA!不同的字符集和校对。我会更正并再试一次!

随后:

得到了它。查询从10secs下降到0.006secs。感谢大家让我以不同的方式看待它。

3 个答案:

答案 0 :(得分:1)

MySQL不会索引子查询。如果希望索引提高查询性能,请将其重写为不使用子查询。

答案 1 :(得分:0)

尝试颠倒原始查询中表格的顺序:

SELECT tag, count(tag) AS total 
FROM requests 
INNER JOIN tags ON requests.ASIN=tags.ASIN 
WHERE user_id=9 
GROUP BY tag 
ORDER BY total DESC

答案 2 :(得分:0)

AHA!不同的字符集和校对。我会更正并再试一次!

随后:

得到了它。查询从10secs下降到0.006secs。感谢大家让我以不同的方式看待它。