在 SO,最近有人问为什么 ORDER BY 不使用索引?
这种情况涉及 MySQL 中的一个简单的 InnoDB 表,包括三列和 10k 行。其中一列,一个整数,被索引——OP 试图检索他在该列上排序的整个表:
SELECT * FROM person ORDER BY age
Run Code Online (Sandbox Code Playgroud)
他附上了EXPLAIN
输出,显示这个查询是用 a filesort
(而不是索引)解决的,并询问为什么会这样。
尽管提示 FORCE INDEX FOR ORDER BY (age)
导致使用索引,但有人回答(通过其他人的支持评论/赞成)索引仅用于在所选列都从索引中读取时进行排序(即通常Using index
在Extra
列中指示的EXPLAIN
输出)。后来给出了一个解释,遍历索引然后从表中获取列会导致随机 I/O,MySQL 认为这比filesort
.
这似乎与关于ORDER BY
优化的手册章节背道而驰,它不仅传达了强烈的印象,即满足ORDER BY
索引比执行额外排序更可取(实际上,filesort
是快速排序和归并排序的组合,因此 必须有一个下限; 虽然按顺序遍历索引并寻找表应该是 - 所以这是完全有道理的),但它也忽略了这种所谓的“优化”,同时还说明了:Ω(nlog n)
O(n)
以下查询使用索引来解析
ORDER …
在GROUP BY
和HAVING
与隐藏列,MySQL手册文件(强调):
MySQL 扩展了 的使用,
GROUP BY
以便选择列表可以引用GROUP BY
子句中未命名的非聚合列。这意味着前面的查询在 MySQL 中是合法的。您可以使用此功能通过避免不必要的列排序和分组来获得更好的性能。但是,这主要在GROUP BY
每个组中未命名的每个非聚合列中的所有值都相同时很有用。服务器可以自由地从每个组中选择任何值,因此除非它们相同,否则选择的值是不确定的。
尽管开发人员发出了这个明确的警告,但有些人继续依赖未记录的行为来确定将从隐藏列中选择的值。
特别是,MySQL似乎经常从每个组中选择“第一”记录(其中“第一”的概念本身没有记录,例如某些存储引擎上的最旧记录或根据应用于物化表的某种排序顺序从子查询)。我已经看到这个被利用来检索,例如,分组最大值:
SELECT * FROM (
SELECT * FROM my_table ORDER BY sort_col DESC
) t GROUP BY group_col
Run Code Online (Sandbox Code Playgroud)
为了完整起见,可以通过简单的连接以标准和文档化的方式完成相同的操作:
SELECT * FROM my_table NATURAL JOIN (
SELECT group_col, MAX(sort_col) sort_col
FROM my_table
GROUP BY group_col
) t
Run Code Online (Sandbox Code Playgroud)
我相信人们永远不应该依赖无证行为,因为可能存在无法预见的极端情况导致该行为中断。例如,在GROUP BY
用索引满足操作时,MySQL 对结果进行排序,从而可能选择一个意外的值。
还有哪些极端情况可以打破这种行为?或者它对于生产系统是否足够可靠?
假设一个人有一列单词,可以在其上建立BTREE
索引:
CREATE TABLE myTable (
words VARCHAR(25),
INDEX USING BTREE (words)
);
LOAD DATA LOCAL INFILE '/usr/share/dict/words' INTO TABLE myTable (words);
Run Code Online (Sandbox Code Playgroud)
现在人们想要找到与某些搜索查询共享最长公共前缀的记录,例如'foobar'
. 我想这样做:
SELECT DISTINCT words
FROM myTable
WHERE words LIKE CASE
WHEN NOT EXISTS (SELECT * FROM myTable WHERE words LIKE 'f%') THEN '%'
WHEN NOT EXISTS (SELECT * FROM myTable WHERE words LIKE 'fo%') THEN 'f%'
WHEN NOT EXISTS (SELECT * FROM myTable WHERE words LIKE 'foo%') THEN 'fo%'
WHEN NOT EXISTS (SELECT …
Run Code Online (Sandbox Code Playgroud)