【问题标题】:SQL Server output parameter issueSQL Server 输出参数问题
【发布时间】: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 值(使其看起来像纯输出参数行为)?

【问题讨论】:

    标签: sql sql-server sql-server-2008 stored-procedures


    【解决方案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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-03-28
            • 2011-11-18
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多