SQL Server中的服务器端分页

时间:2013-01-17 17:18:27

标签: sql-server-2008 row-number

我对SQL Server如何使用公用表表达式和ROW_NUMBER实现更少的读取和性能改进感到困惑。为什么表达式中实现的表不必执行普通查询必须执行的所有读取以允许查询使用ROW_NUMBER进行排序?

1 个答案:

答案 0 :(得分:4)

CTE不是(必然)“已实现”。并不是它将不可避免地将所有行复制到其他地方并且将在副本上执行其他操作(尽管它可能表现得优化器决定它更好)。

如果我们采用这个简单的查询:

SELECT  *
FROM    (
        SELECT  *,
                ROW_NUMBER() OVER (ORDER BY id) rn
        FROM    mytable
        ) q
WHERE   rn BETWEEN 101 AND 110

看看它的计划,我们会看到类似的东西:

  |--Filter(WHERE:([Expr1003]>=(101) AND [Expr1003]<=(110)))
       |--Top(TOP EXPRESSION:(CASE WHEN (110) IS NULL OR (110)<(0) THEN (0) ELSE (110) END))
            |--Sequence Project(DEFINE:([Expr1003]=row_number))
                 |--Segment
                      |--Clustered Index Scan(OBJECT:([ee].[dbo].[mytable].[PK__mytable__3213E83F29C2D227]), ORDERED FORWARD)

此处,扫描记录(以id顺序,当表格聚集在id上),分配ROW_NUMBER(这是Sequence Project所做的)并通过到达TOP时,在达到某个阈值时暂停执行(在我们的案例中为110个记录)。

这110条记录传递给Filter,只传递rn大于100的记录。

查询本身仅扫描110条记录:

SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 1 ms.

(строк обработано: 10)
Table 'mytable'. Scan count 1, logical reads 3, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

共3页。

现在让我们看看解压缩的查询:

SELECT  *
FROM    mytable
ORDER BY
        id

这个很简单:从表中读取所有内容并吐出来。

  |--Clustered Index Scan(OBJECT:([ee].[dbo].[mytable].[PK__mytable__3213E83F29C2D227]), ORDERED FORWARD)

然而,轻松看并不意味着轻松完成。该表非常大,我们需要做很多读取才能返回所有记录:

SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

(строк обработано: 1310720)
Table 'mytable'. Scan count 1, logical reads 2765, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 266 ms,  elapsed time = 11690 ms.

因此,简而言之,分页查询只知道何时停止。