我对SQL Server如何使用公用表表达式和ROW_NUMBER实现更少的读取和性能改进感到困惑。为什么表达式中实现的表不必执行普通查询必须执行的所有读取以允许查询使用ROW_NUMBER进行排序?
答案 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.
因此,简而言之,分页查询只知道何时停止。