2009-06-29 89 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向客戶端發送錯誤消息。