【发布时间】:2015-08-10 07:14:47
【问题描述】:
假设我有一个非常简单的表格,如下所示:
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:
在某些情况下,MySQL 无法使用索引来解析
ORDER BY,尽管它仍然使用索引来查找与WHERE子句匹配的行。这些案例包括:
- 你把
ASC和DESC混在一起:
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是按升序存储索引的,所以你不能一次双向读取。您可以使用的一个技巧是指定第三列,即 key_part2 的否定(即 -1 * key_part2),在其上创建复合索引,并按两个值升序排序。 doc ref here,大约下降了三分之一。
-
key_parts是什么?我问是因为列的语义可以提供替代解决方案。 -
我必须处理的这个问题的具体情况是,
key_part1是一个只有几个值的枚举,key_part2是另一个表的外键。 -
您在第二个查询中有一个 where 子句,而不是第一个。
标签: mysql indexing sql-order-by