简体   繁体   English

SQL Server 2008存储过程的返回值

[英]SQL Server 2008 stored procedure return value

Here is my stored procedure 这是我的存储过程

ALTER PROCEDURE Delete
     @ID nvarchar(64),
     @value int = 0 output
AS
BEGIN
    IF(EXISTS(SELECT * FROM A where Ap = @ID))
    BEGIN
        set @value = 1
    END
    ELSE IF(EXISTS(SELECT * FROM B where Bp = @ID))
    BEGIN
        set @value = 2
    END
    ELSE
    BEGIN
        select * 
        from Table_D
    END
END

RETURN @value

Problem is that when I execute it, this does not return any value 问题是当我执行它时,它不返回任何值

There are multiple ways of returning status information from a stored procedure to an application. 有多种方法可以将状态信息从存储过程返回到应用程序。 Each has its pros and cons; 每个人都有其优点和缺点; no single technique can definitely be said to be the right one in all circumstances. 在任何情况下,绝对没有一种技术可以说是正确的技术。 Even so, I'll start off with: 即使这样,我还是要开始:

TL;DR: recommendation TL; DR:建议

Use RAISERROR if your stored procedure runs into trouble and cannot return the data it normally returns. 如果存储过程遇到问题并且无法返回其通常返回的数据,请使用RAISERROR Use OUTPUT parameters for information the client isn't free to ignore, but which isn't logically part of your result. 使用OUTPUT参数获取客户端不能随意忽略的信息,但是从逻辑上讲,这不是您的结果的一部分。 Use the return value if you have an informational status code that the client is free to ignore. 如果您具有客户端可以随意忽略的信息状态代码,请使用返回值。 Use additional result sets only if you know what you're doing. 仅当您知道自己在做什么时,才使用其他结果集。

RAISERROR RAISERROR

If your stored procedure encounters an error and can't return any data, you can use RAISERROR to terminate execution and cause an exception to be raised on the client side. 如果您的存储过程遇到错误并且无法返回任何数据,则可以使用RAISERROR终止执行并在客户端引发异常。

CREATE PROCEDURE [Delete]
     @ID nvarchar(64)
AS BEGIN
    IF(EXISTS(SELECT * FROM A where Ap = @ID))
    BEGIN
        RAISERROR('Wrong. Try again.', 11, 1);
        RETURN;
    END
    ELSE IF(EXISTS(SELECT * FROM B where Bp = @ID))
    BEGIN
        RAISERROR('Wrong in a different way. Try again.', 11, 2);
        RETURN;
    END
    ELSE
    BEGIN
        select * 
        from Table_D
    END
END

The second parameter (severity) must be set to at least 11 to make the error propagate as an exception, otherwise it's just an informational message. 必须将第二个参数(严重性)设置为至少11,以使错误作为异常传播,否则,它只是一条信息性消息。 Those can be captured too, but that's out of the scope of this answer. 那些也可以捕获,但这超出了此答案的范围。 The third parameter (state) can be whatever you like and could be used to pass the code of the error, if you need to localize it, for example. 第三个参数(状态)可以是您喜欢的任何参数,例如,如果需要本地化它,则可用于传递错误代码。 User-generated message always have SQL error code 50000, so that can't be used to distinguish different errors, and parsing the message is brittle. 用户生成的消息始终具有SQL错误代码50000,因此不能用于区分不同的错误,并且对消息的解析很脆弱。

The C# code to process the result: 用于处理结果的C#代码:

try {
    using (var reader = command.ExecuteReader()) {
        while (reader.Read()) {
            ...
        }
    }
} catch (SqlException e) {
    Console.WriteLine(
        "Database error executing [Delete] (code {0}): {1}", e.State, e.Message
    );
}

