在 SQL Server 中如何使用 ROW_NUMBER 进行分页?

LCJ*_*LCJ 13 performance sql-server query-performance

我有一张Employee有 100 万条记录的表。我有以下 SQL 用于在 Web 应用程序中分页数据。它工作正常。但是,我认为的一个问题是 - 派生表tblEmployee选择表中的所有记录Employee(以创建 MyRowNumber值)。

我认为,这会导致选择Employee表中的所有记录。

真的那么有效吗?或者 SQL Server 是否也被优化为只从原始Employee表中选择 5 条记录?

DECLARE @Index INT;
DECLARE @PageSize INT;

SET @Index = 3;
SET @PageSize = 5;

SELECT *  FROM
  (SELECT  ROW_NUMBER() OVER (ORDER BY EmpID asc) as MyRowNumber,*
  FROM Employee) tblEmployee
WHERE MyRowNumber BETWEEN ( ((@Index - 1) * @PageSize )+ 1) AND @Index*@PageSize 
Run Code Online (Sandbox Code Playgroud)

Aar*_*and 19

测试的替代方法可能是:

;WITH x AS (SELECT EmpID, k = ROW_NUMBER() OVER (ORDER BY EmpID) FROM dbo.Emp)
SELECT e.columns
FROM x INNER JOIN dbo.Emp AS e
ON x.EmpID = e.EmpID
WHERE x.k BETWEEN (((@Index - 1) * @PageSize) + 1) AND @Index * @PageSize
ORDER BY ...;
Run Code Online (Sandbox Code Playgroud)

是的,您敲了两次表,但是在扫描整个表的 CTE 中,您只获取了密钥,而不是所有数据。但你真的应该看看这篇文章:

http://www.sqlservercentral.com/articles/T-SQL/66030/

以及后续讨论:

http://www.sqlservercentral.com/Forums/Topic672980-329-1.aspx

在 SQL Server 2012 中,你当然可以使用新的OFFSET/FETCH NEXT语法:

;WITH x AS 
(
  SELECT EmpID FROM dbo.Emp
    ORDER BY EmpID
    OFFSET  @PageSize * (@Index - 1) ROWS
    FETCH NEXT @PageSize ROWS ONLY
)
SELECT e.columns
FROM x INNER JOIN dbo.Emp AS e
ON x.EmpID = e.EmpID
ORDER BY ...; 
Run Code Online (Sandbox Code Playgroud)

我还在博客上更详细地介绍了这一点:

  • @Akash 你有没有彻底测试过这个?我观察到了一些计划上的差异,但没有具体提到性能方面的任何内容,因为我没有做过任何广泛的测试。即使性能相同,语法也稍微不那么麻烦。我在这里写了关于它的博客:https://sqlblog.org/2010/11/10/sql-server-v-next-denali-using-the-offset-clause-paging 和这里:https://sqlperformance.com /2015/01/t-sql-queries/pagination-with-offset-fetch (3认同)