我使用SQL Server 2005中的row_number over()功能进行了一次昂贵的查询。在查询分页时,我只返回这些记录的子列表。但是,我还想返回记录总数,而不仅仅是分页子集。有效地运行查询两次以获得计数是不可能的。
选择计数(*)也是不可能的,因为当我尝试这个时,表现非常糟糕。
我真正喜欢的是@@ ROW_NUMBERROWCOUNT: - )
答案 0 :(得分:36)
与OVER(PARTITION BY ..)一起使用时检查COUNT(*)聚合,如下所示:
SELECT
ROW_NUMBER() OVER(ORDER BY object_id, column_id) as RowNum
, COUNT(*) OVER(PARTITION BY 1) as TotalRows
, *
FROM master.sys.columns
这是恕我直言,这是最好的方式,而无需进行两次查询。
答案 1 :(得分:36)
多年来,一堆开发人员的汗水已经有效地分页结果集。然而,没有一个答案 - 这取决于你的用例。部分用例是有效地获取页面,部分是确定完整结果集中有多少行。很抱歉,如果我稍微偏离分页,但两者在我的脑海里紧紧相连。
有很多策略,如果您有任何类型的数据量,大多数都是不好的。不适合用例。虽然这不是一个完整的清单,但以下是一些选项......
Count(*)
WHERE/JOIN
条件,则WHERE/JOIN
两次是非常昂贵的。ROW_Number() OVER()
和COUNT(1) OVER(PARTITION By 1)
COUNT(*) OVER(PARTITION BY 1) as TotalRows
导致大约12,000次读取。将其与简单的SELECT COUNT(*) FROM Media
- 12个读数进行比较。 Wowzers。 更新 - 我提到的读取问题有点红鲱鱼。事实证明,对于窗口函数,用于测量读取的单位是混合的。最终结果似乎是大量的读取。您可以在此处查看有关此问题的更多信息:Why are logical reads for windowed aggregate functions so high?
Row_Number()
)开始,1到N的行数为(N + 1) - 1
。链接中有更多解释。 ROW_Number() OVER(Order by ID)
和ROW_Number() OVER(Order by ID DESC)
然后将两个数字相加并减去1. 读我的好人,读。以下是我倾斜的一些文章......
希望有所帮助。
答案 2 :(得分:4)
如果count(*)很慢,您首先需要仔细检查索引并确保统计信息是最新的,从而解决该问题。
根据我的经验,没有比做两个单独的查询更好的了,一个用于获取数据页,另一个用于获取总计数。随着行数的增加,使用临时表来获取总计数是一种失败的策略。例如,将一万亿行插入临时表只是为了计算它们的成本显然会过高。
答案 3 :(得分:0)
我这样做是将整个结果集与row_number一起放入临时表中,然后使用@@ rowcount并使用该查询返回我需要的数据页面。
答案 4 :(得分:0)
在 SQL2016 中,您有 session_context - 分页和最大行数变得快速而简单。 因此,我自己的发明用于具有数百万行的表。:-
create function dbo.x (
@tr int
)
RETURNS int
AS
BEGIN
declare @TotR INT
if @tr = -1 begin
select @TotR = cast(session_context(N'TotRows') as int)
end
else begin
EXEC sp_set_session_context N'TotRows', @tr
select @TotR = @tr
end
return @TotR
end
go
DECLARE
@PageSize INT = 10,
@PageNum INT = 1,
@TotalRows INT;
EXEC sp_set_session_context N'TotRows', 0
;WITH Data_CTE
AS
(
SELECT [name], object_id
FROM sys.all_objects
--where name ='x1'
),
Count_CTE
AS
(
Select dbo.x((SELECT COUNT(*) AS TotalRows FROM Data_CTE)) x1
)
SELECT Data_CTE.*
FROM Data_CTE
cross join Count_CTE
where Count_CTE.x1>0
ORDER BY [name]
OFFSET (@PageNum - 1) * @PageSize ROWS
FETCH NEXT @PageSize ROWS ONLY;
select dbo.x(-1)