从 OFFSET / FETCH NEXT 获取总行数

问题描述 投票:0回答:4

因此,我有一个函数可以返回一些我想在我的网站上实现分页的记录。 有人建议我使用 SQL Server 2012 中的 Offset/Fetch Next 来完成此操作。 在我们的网站上,我们有一个区域列出了记录总数以及您当时所在的页面。

之前,我获取了整个记录集,并能够以编程方式在此基础上构建分页。 但是使用仅 FETCH NEXT X ROWS 的 SQL 方式,我只返回 X 行,所以我不知道我的总记录集是什么以及如何计算我的最小和最大页数。 我能告诉您执行此操作的唯一方法是调用该函数两次并在第一次上执行行计数,然后使用 FETCH NEXT 运行第二次。 有没有更好的方法,不会让我运行查询两次? 我正在努力提高性能,而不是降低性能。

performance pagination sql-server-2012
4个回答
171
投票

我在使用 COUNT() OVER() 方法时遇到了一些性能问题。 (我不确定是否是服务器的问题,因为它花了 40 秒返回 10 条记录,然后没有任何问题。)这种技术在所有条件下都有效,无需使用 COUNT() OVER() 并完成同样的事情:

DECLARE 
    @PageSize INT = 10, 
    @PageNum  INT = 1;

WITH TempResult AS(
    SELECT ID, Name
    FROM Table
), TempCount AS (
    SELECT COUNT(*) AS MaxRows FROM TempResult
)
SELECT *
FROM TempResult, TempCount
ORDER BY TempResult.Name
    OFFSET (@PageNum-1)*@PageSize ROWS
    FETCH NEXT @PageSize ROWS ONLY

143
投票

您可以使用

COUNT(*) OVER()
...这是一个使用
sys.all_objects
的快速示例:

DECLARE 
  @PageSize INT = 10, 
  @PageNum  INT = 1;

SELECT 
  name, object_id, 
  overall_count = COUNT(*) OVER()
FROM sys.all_objects
ORDER BY name
  OFFSET (@PageNum-1)*@PageSize ROWS
  FETCH NEXT @PageSize ROWS ONLY;

但是,这应该保留用于小数据集;在较大的集合上,性能可能会很糟糕。 请参阅这篇 Paul White 文章以获得更好的替代方案,包括维护索引视图(仅在结果未经过滤或您提前知道

WHERE
子句时才有效)和使用
ROW_NUMBER()
技巧。


4
投票

显然,根据查询的不同,结果可能会有很大差异。我用以下结果测试了我的案例:(8 个连接,2 个子查询,5800 行不同结果,5900 行非不同):

    使用
  • COUNT(1) OVER()
     
    ~0.820 秒(Aaron Bertrand 的答案,但结果错误*)
  • ~0.850 秒,使用
    #TEMP
    表格。
  • ~1.590 秒
    WITH .. AS
    James Moberg 的回答
  • ~1.600秒运行两次(第一次没有订购,只是为了计数)

*就我而言,Aaron Bertrand's的答案不起作用,因为

COUNT(1) OVER()
似乎包含由
DISTINCT
过滤掉的行。

使用临时表:

DECLARE 
  @PageSize INT = 10, 
  @PageNum  INT = 1;
 
SELECT
  name, object_id
INTO #MY_TEMP
FROM sys.all_objects

SELECT *
FROM #MY_TEMP
ORDER BY name
  OFFSET (@PageNum-1)*@PageSize ROWS
  FETCH NEXT @PageSize ROWS ONLY;

SELECT COUNT(1) FROM #MY_TEMP
-- or
-- SELECT @MY_OUTPUT_PARAM = COUNT(1) FROM #MY_TEMP

DROP TABLE #MY_TEMP

临时表的好处是可以将计数分成不同的结果或输出参数。


1
投票

基于 James Moberg 的回答

这是使用

Row_Number()
的替代方案,如果您没有 SQL Server 2012 并且无法使用 OFFSET

DECLARE 
    @PageNumEnd INT = 10, 
    @PageNum  INT = 1;

WITH TempResult AS(
    SELECT ID, NAME
    FROM Tabla
), TempCount AS (
    SELECT COUNT(*) AS MaxRows FROM TempResult
)

select * 
from
(
    SELECT
     ROW_NUMBER() OVER ( ORDER BY PolizaId DESC) AS 'NumeroRenglon', 
     MaxRows, 
     ID,
     Name
    FROM TempResult, TempCount

)resultados
WHERE   NumeroRenglon >= @PageNum
    AND NumeroRenglon <= @PageNumEnd
ORDER BY NumeroRenglon
© www.soinside.com 2019 - 2024. All rights reserved.