我正在尝试破解一个名为 Phabricator / Phorge 的 FLOSS 应用程序
让我们看一下这个简单的 MySQL 表,它按 ID 及其状态( 、 等)存储一些open
问题:closed
invalid
CREATE TABLE `ponder_question` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`status` varchar(32) NOT NULL,
PRIMARY KEY (`id`),
KEY `status` (`status`),
Run Code Online (Sandbox Code Playgroud)
我想先按特定状态排序,然后再按其他状态排序。所以:
SELECT * FROM ponder_question
ORDER BY status='open' DESC
LIMIT 5
Run Code Online (Sandbox Code Playgroud)
它有效,但请考虑这一点DESCRIBE
。该查询显然正在检查 5000 行,这可能太多了/它正在进行全表扫描:
ID | 选择类型 | 桌子 | 分区 | 类型 | 可能的键 | 钥匙 | 密钥长度 | 参考 | 行 | 过滤的 | 额外的 |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | 简单的 | 思考问题 | 无效的 | 指数 | 无效的 | 地位 | 130 | 无效的 | 5000 | 100.00 | 使用索引;使用文件排序 |
(相反,如果你EXPLAIN
是简单的,ORDER BY status DESC LIMIT 5
它只检查 5 行)
为了减少检查的行数并避免Using filesort
我还尝试了FORCE INDEX
:
SELECT * FROM ponder_question
FORCE INDEX (status)
ORDER BY status='open' DESC
LIMIT 5
Run Code Online (Sandbox Code Playgroud)
我在 MySQL 5.7 和 MariaDB 10.3 中进行了测试。无论如何,我的问题不是特定于版本的。
我想我需要避免这种方法。
我可能需要更改架构以拥有更简单的排序子句,但也许不需要。
说实话,最终的目标是ORDER BY status='open' DESC, id
,所以通过创建展示所有开放的问题,然后通过创建展示所有封闭的问题。
我认为这种方法很糟糕,但我只是想善待这个应用程序,而不提出架构更改。所以请随意告诉我我的问题很愚蠢:
问题:如何以更有效的方式首先按值实现订单?
一些相关的类似方法显然没有描述这个问题:
“使用索引”意味着它使用的索引是“覆盖”的。这意味着所需的所有列(状态和 id)都在一个索引中。索引已声明INDEX(status)
,但隐式PRIMARY KEY
附加了。
几乎任何函数,并且在许多情况下任何“运算符”(例如“=”)都会导致ORDER BY
不可控制。因此它不能使用索引在给定的之后停止LIMIT
。但是,由于“覆盖”,它确实使用了索引的 BTree,而不是数据的 BTree。
对于小表(例如 1K 行以下),我不会担心性能。
如果表足够大(例如超过 1M 行),那么有一种更复杂的方法可以有效地完成您所要求的操作:
( SELECT 1 AS seq,
*
FROM ponder_question
WHERE status >= 'open'
LIMIT 5
) UNION ALL
( SELECT 2 AS seq,
*
FROM ponder_question
WHERE status < 'open'
LIMIT 5
) ORDER BY seq
LIMIT 5 -- Yet again
Run Code Online (Sandbox Code Playgroud)
每个都SELECTs
将使用INDEX(status)
,但在 5 行后停止。
UNION
最多有 10 行,然后对其进行简单排序。
归档时间: |
|
查看次数: |
369 次 |
最近记录: |