1

假设我有下表(我们称之为my_table):

CREATE TABLE `my_table` (
  `table_id` int(10) unsigned NOT NULL auto_increment,
  `my_field` int(10) unsigned NOT NULL default '0'
   PRIMARY KEY  (`table_id`),
   KEY `my_field` (`my_field`,`table_id`)
 ) ENGINE=MyISAM

的主键my_tabletable_id(auto_increment),我也有一个带有my_fieldand的键table_id

如果我测试这个查询......

EXPLAIN SELECT * FROM my_table
WHERE my_field = 28
ORDER BY table_id DESC;

...我得到:

id select_type table type possible_keys key key_len ref rows Extra
--- ----------- -------- ---- ------------- -------- --- ---- ----- ---- -----
1 简单的 my_table 参考 my_field my_field 8 const 36

您可以看到它使用了正确的密钥 ( my_field)。

但如果我试试这个...

EXPLAIN SELECT * FROM my_table
WHERE my_field IN (1, 28, 20)
ORDER BY table_id DESC;

...我得到:

id select_type table type possible_keys key key_len ref rows Extra
--- ----------- -------- ---- ------------- ------ ----- -- ------ ---- ---------------
1 SIMPLE my_table ALL my_field (NULL) (NULL) (NULL) 406 使用 where;使用文件排序

您可以看到它根本没有使用任何键,更糟糕的是,使用了filesort

即使我做“ FORCE INDEX (my_field)”,它仍然会进行文件排序。

有什么办法可以避免文件排序?

4

4 回答 4

14

据我了解,MySQL 不能使用索引对该查询进行排序。

MySQL 只能使用索引,如果它恰好与您的查询以相同的方式排序。假设您的记录(table_id,my_field)

(1,1), (2,28), (3,14), (4,20)

上的索引(my_field,table_id)将像这样存储

(1,1), (14,3), (20,4), (28,2)

从您的 IN 示例执行查询时(为简单起见,我们会说您的 ORDER BY 是 ASCending),MySQL 会发现

(1,1), (20,4), (28,2)

……按这个顺序。无论如何,它都必须将它们分类为(1,1),(28,2),(20,4). 这就是文件排序。ORDER BY my_field这就是为什么 MySQL 只能在查询是或者ORDER BY my_field, table_id因为索引已经按此顺序时才使用该索引的原因。这也是为什么如果您混合 ASC 和 DESC ,它[当前,某些将来的版本可能允许您以混合顺序对复合索引进行排序]使用索引。该索引按 ASC、ASC 排序,无论您以哪种方式阅读它,它都不会按正确的顺序排列。

请注意,"filesort" 没有任何问题,它是正常执行查询的一部分。它实际上也不使用文件,应该非常快。

如果您必须对数千行进行排序,则可以通过使用小型派生表获得更好的结果,特别是如果每​​一行都非常大(很多字段、BLOB 等...)

  SELECT t.*
    FROM (
          SELECT table_id FROM my_table WHERE my_field IN (1, 28, 20)
         ) tmp
    JOIN my_table t USING (table_id)
ORDER BY t.table_id DESC

您将用文件排序换取派生表。在某些情况下,它的性能可能会更高,而在其他情况下,性能会稍差一些。YMMV

于 2009-09-17T20:29:55.597 回答
1

首先,我认为你的键在你的 SQL 中是倒退的。你不想主键是table_id吗?

这可能是你想要的:

CREATE TABLE `my_table` (
  `table_id` int(10) unsigned NOT NULL auto_increment,
  `my_field` int(10) unsigned NOT NULL default '0'
   PRIMARY KEY  (`table_id`),
   INDEX `my_index` (`my_field`)
 ) ENGINE=MyISAM
于 2009-09-17T19:32:02.607 回答
1

您的查询有一个错误的“=”符号。像这样删除它:

EXPLAIN SELECT * FROM my_table
WHERE my_field IN (1, 28, 20)
ORDER BY table_id DESC;
于 2009-09-17T19:42:50.137 回答
1

它不会使用密钥my_field,因为它是基于table_id和的密钥my_field。如果您正在执行基于my_field单独的查询,则必须有一个my_field专门的索引。

在多个列上拥有一个键并不意味着如果您按任何列搜索,就会使用该键。仅当查询中的所有搜索列也在该键中时才使用特定键。

于 2009-09-17T19:43:41.280 回答