2

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

4

1 回答 1

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.

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

于 2013-01-18T09:53:17.577 回答