在这种情况下,CTE中需要SELECT TOP(微观优化,我知道......)。
DECLARE @pageSize INT, @currentPage INT, @top INT
SET @pageSize = 10
SET @currentPage = 150
SET @top = @pageSize * @currentPage + @pageSize
WITH t AS
(
SELECT TOP(***@top***) ID, name
ROW_NUMBER() OVER (ORDER BY ID) AS _row,
FROM dbo.User
)
SELECT TOP(@pageSize) *
FROM t
WHERE t._row > (@top-@pageSize)
ORDER BY t.ID
以上以具有行号列的特定顺序从起始编号(@ top- @ pageSize)返回10(@pageSize)行。 CTE声明是否知道" SELECT TOP" CTE和WHERE子句之外,也是CTE之外的,因此CTE永远不会按特定顺序返回比所需更多的行?
基本上只是谈论ROW_NUMBER函数,它不计算未返回的行的行号(如果我有数百万行...),如果我要选择CTE中的前100名,还是会为所选表格中的所有百万行计算row_number吗?
我曾尝试使用和不使用" SELECT TOP(@top)"在CTE语句中,在一个运行10.000次的循环中,没有看到时间使用的任何差异。虽然,目前我的表中只有38.000行。
编辑: 结果如下:
WITH t AS
(
**DO A TOP() WITH AN ORDER BY IN THE CTE**
SELECT TOP(@top) ID, name
ROW_NUMBER() OVER (ORDER BY ID) AS _row,
FROM dbo.User
ORDER BY ID
)
SELECT TOP(@pageSize) *
**SELECTING TOP N FROM THE CTE, WHERE ROW-NUMBER IS ... DUE TO THE CTE IS IN ORDER ALREADY**
FROM t
WHERE t._row > (@top-@pageSize)
如果我对它们进行排序"倒退",选择" bottom @ pageSize"这可能会更有效率。 CTE,这将省略where子句...但如果它实际上更快,那将需要一些测试......
答案 0 :(得分:2)
不建议在没有top
的情况下使用order by
。无法保证您将获得所需的行,因此不应包含top
。或者,您应该包含order by id
,如果这是您想要的顺序。
top
的用户不会影响row_number()
计算,因为该计算将在应用top
之前完成。您可以想象在那里有另一个窗口函数,例如sum() over ()
,以便了解top
通常不能在row_number()
之前应用,并且找到安全的环境将会很困难。< / p>
答案 1 :(得分:1)
如果您在ID
上有支持索引,则无需阅读和枚举整个表格。 SQL Server必须读取表格,包括您想要的页面。因此,如果您想要第1页(第11行到第20行),则查询将只获取20行。即使你不在CTE中使用顶部也是如此。
要测试一些数据的表格:
create table dbo.[User]
(
ID int identity primary key,
Name nvarchar(128)
)
go
insert into dbo.[User](Name)
select top(1000) Name
from sys.all_objects
没有冗余顶部表达式的查询。
DECLARE @pageSize INT, @currentPage INT, @top INT;
SET @pageSize = 10;
SET @currentPage = 1;
SET @top = @pageSize * @currentPage + @pageSize;
with C as
(
select U.ID,
U.Name,
row_number() over(order by U.ID) as rn
from dbo.[User] as U
)
select C.ID,
C.Name
from C
where C.rn > @pageSize * @currentPage and
C.rn <= @pageSize * (@currentPage + 1);
这将为您提供如下查询计划:
每个运算符的数字是实际获取的行数。聚集索引扫描读取ID
排序的20行。 Segment和Sequence Project枚举行。 Top是运算符,用于确保提取的行数不超过20行。过滤器删除行1到10并让行11到20通过。
如果我们尝试获取第5页(@currentPage = 5
以获取第51至60行),则该计划将如下所示:
Top运算符确保从聚簇索引中只读取60行,并且筛选器筛选出前50行以返回最后10行。
使用带有额外顶部表达式的上一个查询不会添加任何有价值的内容。只有一个额外的冗余顶级操作符。
了解查询计划中发生的事情的关键是要知道执行是从左到右完成的,一次要求一行。这就是顶级操作符在返回足够的行时停止聚簇索引扫描的方式。