从OFFSET / FETCH NEXT获取总行数

时间:2012-09-10 13:11:51

标签: performance paging sql-server-2012

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

之前,我正在获取整个记录集,并能够以编程方式构建分页。但是只使用带有FETCH NEXT X ROWS的SQL方法,我只返回X行,所以我不知道我的总记录集是什么以及如何计算我的最小和最大页面。我能告诉你这样做的唯一方法是调用函数两次并在第一次执行行计数,然后使用FETCH NEXT运行第二行。有没有更好的方法不让我运行查询两次?我试图加快性能,而不是减慢速度。

4 个答案:

答案 0 :(得分:120)

我使用COUNT()OVER()方法遇到了一些性能问题。 (我不确定它是否是服务器,因为它返回10条记录需要40秒,后来没有任何问题。)这种技术在所有条件下都能正常运行,而不必使用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

答案 1 :(得分:94)

您可以使用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;

答案 2 :(得分:2)

显然,结果可能因查询而异。我用以下结果测试了我的案例:(8 个连接,2 个子查询,不同结果中的 5800 行,5900 非不同):

  • ~0.820 秒,使用 COUNT(1) OVER()Aaron Bertrand's answer,但结果错误*)
  • ~0.850 秒,使用 #TEMP 表。
  • ~1.590 秒 WITH .. AS (James Moberg's anser)
  • ~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

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

答案 3 :(得分:1)

基于James Moberg's answer

这是使用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