假设我有一个像这样的非常简单的表:
CREATE TABLE `t1` (
`key_part1` INT UNSIGNED NOT NULL,
`key_part2` INT UNSIGNED NOT NULL,
`value` TEXT NOT NULL,
PRIMARY KEY (`key_part1`, `key_part2`)
) ENGINE=InnoDB
使用这个表,我想发出这样的查询:
SELECT *
FROM `t1`
ORDER BY `key_part1` ASC, `key_part2` DESC
LIMIT 1
我曾希望此查询中的 ORDER BY
会被索引满足。然而,根据MySQL documentation :
In some cases, MySQL cannot use indexes to resolve the
ORDER BY
, although it still uses indexes to find the rows that match theWHERE
clause. These cases include the following:
- You mix
ASC
andDESC
:
SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 ASC;
我尝试了一个类似于上述查询的查询,正如预期的那样,EXPLAIN
输出表明这样的查询进行了文件排序。这对我来说完全没有意义,因为我可以执行以下操作:
SELECT *
FROM `t1`
WHERE `key_part1` = (
SELECT `key_part1`
FROM `t1`
ORDER BY `key_part1` ASC
LIMIT 1
)
ORDER BY `key_part2` DESC
LIMIT 1
当我EXPLAIN
时,它说子查询和外部查询都不使用文件排序。此外,我尝试了这种具有类似结构的大表技巧,发现它使我的查询速度提高了 3 个数量级。
我的问题是
- 我在此处显示的两个查询是否等效?他们看起来像他们,但我可能会遗漏一些东西。如果不是,我的表中需要什么样的数据才能使它们给出不同的结果?
- MySQL 不能自己执行此优化技巧是否有原因,或者这只是一个可能的优化案例,但尚未写入 MySQL?
如果重要的话,我使用的是 MySQL 5.6.22。
进一步说明:
“等效”是指“产生相同的结果”。此外,我非常清楚,如果我将 LIMIT 1
更改为 LIMIT 2
或其他内容,查询将不再产生相同的结果。 我对那些情况不感兴趣,只对 LIMIT 1
的情况感兴趣。
最佳答案
这并不是说 MySQL 缺少优化“技巧”,这是复合索引工作方式的一个特性。 MySQL 一次只能在一个方向上进行索引扫描,并且必须遵循索引的排序方式(因此它可以进行二进制搜索等计算机科学方面的工作)。
让我们看看您的示例查询:
选择 *
从 t1
其中 key_part1
= (
选择 key_part1
从 t1
按 key_part1
升序排序
限制 1
)
按 key_part2
降序排序
限制 1
这可以在 key_part2 上排序,因为所有返回的行都将具有相同的 key_part1。所以基本上mysql可以忽略索引的那部分;它在功能上等同于 ORDER BY key_part1 DESC, key_part2 DESC
。子查询中 ORDER BY
的方向无关紧要,因为它在子查询中。
编辑
需要明确的是,您的示例查询实际上看起来像这样:
选择 *
从 t1
WHERE key_part1
= #{some value}
按 key_part2
降序排序
限制 1
其中 #{some value}
是子选择的结果。现在应该清楚为什么这种排序不需要文件排序,因为您根本没有按 key_part1
排序。事实上,没有必要,因为所有返回的行都将具有相同的 key_part1
。
关于mysql - 如果 ASC 和 DESC 混合使用,为什么 MySQL 不能为 ORDER BY 使用索引?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30495388/