SQL Server 输出参数问题

Posted

技术标签:

【中文标题】SQL Server 输出参数问题【英文标题】:SQL Server output parameter issue 【发布时间】:2009-08-08 07:25:09 【问题描述】:

我使用的是 SQL Server 2008 Enterprise。我正在学习 SQL Server 存储过程的 OUTPUT 参数。例如,存储过程 sp_add_jobschedule 有一个名为 schedule_id 的 OUTPUT 参数。

http://msdn.microsoft.com/en-us/library/ms366342.aspx

我的困惑是,看起来 OUTPUT 参数可以提供一个输入值并返回一个值,看起来它具有 INPUT 和 OUTPUT 参数的行为?是否允许不为 OUTPUT 参数提供任何 INPUT 值(使其看起来像纯输出参数行为)?

【问题讨论】:

【参考方案1】:

这种混淆在一定程度上是有道理的——像 Oracle 这样的其他 RDBMS 确实有存储过程参数,这些参数可以是 IN(仅输入)、OUT(仅输出)或 INOUT(两种方式 - “按引用传递”类型的参数)。

SQL Server 在这里有点草率,因为它将参数标记为OUTPUT,但实际上,这意味着INPUT/OUTPUT - 它基本上只是意味着存储过程有机会从其返回一个值调用那个参数。

所以是的 - 即使它被称为 OUTPUT 参数,它实际上更像是一个 INPUT/OUTPUT 参数,而那些 ININOUTOUT 像 Oracle 中的那些在 SQL 中不存在服务器(在 T-SQL 中)。

【讨论】:

感谢 Marc,“这种混淆在一定程度上是有道理的——像 Oracle 这样的其他 RDBMS 确实有存储过程参数”——这就是我感到困惑的原因。我正在从 DB2 迁移到 SQL Server。很多概念都不一样。 :-( 还有一个问题,我们可以将OUTPUT参数作为可选参数,即我们不能提供它的输入值,而只是将其用作输出? 是的,当然 - 您可以提供 INPUT 值或将其留空 - 如果您不在存储过程中使用它,那么您传入的内容完全没有意义。这取决于您和代码无论您是否查看传入的值,您都可以在过程中写入。“OUTPUT”子句仅表示存储过程能够在此参数中返回值 - 仅此而已。 但是当我在 SSIS 中使用它时,OUT 就出来了,我真的希望 OUT 对 in 和 out 都适用。 @IrawanSoetomo 是的,我也刚刚意识到:SSIS 中的 OUTPUT 声明的参数的行为不像 SQL OUTPUT 参数(也不是 Oracle INOUT):参数传递到 T-SQL 为NULL 无论在调用时是什么。【参考方案2】:

我可以给你一个简短的例子,说明如何使用输出参数创建存储过程。

CREATE PROCEDURE test_proc

@intInput int,
@intOutput int OUTPUT

AS
set @intOutput = @intInput + 1 

go

要调用这个过程,然后使用输出参数,请执行以下操作:

declare @intResult int
exec test_proc 3 ,@intResult OUT
select @intResult

你看,你应该首先声明输出变量。执行存储过程后,输出值将在您的变量中。您可以为输出变量设置任何值,但在执行存储过程后,它将包含存储过程返回的确切值(无论输出变量中的值是什么)。

例如:

declare @intResult int
exec test_proc 3 ,@intResult OUT
select @intResult

它将返回 4。并且:

declare @intResult int
set @intResult = 8
exec test_proc 3 ,@intResult OUT
select @intResult

同样返回 4。

【讨论】:

谢谢,所以OUTPUT参数有输入参数和输出参数的行为? @George2:是的,OUTPUT 参数同时作为输入和输出参数。【参考方案3】:

是的,您可以使用 OUTPUT 参数来传入和检索值(尽管目前我想不出这样做的充分理由)。

这里有一个简单的例子来证明这一点:

-- The stored procedure
CREATE PROCEDURE OutParamExample
    @pNum int OUTPUT
AS
BEGIN
    select @pNum
    set @pNum = @pNum + 5
END
GO

-- use a local variable to retrieve your output param value
declare @TheNumber int
set @TheNumber = 10

print @TheNumber
exec OutParamExample @TheNumber OUTPUT
print @TheNumber

结果将如下所示:

10

-----------
10

(1 row(s) affected)

15

编辑:好的,我想我在第二段中遗漏了一个“不”,可能没有回答您提出的问题。如果你想要一个严格的输出参数(例如返回码之类的东西),你当然不必为作为输出参数传递的局部变量提供 value,但你仍然必须声明局部变量,这样您就可以在过程本身的范围之外访问返回的值。

例如:

declare @LocalNumber int
-- I don't have to assign a value to @LocalNumber to pass it as a parameter
exex OutParamExample @LocalNumber OUTPUT  
-- (assume SP has been altered to assign some reasonable value)

-- but I do have to declare it as a local variable so I can get to
-- the return value after the stored procedure has been called
print @LocalNumber

【讨论】:

【参考方案4】:

进一步补充上面其他人所说的,OUTPUT 参数可以充当 INPUT 以及 OUTPUT。但是,它依赖于存储过程来使用传递给它的值。

如果存储过程忽略了为 OUTPUT 参数传入的值(通常应该如此),则无论如何都会忽略 INPUT 值。

使用 Sergey 的代码,我可以使用用户为 @intOutput 传递的值(如果需要)

create PROCEDURE test_proc

@intInput int,
@intOutput int OUTPUT

AS
set @intOutput = @intOutput + 1 

go

但是,这违背了 OUTPUT 参数的目的。 为了给出不同的观点,c# 编译器强制你通过赋值覆盖 out 参数的值(不使用输出参数)。

例如我不能在 c# 中做到这一点。这里它只作为输出参数(即忽略用户传递给这个函数的值)

static void dosomething(out int i)

    //i = 0;
    i = i + 1;

【讨论】:

【参考方案5】:

问题是 - 为什么您要禁止提供输入?这没有任何意义。考虑这个简单的例子:

CREATE PROCEDURE test (@param AS INT OUTPUT) AS
BEGIN
  SET @param = 100
END
GO

DECLARE @i INT
SET @i = 0

EXECUTE test @i OUTPUT
PRINT @i

DROP PROCEDURE test

打印出来

100

看看 - 如果你不把一个变量 in 首先放入,你应该如何获得一个值 out

【讨论】:

谢谢Tomalak,我不确定我是否可以用这种方式设计一个带有输出参数的存储过程。我可以将输出参数作为可选输入参数(因为输出参数允许我们不输入任何值)和返回值? 我不确定这有什么难理解的。 :) 实际问题是什么? 我使用OUTPUT参数作为选项输入参数,不知道使用是否正确?我就是这样使用的,在存储过程中,我会检查调用者是否为OUTPUT参数输入了任何值,如果是,我会做一些事情,如果没有OUTPUT参数的输入值,我会做一些其他事情。 【参考方案6】:

