我可以在存储过程中有一个可选的 OUTPUT 参数吗?
Posted
技术标签:
【中文标题】我可以在存储过程中有一个可选的 OUTPUT 参数吗?【英文标题】:Can I have an optional OUTPUT parameter in a stored procedure? 【发布时间】:2011-03-17 02:26:15 【问题描述】:我有一个存储过程,它有一堆输入和输出参数,因为它正在向多个表中插入值。在某些情况下,存储过程仅插入到单个表中(取决于输入参数)。这是一个模拟场景来说明。
表格/数据对象:
人物
Id
Name
Address
姓名
Id
FirstName
LastName
地址
Id
Country
City
假设我有一个插入一个人的存储过程。如果地址不存在,我不会将其添加到数据库中的Address
表中。
因此,当我生成调用存储过程的代码时,我不想麻烦添加Address
参数。对于INPUT
参数,这是可以的,因为 SQL Server 允许我提供默认值。但是对于OUTPUT
参数,我应该在存储过程中做什么以使其成为可选参数,这样我就不会收到错误...
过程或函数“Person_InsertPerson”需要参数 '@AddressId',未提供。
【问题讨论】:
你的代码是什么样的?也就是说,您正在某个地址是否存在分支。我怀疑我的问题是,一旦看到那个分支,“当地址不存在时,为什么不用NULL
在传递给@AddressId
的任何内容中调用存储过程,不使用一个分支?”
【参考方案1】:
输出参数和默认值不能很好地协同工作!这来自 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 开始的目的。
恕我直言,这个功能组合是一个可疑的构造,我会认为是 代码气味(呸!!)
【讨论】:
使用OUTPUT
参数作为输入的优点是什么?为什么不将这些输入值作为“常规”参数传递?
@ruffin 没有优势;可选的 OUTPUT 参数并不像每个人期望的那样工作,这就是我建立这个答案的原因。
+1。我想我只是补充一点,这意味着通过 OUTPUT
参数传递值 c/sh/ 应该被认为是一个坏主意,原因有两个:1. 意识形态/代码气味,以及 2. 你的行为的实际原因在这里找到。 ¯\_(ツ)_/¯
因此,故事的寓意是,如果您要使用可选的 OUTPUT 参数,请始终将默认值设置为 NULL(并且不要闻到气味)。
嗯,顺便说一句,可选输入参数也是如此。输出参数并没有什么特别之处。如果您使用 NULL 值显式传递参数,则该值不会被参数的默认值覆盖。我不明白为什么这是一些意想不到的陷阱,这是记录在案的预期行为。【参考方案2】:
由于您执行的是存储过程而不是 SQL 语句,因此您必须将 SQL 命令的命令类型设置为存储过程:
cmd.CommandType = CommandType.StoredProcedure;
取自here。
此外,一旦消除了该错误,您可以在过程中使用 SQL 的 nvl()
函数来指定遇到 NULL 值时要显示的内容。
很抱歉没有正确解决这个问题......一定误解了你。这是一个 nvl 的例子,我认为它可能会更好地解决它?
select NVL(supplier_city, 'n/a')
from suppliers;
如果supplier_city
字段包含空值,上述SQL 语句将返回“n/a”。否则,它将返回 supplier_city
值。
【讨论】:
这不能回答问题。我正在使用 Enterprise Library 的 db.GetStoredProcCommand ,它将为我做到这一点。我以这种方式调用了许多存储过程,它们工作得很好。例如。使用 (DbCommand insertPersonCommand = db.GetStoredProcCommand("Person_InsertPerson")) this.AppendInsertPersonParameters(db, insertPersonCommand); 调用 StoredProc 的代码db.ExecuteNonQuery(insertPersonCommand, dbTransaction);【参考方案3】:看起来我可以为OUTPUT
参数添加一个默认值,例如:
@AddressId int = -1 Output
似乎它的可读性很差,因为AddressId
被严格用作OUTPUT
变量。但它有效。如果您有更好的解决方案,请告诉我。
【讨论】:
这有缺陷,请参阅我的完整治疗答案。【参考方案4】:补充菲利普所说的:
我的 sql server 数据库中有一个如下所示的存储过程:
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)
【讨论】:
【参考方案5】:输入和输出参数都可以指定默认值。在这个例子中:
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
【讨论】:
谢谢!我想知道为什么我不能使用@var INT OUTPUT = NULL
——语法很混乱。
并没有真正调用默认值(您设置@output)默认值仅在您不传递某些内容时才激活,请参阅我的完整处理答案。以上是关于我可以在存储过程中有一个可选的 OUTPUT 参数吗?的主要内容,如果未能解决你的问题,请参考以下文章