当 n 可能为 0 时,将 SET ROWCOUNT n 转换为 TOP (n)

Convert SET ROWCOUNT n to TOP (n) when n could be 0

我们目前正在使用 SQL server 2016。根据 Microsoft 页面 Deprecated Database Engine Features in SQL Server 2016SET ROWCOUNT 已弃用。所以我们试图将所有 SET ROWCOUNT N 语句转换为 TOP(N) 语句。看起来很容易,但是我们遇到了一个情况,N是存储过程中的参数,默认为0。

所以在旧代码中,它类似于 SET ROWCOUNT @NumRows。如果 @NumRows 为 0,则表示关闭 SET ROWCOUNT 选项,因此以下查询将 return 所有行。但是,如果我们将其转换为 TOP(@NumRows),则意味着它将 return 0 行。为了避免这个问题,我们可以添加额外的 IF 条件,如果 @NumRows 为 0,则将 @NumRows 设置为一个巨大的数字。或者我们可以添加额外的 IF 条件,如果 @NumRows 为 0,那么我们使用 SELECT 而不使用 TOP,否则我们照常使用 SELECT TOP(N)

但是这些解决方案中的任何一个都会在存储过程中添加额外的代码,所以我的问题是:考虑到 N 可能为 0,是否有一种优雅的方法将 SET ROWCOUNT N 转换为 TOP (N)

更新:添加了存储过程模板

-- Default to 0, in this case, SET ROWCOUNT 0 will return all result
-- But if we change it to TOP(@rows), it returns no result.
CREATE PROCEDURE Proc1  
  @rows int = 0  
AS  
SET ROWCOUNT @rows
SELECT Col1 FROM table1
ORDER BY Col2
SET ROWCOUNT 0

-- In this case, the default is not 0
-- But the program that calls this stored procedure could pass in value 0. 
-- We also don't want to change default value for this stored procedure. 
-- So I think in this case, we may have to add IF @rows = 0, SET @rows = huge_number
CREATE PROCEDURE Proc2
  @rows int = 10
AS
SET ROWCOUNT @rows
SELECT Col3 FROM table2
ORDER BY Col4
SET ROWCOUNT 0

您可以在 TOP 子句中使用 subquery/expression 而不是特定值:

DECLARE @param INT = 0;

SELECT TOP (SELECT IIF(@param=0,2000000, @param)) * FROM sys.objects;
SELECT TOP (IIF(@param=0,2000000, @param)) * FROM sys.objects;

Rextester Demo

注意可能的性能影响。此外 TOP 没有显式 ORDER BY 可能 return 不同的结果集。

听起来你的过程定义是这样的。如果您发布定义,在这里提供帮助会容易得多。

create procedure MyProc
(
    @NumRows int = 0
)
as 
    if @NumRows > 0
        set rowcount @NumRows
    else
        set rowcount 0

    select Columns
    from Table

假设定义如上,您可以将其更改为类似的内容。

create procedure MyProc
(
    @NumRows int = 2147483648 --max value for an int. This could be a bigint also if you have LOTS of data.
)
as 
    select top(@NumRows) Columns
    from Table

请注意,在使用 TOP 时,您需要指定一个顺序,否则您无法知道将返回哪些行。

--编辑--

感谢您提供代码示例。看来我的猜测非常接近。您不必在此处使用 IF 语句,您可以直接在查询中更轻松地完成此操作。这类似于lad2025发布的答案。

select top(isnull(nullif(@NumRows, 0), 2147483647)) Columns
from Table