选择子查询的COUNT(*)而不运行它两次

19 sql t-sql sql-server paging count

我有一个程序来返回一个受页码和其他东西限制的结果集.作为OUTPUT参数,我需要根据除页码之外的参数返回所选行的总量.所以我有类似的东西:

WITH SelectedItems AS
(SELECT Id, Row1, Row2, ROW_NUMBER() OVER (ORDER BY Row1) AS Position
FROM Items
WHERE Row2 = @Row2)
SELECT Id, Row1, Row2
FROM SelectedItems
WHERE Position BETWEEN @From AND @To
Run Code Online (Sandbox Code Playgroud)

然后我需要将OUTPUT参数设置为内部查询中的行数.我可以复制查询并对其进行计数,但是这个查询可能会返回数千行(并且将来会更多),所以我正在寻找方法来实现这一点并获得良好的性能.我在考虑表变量,这是个好主意吗?还是其他任何建议?

更具体地说,它是Microsoft SQL Server 2008.

谢谢你,Jan

bad*_*d99 19

您可以使用COUNT(*)将总行数计入主查询中的单独列.像这样:

WITH SelectedItems AS
(SELECT Id, Row1, Row2, ROW_NUMBER() OVER (ORDER BY Row1) AS Position, 
COUNT(*) OVER () AS TotalRows
FROM Items
WHERE Row2 = @Row2)
SELECT Id, Row1, Row2
FROM SelectedItems
WHERE Position BETWEEN @From AND @To
Run Code Online (Sandbox Code Playgroud)

这将返回结果集中的计数而不是输出参数,但这应该符合您的要求.否则,与临时表结合使用:

DECLARE @tmp TABLE (Id int, RowNum int, TotalRows int);

WITH SelectedItems AS
(SELECT Id, Row1, Row2, ROW_NUMBER() OVER (ORDER BY Row1) AS Position, 
COUNT(*) OVER () AS TotalRows
FROM Items
WHERE Row2 = @Row2)
INSERT @tmp
SELECT Id, Row1, Row2
FROM SelectedItems
WHERE Position BETWEEN @From AND @To

SELECT TOP 1 @TotalRows = TotalRows FROM @tmp
SELECT * FROM @tmp
Run Code Online (Sandbox Code Playgroud)

您会发现使用临时表只是为了您的分页结果不会占用太多内存(当然,这取决于您的页面大小),并且您只能在短时间内保持活动状态.从临时表中选择完整的结果集并选择TotalRows只需要更长的时间.

这将比运行完全独立的查询快得多,在我的测试中(重复WITH)将执行时间加倍.

  • 它在性能方面没有区别,SQL内部将count(*)更改为count(1).在MySql中也是如此,它将count(*)转换为count(0).虽然count(1)更快输入! (2认同)