我可以在存储过程中有一个可选的 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 参数吗?的主要内容,如果未能解决你的问题,请参考以下文章

s-s-rS 中的可选多值参数

带有WHERE子句逻辑的SQL Server存储过程

Spring Data JPA:如何使用可选的过滤器参数?

django - 可选 url 参数的正则表达式

Oracle存储过程的语法分析

可选的SSRS参数