存储过程中可以有一个可选的OUTPUT参数吗?


76

我有一个具有一堆输入和输出参数的存储过程,因为它正在向多个表中插入值。在某些情况下,存储的proc仅插入到一个表中(取决于输入参数)。这是一个模拟的场景来说明。

表/数据对象:

Id
Name
Address

名称

Id
FirstName
LastName

地址

Id
Country
City

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

因此,当我生成代码以调用存储过程时,我不想添加任何Address参数。对于INPUT参数,这是可以的,因为SQL Server允许我提供默认值。但是对于OUTPUT参数我该在存储过程中做什么使其使其可选,所以我不会收到错误...

过程或函数“ Person_InsertPerson”需要未提供的参数“ @AddressId”。


您的代码是什么样的?也就是说,您正在某个分支上确定地址是否存在。我很怀疑我的问题,一旦看到那个分支,便会是:“为什么不使用地址而在不存在地址的情况下,NULL传递给您的任何东西调用sproc ?” @AddressId
ruffin

Answers:


119

输入和输出参数均可分配默认值。在此示例中:

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中使用不同的值和设置来弄乱它和下面的测试调用例程,以查看它们如何协同工作。

DECLARE @Output int

SET @Output = 3

EXECUTE MyTest
  @Data1 = 1
 ,@Data2 = 2
 ,@Data3 = @Output output

PRINT '---------'
PRINT @Output

2
谢谢!我想知道为什么我不能使用@var INT OUTPUT = NULL-语法令人困惑。
trojjer '16

2
并不会真正调用默认值(您设置为@output),默认值仅在您不传递任何内容时才激活,请参见我的回答以获取完整说明。
escape-llc

13

输出参数和默认值不能很好地配合!这来自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

如果您输入的未初始化值(即NULL),则OUTPUT您确实NULL进入了SP,而不是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值没有采取任何显式操作。

添加SET了的OUTPUT参数在SP(像你应该做的),但是没有设置从呼叫者什么:

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

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

现在,对于图扭曲:使默认值“激活”的唯一方法是根本不传递OUTPUT参数,恕我直言,这毫无意义:由于将其设置为OUTPUT参数,因此意味着返回“重要”的东西应该收集的。

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

给出以下输出:

yay its zero
@QR=NULL

但这无法捕获SP的输出,大概是该SP最初的目的。

恕我直言,此功能组合是可疑的构造,我会考虑代码气味(phe!)


使用简洁的参数(包括在代码中重复使用可能会调用此代码和其他sproc的代码)比使用简洁的方法有什么优势OUTPUT?为什么不将那些输入值作为“常规”参数传递?
ruffin

2
@ruffin没有优势;可选的OUTPUT参数不能按每个人的预期工作,这就是为什么我建立了这个答案。
escape-llc

+1。我想我只是补充说,这意味着通过OUTPUT参数c / sh /传入值将被认为是一个坏主意,原因有两个:1.意识形态/代码味道,以及2.从您在这里发现的行为的实际原因。¯\ _(ツ)_ /¯
鲁芬

因此,就故事而言,如果您要使用可选的OUTPUT参数,请始终将默认值设置为NULL(并避免闻到气味)。
鲍达德

实际上,道德上不要同时使用这两个“功能” ...鼻孔保持是可选的;-)
escape-llc

4

看起来我可以只向OUTPUT参数添加默认值,例如:

@AddressId int = -1 Output

由于AddressId严格地将其用作OUTPUT变量,因此在可读性方面似乎很差。但这有效。如果您有更好的解决方案,请告诉我。


这有陷阱,请参阅我的答案以进行全面治疗。
escape-llc

1

补充菲利普所说的话:

我在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)

1

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

cmd.CommandType = CommandType.StoredProcedure;

取自这里

同样,一旦清除了该错误,便可以nvl()在过程中使用SQL的函数来指定遇到NULL值时要显示的内容。

很抱歉无法正确解决问题...一定误会了您。这是nvl的示例,我认为它可能会更好一些?

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

如果该supplier_city字段包含空值,则上述SQL语句将返回“ n / a” 。否则,它将返回该supplier_city值。


那没有回答问题。我正在使用企业库的db.GetStoredProcCommand,它将为我执行此操作。我已经以这种方式调用了许多存储过程,它们工作得很好。例如。使用(DbCommand insertPersonCommand = db.GetStoredProcCommand(“ Person_InsertPerson”))来调用StoredProc的代码{this.AppendInsertPersonParameters(db,insertPersonCommand); db.ExecuteNonQuery(insertPersonCommand,dbTransaction);
贾斯汀
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.