2009-06-29 90 views
25

我有一个存储过程,它执行一些参数验证,如果参数无效,应该会失败并停止执行。执行存储过程参数验证的“正确”方式

我的错误第一种方法检查是这样的:

create proc spBaz 
(
    @fooInt int = 0, 
    @fooString varchar(10) = null, 
    @barInt int = 0, 
    @barString varchar(10) = null 
) 
as 
begin 
    if (@fooInt = 0 and (@fooString is null or @fooString = '')) 
    raiserror('invalid parameter: foo', 18, 0) 

    if (@barInt = 0 and (@barString is null or @barString = '')) 
    raiserror('invalid parameter: bar', 18, 0) 

    print 'validation succeeded' 
    -- do some work 
end 

这并没有这样的伎俩,因为严重性18不停止执行和“验证成功”与错误信息一起打印。

我知道我可以简单地增加每RAISERROR后回报,但这个看起来有点丑对我说:

if (@fooInt = 0 and (@fooString is null or @fooString = '')) 
    begin 
    raiserror('invalid parameter: foo', 18, 0) 
    return 
    end 

    ... 

    print 'validation succeeded' 
    -- do some work 

由于严重性11错误和更高一个try/catch块内抓我测试了另一种方法是封装我的错误检查里面这样一个try/catch块。问题在于错误被吞噬了,根本没有发送给客户端。所以我做了一些研究,发现了一种rethrow错误:

我还是不满意这种做法,所以我问你:

请问你的参数验证样子的?做这种检查有没有某种“最佳做法”?

回答

37

我不认为有一个“正确”的方式来做到这一点。

我自己的偏好与您的第二个示例类似,但是对于每个参数和更明确的错误消息都有单独的验证步骤。正如你所说,这有点麻烦和丑陋,但代码的意图对任何读者都是显而易见的,并且它完成了工作。

IF (ISNULL(@fooInt, 0) = 0) 
BEGIN 
    RAISERROR('Invalid parameter: @fooInt cannot be NULL or zero', 18, 0) 
    RETURN 
END 

IF (ISNULL(@fooString, '') = '') 
BEGIN 
    RAISERROR('Invalid parameter: @fooString cannot be NULL or empty', 18, 0) 
    RETURN 
END 
+0

是否有任何理由为什么你使用IF(ISNULL(@fooString,'')='')而不是IF(@fooString为null)? – macleojw 2009-06-29 12:22:26

+9

@macleojw:他同时检查null和''..聪明:) – VVS 2009-06-29 12:23:31

+6

第二个验证程序的语法无效:'RAISEERROR'。应该只有一个'e'。有趣的是,在英语中它是正确的,因为'raise + error'具有双'e',但不是MS SQL语言。 – 2012-06-19 11:25:05

1

我们通常避免RAISEERROR(),并返回一个值,该值指示错误,例如一个负数:

if <errorcondition> 
    return -1 

或者通过导致两个输出参数:

create procedure dbo.TestProc 
    .... 
    @result int output, 
    @errormessage varchar(256) output 
as 
set @result = -99 
set @errormessage = null 
.... 
if <errorcondition> 
    begin 
    set @result = -1 
    set @errormessage = 'Condition failed' 
    return @result 
    end 
+0

你为什么喜欢通过raiseerror()返回? – macleojw 2009-06-29 12:23:54

+0

Raiseerror是不可预测的(可能会继续执行!),而不是eveyr客户端以相同的方式处理它。 perl客户端可能会死亡! – Andomar 2009-06-29 13:23:18

0

我宁愿尽快退出,并且不要指出在程序结束时所有内容都从同一点返回。几年前,我接受了这种习惯的习惯。此外,我总是返回一个值:

RETURN 10 

该应用程序将在正数上显示致命错误,并在负数值上显示用户警告消息。

我们总是通过错误消息的文本传回一个OUTPUT参数。

例如:

IF ~error~ 
BEGIN 
    --if it is possible to be within a transaction, so any error logging is not ROLLBACK later 
    IF XACT_STATE()!=0 
    BEGIN 
     ROLLBACK 
    END 

    SET @OutputErrMsg='your message here!!' 
    INSERT INTO ErrorLog (....) VALUES (.... @OutputErrMsg) 
    RETURN 10 

END 
1

你可以从这个答案的历史看,我跟着这个问题和接受的答案,然后着手“发明”的解决方案,这是基本相同的第二种方法。

咖啡因是我的主要能量来源,因为我花了大半生半天,因为我花了太多时间编码;因此,我没有意识到我的假人,直到你正确地指出了它。因此,为了记录,我更喜欢第二种方法:使用SP来提高当前错误,然后在参数验证周围使用TRY/CATCH。

它减少了对所有IF/BEGIN/END块的需求,因此减少了线数,并将焦点放回验证。阅读SP代码时,能够看到正在参数上执行的测试很重要;在我看来,所有额外的语法松散来满足SQL解析器都会妨碍我们。

0

我总是使用参数@Is_Success位作为OUTPUT。所以如果我有错误,那么@ Is_success = 0。当父进程检查@ Is_Success = 0时,它会回滚它的事务(带有子事务),并从@Error_Message向客户端发送错误消息。