19

我有一个程序来返回一个受页码和其他东西限制的结果集。作为 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

然后我需要将 OUTPUT 参数设置为内部查询中的行数。我可以只复制查询并对其进行计数,但是此查询可能会返回数千行(并且将来会更多),因此我正在寻找具有良好性能的方法。我在考虑表变量,这是个好主意吗?或者有什么其他建议?

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

谢谢你,简

4

5 回答 5

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

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

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

您会发现仅对分页结果使用临时表不会使用太多内存(当然取决于您的页面大小),并且您只能将其保持很短的时间。从临时表中选择完整的结果集并选择 TotalRows 只会花费一点时间。

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

于 2009-02-24T15:46:34.070 回答
3

我认为您应该在单独的查询中执行此操作。虽然这两个查询可能看起来几乎相同,但查询优化器处理它们的方式会有很大不同。

从理论上讲,SQL Server 甚至可能不会遍历子查询中的所有行来计算它。

于 2009-02-24T11:38:15.900 回答
2

我现在无法访问我的代码库,但我相信您可以使用 COUNT() OVER(或类似命令)将总行数作为子查询的一部分返回。然后,您可以将其作为最终结果集的一部分返回。它在每一行中都会重复,但在我看来,对于使用分页并且最终结果应该有限的应用程序来说,这是一个较小的性能损失。

几个小时后,我将发布确切的代码。

编辑:这是我用来生成计数的行。最后,我们的开发人员想要一个单独的方法来自己获取计数,所以现在我在同一个存储过程中的两个位置维护搜索条件。

COUNT(*) OVER (PARTITION BY '') AS TotalCount

将其添加到您的 CTE,然后您可以选择 TotalCount,它将成为您每一行中的一列。

于 2009-02-24T12:03:21.907 回答
2

您必须在不限制范围的情况下运行整个查询至少一次以获得完整的行数。由于无论如何您都将这样做,因此您应该选择@@RowCount 以输出找到的总行数,而不是在每行中使用冗余的 count(*) 列重载数据读取器。

1.第一次运行NEW查询时:

select YOUR_COLUMNS 
from YOUR_TABLE 
where YOUR_SEARCH_CONDITION 
order by YOUR_COLUMN_ORDERING_LIST;
select @@rowcount;

2. 只读取前 X 行

上面的查询避免了用冗余的 COUNT(*) 列淹没 SqlDataReader,否则每次调用 SqlDataReader.Read() 时都会发送该列。 由于您是第一次运行查询...而不是选择范围,只需读取前 X 行。 这正是您想要的……完整的结果计数、前 X 条记录以及没有冗余计数列的结果集的高效流式传输。

3. 对于 SAME 查询的后续运行以获取结果的子集

select YOUR_COLUMNS 
from (select YOUR_COLUMNS, ROW_NUMBER() 
over(order by BY YOUR_COLUMN_ORDERING_LIST) as RowNum) Results 
where Results.RowNum between @From and @To;

在任何情况下,@@rowcount这是在不限制结果集的情况下在第一次运行查询时访问计数的最直接方法(无论如何你都想要第 X 个结果),不运行单独的 count() 查询,不使用临时表,并且不包括冗余的 count() 列。

于 2010-05-17T19:25:23.030 回答
1

您不能将输出变量设置为@@RowCount 吗?这将获得受最后执行语句影响的行:

SELECT stuff FROM mytable

SET @output = @@ROWCOUNT

这应该为您提供所需的内容,并且不涉及再次运行查询。

于 2009-02-25T03:13:55.370 回答