This is a natural fit for errors because the code to actually process the data stays what it is, and you can handle the exception at the right location (rather than propagating a status code everywhere). 这自然适合错误,因为实际处理数据的代码保持原样,并且您可以在正确的位置处理异常(而不是在任何地方传播状态代码)。 But this method is not appropriate if the stored procedure is expected to return a status that is informational and not an error, as you would be catching exceptions all the time even though nothing's wrong. 但是,如果预期存储过程返回的信息状态为非错误状态,则此方法不合适,因为即使没有问题,您也会一直捕获异常。

Output parameter 输出参数

A stored procedure can set parameter values as well as receive them, by declaring them OUTPUT : 存储过程可以通过声明OUTPUT来设置和接收参数值:

CREATE PROCEDURE [Delete]
     @ID nvarchar(64),
     @StatusCode INT OUTPUT
AS BEGIN
    IF(EXISTS(SELECT * FROM A where Ap = @ID))
    BEGIN
        SET @StatusCode = 1;
    END
    ELSE IF(EXISTS(SELECT * FROM B where Bp = @ID))
    BEGIN
        SET @StatusCode = 2;
    END
    ELSE
    BEGIN
        SET @StatusCode = 0;
        select * 
        from Table_D
    END
END

From C#, this is captured in a parameter marked as an output parameter: 在C#中,这是在标记为输出参数的参数中捕获的:

SqlParameter statusCodeParameter = command.Parameters.Add(
    new SqlParameter {
        ParameterName = "@StatusCode",
        SqlDbType = SqlDbType.Int,
        Direction = ParameterDirection.Output 
    }
);
using (var reader = command.ExecuteReader()) {
    int statusCode = (int) statusCodeParameter.Value;
    if (statusCode != 0) {
        // show alert
        return;
    }
    while (reader.Read()) {
        ...    
    }
}

The benefits here are that the client cannot forget to declare the parameter (it must be supplied), you're not restricted to a single INT , and you can use the value of the parameter to decide what you want to do with the resul set. 这样做的好处是,客户端不会忘记声明参数(必须提供该参数),您不仅限于单个INT ,而且可以使用参数的值来决定要对resul集执行的操作。 Returning structured data is cumbersome this way (lots of OUTPUT parameters), but you could capture this in a single XML parameter. 这种方式返回结构化数据很麻烦(很多OUTPUT参数),但是您可以在单个XML参数中捕获它。

Return value 返回值

Every stored procedure has a return value, which is a single INT . 每个存储过程都有一个返回值,该值是一个INT If you don't explicitly set it using RETURN , it stays at 0. 如果未使用RETURN显式设置它,则它保持为0。

CREATE PROCEDURE [Delete]
     @ID nvarchar(64)
AS BEGIN
    IF(EXISTS(SELECT * FROM A where Ap = @ID))
    BEGIN
        RETURN 1
    END
    ELSE IF(EXISTS(SELECT * FROM B where Bp = @ID))
    BEGIN
        RETURN 2
    END
    ELSE
    BEGIN
        select * 
        from Table_D
    END
END

From C#, the return value has to be captured in a single special parameter marked as the return value: 从C#中,必须使用标记为返回值的单个特殊参数捕获返回值:

SqlParameter returnValueParameter = command.Parameters.Add(
    new SqlParameter { Direction = ParameterDirection.ReturnValue }
);
using (var reader = command.ExecuteReader()) {
    // this could be empty
    while (reader.Read()) {
        ...
    }
}
int returnValue = (int) returnValueParameter.Value;

It's important to note that the return value will not be available until you've processed all other result sets that the stored procedure generates (if any), so if you're using it for a status code that indicates there are no rows, you must still process the empty result set first before you have the status code. 重要的是要注意,只有在处理完存储过程生成的所有其他结果集(如果有)后,返回值才可用,因此,如果将其用于表示没有行的状态代码,则返回您必须先处理空结果集,然后才能获取状态码。 You cannot return anything other than an INT . 除了INT之外,您不能返回任何其他内容。 Frameworks/OR mappers often have no support for the return value. 框架/ OR映射器通常不支持返回值。 Finally, note that the client is not required to do anything with the return value, so you have to carefully document its intended use. 最后,请注意,客户端不需要对返回值做任何事情,因此您必须仔细记录其预期用途。

