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

问题描述:

我们当前正在使用SQL server 2016.基于微软页面Deprecated Database Engine Features in SQL Server 2016SET ROWCOUNT已被弃用。所以我们试图将所有SET ROWCOUNT N报表转换为TOP(N)报表。看起来很容易,但我们遇到了一个情况,其中N是存储过程中的一个参数,默认为0.当n可能为0时,将SET ROWCOUNT n转换为TOP(n)

因此在旧代码中,它类似于SET ROWCOUNT @NumRows。如果@NumRows为0,则表示关闭SET ROWCOUNT选项,因此以下查询将返回所有行。但是如果我们将其转换为TOP(@NumRows),那意味着它将返回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 
+0

优雅?但是,在插入到你的'top'子句之前,你可以说'选择@NumRows = iif(@NumRows = 0,2147483648,@NumRows)'。这就是说,这可能会给你很糟糕的查询计划。除非您有数千个程序需要查找/替换,否则您可能需要考虑指定两个不同代码路径的额外工作。你的旅费可能会改变。 – Xedni

+0

我会警告不要使用多个执行路径。它可能会导致非常有趣的性能问题。 Gail Shaw在这里有一个很棒的博客文章。 http://www.sqlinthewild.co.za/index.php/2009/09/15/multiple-execution-paths/ –

+0

@SeanLange感谢您的链接。我通读了它并理解不同执行计划的可能性。所以如果我不使用不同的执行路径,那意味着我总是使用'TOP(N)'。然后我需要检查N值是否为0。如果它是0,我可以从表中'NumRows' = 2147483647或'NumRows' = count(*),这对查询计划有什么影响? – Dongminator

这听起来像你的程序定义是这样的。如果您发布了定义,那么在这里可以更容易地获得帮助。

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时,您需要指定一个顺序或您无法知道将返回哪些行。

- EDIT--

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

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

感谢您将默认设置为2147483648的建议。我还编辑了我的原始问题,以包括存储过程的外观。我认为如果当前默认值为0,我们可以将其更改为2147483648.但是,如果默认值不是0,那么我们应该这样做:'if @NumRows = 0,set @NumRows = 2147483648'?或者只是在所有默认情况下使用这个IF语句? – Dongminator

+0

查看我的编辑,比在这里使用IF语句更简单。 –

你可以使用子查询/表达TOP条款,而不是特定的值:

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可能会返回不同的结果集。

+0

感谢您的回答!你能否详细说明可能的性能影响?如果您可以分享几个链接,我也可以阅读这些链接。 – Dongminator