繁体   English   中英

SQL Server 2008存储过程的返回值

[英]SQL Server 2008 stored procedure return value

这是我的存储过程

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

问题是当我执行它时,它不返回任何值

有多种方法可以将状态信息从存储过程返回到应用程序。 每个人都有其优点和缺点; 在任何情况下,绝对没有一种技术可以说是正确的技术。 即使这样,我还是要开始:

TL; DR:建议

如果存储过程遇到问题并且无法返回其通常返回的数据,请使用RAISERROR 使用OUTPUT参数获取客户端不能随意忽略的信息,但是从逻辑上讲,这不是您的结果的一部分。 如果您具有客户端可以随意忽略的信息状态代码,请使用返回值。 仅当您知道自己在做什么时,才使用其他结果集。

RAISERROR

如果您的存储过程遇到错误并且无法返回任何数据,则可以使用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

必须将第二个参数(严重性)设置为至少11,以使错误作为异常传播,否则,它只是一条信息性消息。 那些也可以捕获,但这超出了此答案的范围。 第三个参数(状态)可以是您喜欢的任何参数,例如,如果需要本地化它,则可用于传递错误代码。 用户生成的消息始终具有SQL错误代码50000,因此不能用于区分不同的错误,并且对消息的解析很脆弱。

用于处理结果的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
    );
}

这自然适合错误,因为实际处理数据的代码保持原样,并且您可以在正确的位置处理异常(而不是在任何地方传播状态代码)。 但是,如果预期存储过程返回的信息状态为非错误状态,则此方法不合适,因为即使没有问题,您也会一直捕获异常。

输出参数

存储过程可以通过声明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

在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()) {
        ...    
    }
}

这样做的好处是,客户端不会忘记声明参数(必须提供该参数),您不仅限于单个INT ,而且可以使用参数的值来决定要对resul集执行的操作。 这种方式返回结构化数据很麻烦(很多OUTPUT参数),但是您可以在单个XML参数中捕获它。

返回值

每个存储过程都有一个返回值,该值是一个INT 如果未使用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

从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;

重要的是要注意,只有在处理完存储过程生成的所有其他结果集(如果有)后,返回值才可用,因此,如果将其用于表示没有行的状态代码,则返回您必须先处理空结果集,然后才能获取状态码。 除了INT之外,您不能返回任何其他内容。 框架/ OR映射器通常不支持返回值。 最后,请注意,客户端不需要对返回值做任何事情,因此您必须仔细记录其预期用途。

结果集

存储过程可以简单地返回它想要的结果集,就像返回其他数据一样。 存储过程允许返回多个结果集,因此,即使您的状态在逻辑上与其他数据分开,也可以将其作为一行返回。

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

要使用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
    }
}

这里的主要缺点是,存储过程返回可变数量的结果集并不直观,并且很少有数据框架/ OR映射程序支持它,因此您几乎总是以这种方式编写手动代码。 返回多个结果集并不是真的很适合返回单个数据,例如状态码,但是它可能是在XML输出参数中返回结构化数据的一种替代方法(尤其是如果有很多的话)。

返回值似乎超出了该过程的范围。 尝试:

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

在这里,正确使用制表符可使代码更具可读性,并且这些问题更加明显

不要使用输出参数。 而是使用此:

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

您可以尝试运行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