考虑在编程语言中通过引用传递的输出参数,它是相同的。我现在能想到的最好的例子是返回错误代码。你想要一些插入...如果你从 SP 中选择返回代码,你必须在你的代码中取回它,使用你没有的 OUTPUT 参数,它已经在你的参数中(我的意思是使用 C# 命令,php init 存储 proc 方法,或与构造字符串不同的东西)

【讨论】:

感谢 Svetlio,看起来它同时具有 INPUT 和 OUTPUT 参数的行为?是否允许不为 OUTPUT 参数提供任何 INPUT 值(使其看起来像纯输出参数行为)? @George2:“是否允许不为 OUTPUT 参数提供任何 INPUT 值(使其看起来像纯输出参数行为)” - 这不是 Sergey 在代码中显示的内容吗? 1 个? 你是说这个?声明@intResult int exec test_proc 3 ,@intResult OUT 选择@intResult【参考方案7】:

关于原始问题的一部分的附加说明:

“OUTPUT参数(..)是否允许不提供任何INPUT值?”

在 SQLServer 中,可以为 OUTPUT 参数指定默认值(正如其他人指出的那样,实际上是 INOUT)。考虑以下示例,您可以在其中指定显式值或让函数本身生成 ID。

CREATE PROCEDURE test (@id uniqueidentifier = NULL OUTPUT) AS
BEGIN
  IF @id IS NULL SET @id = NEWID()
  -- INSERT INTO xyz (...) VALUES (@id, ...)
  PRINT 'Insert with id: ' + CAST (@id as nvarchar(36))
END
GO

DECLARE @insertedId uniqueidentifier
EXECUTE test '00000000-0000-0000-0000-000000000000'
EXECUTE test @insertedId OUTPUT
PRINT @insertedId

DROP PROCEDURE test

打印出来

Insert with id: 00000000-0000-0000-0000-000000000000
Insert with id: 67AE3D27-8EAB-4301-B384-30EEA1488440
67AE3D27-8EAB-4301-B384-30EEA1488440

【讨论】:

【参考方案8】:

另外两点我认为值得注意:

1) 你可以传递多个参数为OUTPUT

2) 如果您不想要结果,则不必使用OUTPUT 调用参数

CREATE PROCEDURE ManyOutputs @a int, @b int output, @c varchar(100) output, @d bit output
AS
BEGIN
    SET @b = @a + 11
    SET @c = 'The Value of A is ' + CAST(@a AS varchar(5)) + ', and the value of B is ' + CAST(@b AS varchar(5))
    IF (@a % 2 = 1)
        SET @d = 1
    ELSE
        SET @d = 0
END
GO

调用此例程:

DECLARE @bVal int
DECLARE @cVal varchar(100)
DECLARE @dVal bit

EXEC ManyOutputs 1, @bVal, @cVal, @dVal
SELECT @bVal AS bVal, @cVal as cVal, @dVal as dVal

返回 NULL、NULL、NULL

EXEC ManyOutputs 2, @bVal OUT, @cVal OUT, @dVal OUT
SELECT @bVal AS bVal, @cVal as cVal, @dVal as dVal

返回13,“A的值为2,B的值为13”,0

EXEC ManyOutputs 3, @bVal, @cVal, @dVal
SELECT @bVal AS bVal, @cVal as cVal, @dVal as dVal

返回13,“A的值为2,B的值为13”,0

(和上次调用一样,因为我们没有使用OUTPUT获取新值,所以它保留了旧值。)

EXEC ManyOutputs 5, @bVal OUT, @cVal OUT, @dVal OUT
SELECT @bVal AS bVal, @cVal as cVal, @dVal as dVal

返回 16,“A 的值为 5,B 的值为 16”,1

【讨论】:

以上是关于SQL Server 输出参数问题的主要内容,如果未能解决你的问题,请参考以下文章

sql server 中 一个要输入参数和输出参数的存储过程。

什么可能导致 SQL Server JDBC 错误“未为参数号 0 设置值”以访问输出参数?

调用使用 Microsoft SQL Server 的输出参数的 Oracle 存储过程

SQL SERVER 存储过程执行带输出参数的SQL语句拼接

如何在SQL Server存储过程/函数中声明输入输出参数?

sql server 带输入输出参数的分页存储过程(效率最高)