我可以在存储过程中使用可选的OUTPUT参数吗?

我有一个存储过程有一堆input和输出参数,因为它是插入值到多个表。 在某些情况下,存储的proc只能插入到一个表(取决于input参数)。 这是一个嘲弄的场景来说明。

表/数据对象:

Id Name Address 

名称

 Id FirstName LastName 

地址

 Id Country City 

说我有一个插入一个人的存储过程。 如果地址不存在,我不会将其添加到数据库中的Address表中。

因此,当我生成的代码来调用存储过程,我不想打扰添加Address参数。 对于INPUT参数,这是可以的,因为SQL Server允许我提供默认值。 但是对于OUTPUT参数我该怎么做在存储过程,使其可选,所以我没有收到错误…

过程或函数'Person_InsertPerson'需要参数'@AddressId',它没有提供。

input和输出参数都可以分配为默认值。 在这个例子中:

 CREATE PROCEDURE MyTest @Data1 int ,@Data2 int = 0 ,@Data3 int = null output AS PRINT @Data1 PRINT @Data2 PRINT isnull(@Data3, -1) SET @Data3 = @Data3 + 1 RETURN 0 

第一个参数是必需的,第二个和第三个参数是可选的 – 如果没有通过调用例程设置,它们将被分配默认值。 尝试使用不同的值和设置来解决SSMS中的以下testing调用例程,以了解它们如何一起工作。

 DECLARE @Output int SET @Output = 3 EXECUTE MyTest @Data1 = 1 ,@Data2 = 2 ,@Data3 = @Output output PRINT '---------' PRINT @Output 

输出参数和默认值不能很好地一起工作! 这是从SQL 10.50.1617(2008 R2)。 不要被愚蠢地相信这个构造神奇地为你的代表(就像我的同事所做的那样)为你创造一个SET值!

这个“玩具”SP询问OUTPUT参数值,无论是默认值还是NULL

 CREATE PROCEDURE [dbo].[omgwtf] (@Qty INT, @QtyRetrieved INT = 0 OUTPUT) AS IF @QtyRetrieved = 0 BEGIN print 'yay its zero' END IF @QtyRetrieved is null BEGIN print 'wtf its NULL' END RETURN 

如果你为OUTPUT发送一个未初始化的值(即NULL ),你真的在​​SP内部得到NULL ,而不是0 。 有道理,这个参数通过了一些东西。

 declare @QR int exec [dbo].[omgwtf] 1, @QR output print '@QR=' + coalesce(convert(varchar, @QR),'NULL') 

输出是:

 wtf its NULL @QR=NULL 

如果我们从调用者添加一个显式的SET ,我们得到:

 declare @QR int set @QR = 999 exec [dbo].[omgwtf] 1, @QR output print '@QR=' + coalesce(convert(varchar, @QR),'NULL') 

和(不令人惊讶的)输出:

 @QR=999 

同样,有道理的是,一个参数被传递,并且SP没有采取明确的行动来SET一个值。

在SP中添加一个OUTPUT参数的SET (就像你应该做的那样),但是不要设置来自调用者的任何东西:

 ALTER PROCEDURE [dbo].[omgwtf] (@Qty INT, @QtyRetrieved INT = 0 OUTPUT) AS IF @QtyRetrieved = 0 BEGIN print 'yay its zero' END IF @QtyRetrieved is null BEGIN print 'wtf its NULL' END SET @QtyRetrieved = @Qty RETURN 

现在执行时:

 declare @QR int exec [dbo].[omgwtf] 1234, @QR output print '@QR=' + coalesce(convert(varchar, @QR),'NULL') 

输出是:

 wtf its NULL @QR=1234 

这是SP中OUTPUT参数处理的“标准”行为。

现在的情节扭曲 :获得默认值“激活”的唯一方法是根本不传递OUTPUT参数 ,恕我直言,没有什么意义:因为它被设置为OUTPUT参数,这意味着返回的东西“重要的“应该收集。

 declare @QR int exec [dbo].[omgwtf] 1 print '@QR=' + coalesce(convert(varchar, @QR),'NULL') 

给出这个输出:

 yay its zero @QR=NULL 

但是这不能捕获SP的输出,大概是SP开始的目的。

恕我直言,这个function组合是一个可疑的构造,我会考虑一个代码气味 (唷!!)

看起来像我可以添加一个默认值的OUTPUT参数,如:

 @AddressId int = -1 Output 

由于AddressId严格意义上是一个OUTPUTvariables,因此在可读性方面似乎很差。 但它的作品。 请让我知道,如果你有一个更好的解决scheme。

加上菲利普所说的话:

我在我的sql服务器数据库中有一个存储过程,如下所示:

 dbo.<storedProcedure> (@current_user char(8) = NULL, @current_phase char(3) OUTPUT) 

我从我的.net代码调用它如下:

  DataTable dt = SqlClient.ExecuteDataTable(<connectionString>, <storedProcedure>); 

我得到一个System.Data.SqlClient.SqlException:过程或函数期望参数“@current_phase”,这是没有提供。

我也在我的程序中的其他地方使用这个函数,传入一个参数并处理输出。 所以我不必修改当前的调用,我只是改变了存储过程,使输出参数也是可选的。

所以现在看起来如下所示:

 dbo.<storedProcedure> (@current_user char(8) = NULL, @current_phase char(3) = NULL OUTPUT) 

由于您正在执行存储过程而不是SQL语句,因此必须将SQL命令的命令types设置为存储过程:

 cmd.CommandType = CommandType.StoredProcedure; 

从这里采取。

另外,一旦你清除了这个错误,你可以在你的过程中使用SQL的nvl()函数来指定当遇到NULL值时你想要显示的内容。

对不起,没有妥善处理这个问题…一定是误解了你。 这里有一个nvl的例子,我认为可能会更好一点呢?

 select NVL(supplier_city, 'n/a') from suppliers; 

如果supplier_city字段包含空值,则上面的SQL语句将返回'n / a'。 否则,它将返回supplier_city值。