使用索引的“ORDER BY column=value”(无全表扫描)

Val*_*ozz 4 mysql order-by

我正在尝试破解一个名为 Phabricator / Phorge 的 FLOSS 应用程序

让我们看一下这个简单的 MySQL 表,它按 ID 及其状态( 、 等)存储一些open问题:closedinvalid

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,所以通过创建展示所有开放的问题,然后通过创建展示所有封闭的问题。

我认为这种方法很糟糕,但我只是想善待这个应用程序,而不提出架构更改。所以请随意告诉我我的问题很愚蠢:

问题:如何以更有效的方式首先按值实现订单?

一些相关的类似方法显然没有描述这个问题:

Ric*_*mes 5

“使用索引”意味着它使用的索引是“覆盖”的。这意味着所需的所有列(状态和 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 行,然后对其进行简单排序。