SQL Server 中的分页

AV9*_*V94 17 sql-server paging

我有一个非常大的数据库,大约 100 GB。我正在执行查询:

select * from <table_name>;
Run Code Online (Sandbox Code Playgroud)

我只想显示第 100 到 200 行。

我想了解这是如何在内部发生的。数据库是否将所有记录从磁盘提取到内存中并将第 100 行到第 400 行发送回查询客户端?或者是否存在任何机制,以便仅从数据库中获取那些记录(第 100 个 -200 个) - 通过使用 B 树等索引机制?

我发现这与分页概念有关,但我无法确切地找到它在数据库级别内部是如何发生的。

Bre*_*zar 37

在您发布的查询中:

select * from <table_name>;
Run Code Online (Sandbox Code Playgroud)

没有第 100-200 行这样的东西,因为您没有指定 ORDER BY。除非出于很多有趣的原因包含 ORDER BY,否则无法保证订单,但这并不是真正的重点。

因此,为了说明您的观点,让我们使用一个表 - 我将使用Stack Overflow 数据转储中的 Users 表,并运行以下查询:

SELECT * FROM dbo.Users ORDER BY DisplayName;
Run Code Online (Sandbox Code Playgroud)

默认情况下,DisplayName 字段上没有索引,因此 SQL Server 必须扫描整个表,然后按 DisplayName 对其进行排序。这是执行计划

带排序的聚集索引扫描

这并不漂亮 - 这是很多工作,估计子树成本约为 30k。(您可以通过将鼠标悬停在 PasteThePlan 上的选择运算符上来查看它。)那么如果我们只想要第 100-200 行会发生什么?我们可以在 SQL Server 2012+ 中使用这个语法:

SELECT * FROM dbo.Users ORDER BY DisplayName OFFSET 100 ROWS FETCH NEXT 100 ROWS ONLY;
Run Code Online (Sandbox Code Playgroud)

执行计划也很丑陋:

带有排序和顶部的聚集索引扫描

SQL Server 仍在扫描整个表以构建排序列表,只是为了给您 100-200 行,而成本仍然在 30k 左右。更糟糕的是,每次运行查询时都会重新构建整个列表(因为毕竟有人可能更改了他们的 DisplayName。)

为了让它更快,我们可以在 DisplayName 上创建一个非聚集索引,它是我们表的副本,按特定字段排序:

CREATE INDEX IX_DisplayName ON dbo.Users(DisplayName);
Run Code Online (Sandbox Code Playgroud)

使用该索引,我们查询的执行计划现在进行索引查找:

索引查找和键查找

查询立即完成,估计子树成本仅为 0.66(而不是 30k)。

总之,如果您以支持您经常运行的查询的方式组织数据,那么是的,SQL Server 可以采取快捷方式使您的查询更快。另一方面,如果您拥有的只是堆或聚集索引,那么您就完蛋了。


Mar*_*ith 16

正如在使用非覆盖索引以避免排序时布伦特的答案的补充一样,后面的页码存在潜在问题,可以从运行以下内容中看出

SELECT * 
FROM dbo.Users 
ORDER BY DisplayName 
OFFSET 100000 ROWS 
FETCH NEXT 100 ROWS ONLY;
Run Code Online (Sandbox Code Playgroud)

执行计划显示查找执行了 100,100 次,尽管 TOP 运算符随后过滤掉了除 100 行之外的所有行。

在此处输入图片说明

这可以通过使用下面的模式来缓解

WITH T
     AS (SELECT Id,
                DisplayName
         FROM   dbo.Users
         ORDER  BY DisplayName
        OFFSET 100000 ROWS 
        FETCH NEXT 100 ROWS ONLY
        )
SELECT U.*
FROM   dbo.Users U
       JOIN T
         ON U.Id = T.Id
ORDER  BY T.DisplayName 
Run Code Online (Sandbox Code Playgroud)

这会在进行查找之前过滤掉除最后 100 行之外的所有行这会对大偏移值的速度产生重大影响。

在此处输入图片说明