在SQL Server中是否有更好的选项来应用分页而不应用偏移量?

我想对包含大量数据的表应用分页。我只想知道一个比在SQL Server中使用偏移量更好的选项。

以下是我的简单查询:

SELECT *
FROM TableName
ORDER BY Id DESC 
OFFSET 30000000 ROWS
FETCH NEXT 20 ROWS ONLY

解决方案

您可以使用Keyset Pagination进行此操作。它比使用行集分页(按行号分页)far more efficient要好。

在行集分页中,必须先读取前面的所有行,然后才能读取下一页。而在键集分页中,服务器可以立即跳到索引中的正确位置,因此不会读取不需要的额外行。

在这种类型的分页中,您不能跳到特定的页码。你跳到一个特定的键上,从那里开始阅读。为了很好地执行此操作,您需要对该键具有唯一索引,该索引包括您需要查询的任何其他列。

除了明显的效率提升外,

的一大好处是避免了分页时由于从以前读取的页面中删除行而导致的";漏行问题。按键分页时不会发生这种情况,因为键不会更改。


下面是一个示例:

假设您有一个名为TableName的表,其索引位于Id,并且您希望从最新的Id值开始并向后工作。

开始时:

SELECT TOP (@numRows)
  *
FROM TableName
ORDER BY Id DESC;

请注意使用ORDER BY以确保顺序正确

客户端将保留最后接收的Id值(在本例中为最低值)。在下一个请求时,您跳到该键并继续:

SELECT TOP (@numRows)
  *
FROM TableName
WHERE Id < @lastId
ORDER BY Id DESC;

注意<Not<=

的用法

如果您想知道,在典型的B-Tree+索引中,具有指示ID的行是而不是读取的,它是之后的被读取的行。


所选的键必须唯一,因此,如果按非唯一列分页,则必须向ORDER BYWHERE添加第二列。例如,您需要OtherColumn, Id上的索引来支持这种类型的查询。不要忘记索引上的INCLUDE列。

SQL Server不支持行/元组比较器,因此您无法执行(OtherColumn, Id) < (@lastOther, @lastId)(但在PostgreSQL、MySQL、MariaDB和SQLite中支持)。

相反,您需要以下各项:

SELECT TOP (@numRows)
  *
FROM TableName
WHERE (
    OtherColumn = @lastOther AND Id < @lastId)
    OR OtherColumn < @lastOther
)
ORDER BY
  OtherColumn DESC,
  Id DESC;

这比看起来更有效,因为SQL Server可以将其转换为两个值上的正确<

<2-13]>的存在使事情变得更加复杂。您可能需要分别查询这些行。

相关文章