Result set 结果集

The stored procedure can simply return what it wants as the result set, just like it's returning the other data. 存储过程可以简单地返回它想要的结果集,就像返回其他数据一样。 A stored procedure is allowed to return multiple result sets, so even if your status is logically separate from the other data, you can return it as a row. 存储过程允许返回多个结果集,因此,即使您的状态在逻辑上与其他数据分开,也可以将其作为一行返回。

CREATE PROCEDURE [Delete]
     @ID nvarchar(64)
AS BEGIN
    DECLARE @StatusCode INT = 0;
    IF(EXISTS(SELECT * FROM A where Ap = @ID))
    BEGIN
        SET @StatusCode = 1;
    END
    ELSE IF(EXISTS(SELECT * FROM B where Bp = @ID))
    BEGIN
        SET @StatusCode = 2;
    END

    SELECT @StatusCode AS StatusCode;

    IF @StatusCode = 0
    BEGIN
        select * 
        from Table_D
    END
END

To process this with C#, we need SqlDataReader.NextResult : 要使用C#处理此问题,我们需要SqlDataReader.NextResult

using (var reader = command.ExecuteReader()) {
    if (!reader.Read()) throw new MyException("Expected result from stored procedure.");
    statusCode = reader.GetInt32(reader.GetOrdinal("StatusCode"));
    if (statusCode != 0) {
        // show alert
        return;
    }
    reader.NextResult();
    while (reader.Read()) {
        // use the actual result set
    }
}

The main drawback here is that it's not intuitive for a stored procedure to return a variable number of result sets, and very few data frameworks/OR mappers support it, so you'll nearly always end up writing manual code like this. 这里的主要缺点是,存储过程返回可变数量的结果集并不直观,并且很少有数据框架/ OR映射程序支持它,因此您几乎总是以这种方式编写手动代码。 Returning multiple result sets is not really a good fit for returning a single piece of data like a status code, but it might be an alternative to returning structured data in an XML output parameter (especially if there's lots). 返回多个结果集并不是真的很适合返回单个数据,例如状态码,但是它可能是在XML输出参数中返回结构化数据的一种替代方法(尤其是如果有很多的话)。

The return seems to be out of scope of the procedure. 返回值似乎超出了该过程的范围。 Try: 尝试:

ALTER PROCEDURE Delete
    @ID nvarchar(64),
    @value int=0 output
AS
BEGIN

    IF(EXISTS(SELECT * FROM A where Ap=@ID))
    BEGIN
        set @value=1
    END

    ELSE IF(EXISTS(SELECT * FROM B where Bp=@ID))
    BEGIN
        set @value=2
    END

    ELSE
    BEGIN
    set @value=5
    end --end if

    RETURN @value
end --end procedure

This is where using tabbing properly makes the code a lot more readable, and these problems more obvious 在这里,正确使用制表符可使代码更具可读性,并且这些问题更加明显

Don't use the output parameter. 不要使用输出参数。 Rather, use this: 而是使用此:

ALTER PROCEDURE Delete
@ID nvarchar(64)

AS
BEGIN

DECLARE @value int
SET @value = 0

IF(EXISTS(SELECT 1 FROM A where Ap=@ID))
BEGIN
set @value=1
END

ELSE IF(EXISTS(SELECT 1 FROM B where Bp=@ID))
BEGIN
set @value=2
END

ELSE
BEGIN
set @value=5
end

Select @value as Value, * from Table_D
end

Can you try running the SP as the script below? 您可以尝试运行SP作为以下脚本吗?

declare @pID as nvarchar(64)
declare @pValue as int

set @pID = 1 -- Your ID filter

exec Delete @pID, @pValue OUTPUT

select @pValue

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM