mysql - 避免对分页嵌套集进行全表扫描

标签 mysql sql nested-sets

我有一个nested set像这样设置:

Node (Id, ParentId, LeftBounds, RightBounds, Level, Name)

LeftBounds 上有一个索引。

但是当我尝试选择分页结果时,

SELECT * FROM Node ORDER BY LeftBounds ASC LIMIT 500000, 1000

Sql 进行全表扫描。我还应该注意其他事项以避免全表扫描吗?

这通常不会是一个大问题,但对于包含数百万行的表,加载最后一页大约需要 3-5 秒。

最佳答案

您的LIMIT 5000000, 1000子句要求MySQL对结果集中的结果进行排序,跳过其中的50万个,然后显示1000。MySQL似乎已经决定最好这样做通过表扫描。这并不奇怪。

您可以尝试延迟连接操作。这样做的目的是减少需要排序的结果集的大小。它的工作原理如下。

SELECT Node.*
  FROM Node
  JOIN (
         SELECT id
           FROM Node
          ORDER BY LeftBounds ASC
          LIMIT 500000, 1000
       ) Subset ON Node.id = Subset.id
  ORDER BY Node.LeftBounds ASC

如您所见,这将您需要处理的大结果集限制为更少的列,特别是 idLeftBounds。然后,它使用找到的 1000 个不同 id 值来检索完整记录。

如果您在(LeftBounds, id)上为自己创建一个复合索引,您很可能会大大加快此查询的速度。但它仍然必须跳过 50 万行,因此您的 EXPLAIN 可能会说您正在执行完整索引扫描。

为了加快查询速度,您可以对此查询做的下一件事是摆脱 SELECT *,而是命名您需要的列。为什么这有帮助?因为它给出了复合覆盖索引的提示,可能有助于完全满足查询。您已经提到 LeftBounds 是唯一的,因此是 JOIN 标准的候选者。那么,让我们通过一个例子来探讨这一点。假设您希望结果集中包含 ParentId、LeftBounds、RightBounds、Level、Name。然后你可以使用这个查询:

SELECT Node.ParentId, Node.LeftBounds, 
       Node.RightBounds, Node.Level, Node.Name
  FROM Node
  JOIN (
         SELECT LeftBounds
           FROM Node
          ORDER BY LeftBounds ASC
          LIMIT 500000, 1000
       ) Subset ON Node.LeftBounds = Subset.LeftBounds
  ORDER BY Node.LeftBounds ASC

如果你在你需要的列上有索引,MySQL可以满足从索引的查询。该索引应按此顺序合并这些列。

LeftBounds, ParentId, RightBounds, Level, Name

LeftBounds 需要位于索引中的第一个,因为这是您用于随机访问索引的列。这里的要点是不必使用 id 列来访问表。

关于mysql - 避免对分页嵌套集进行全表扫描,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28026909/

相关文章:

mysql - PDO 获取多个插入 ID

ruby-on-rails - 如何显示关联记录子集的表单,其中一些尚不存在?

php - 使用 PDO 在一个表中查找记录并将数据追加到另一个表中

mysql - 多个 where 子句,仅读取一个

mysql - select * from table where something 与 select column from table where something

java - 当前正在运行的存储过程

PHP 编辑数据库条目不更新

mysql - 获取MySQL中给定类别的子类别下的项目数

php - 嵌套集模型是否适用于具有多个类别(多对多)的文章?

mysql - MySQL 中查找换行符和回车符 (\r\n)