SQLServerで同じ例外を再スローする方法


86

tryブロックで発生したばかりのSQLServerで同じ例外を再スローしたいと思います。同じメッセージをスローできますが、同じエラーをスローしたいと思います。

BEGIN TRANSACTION
    BEGIN TRY
        INSERT INTO Tags.tblDomain (DomainName, SubDomainId, DomainCode, Description)
            VALUES(@DomainName, @SubDomainId, @DomainCode, @Description)
        COMMIT TRANSACTION
    END TRY
    
    BEGIN CATCH
        declare @severity int; 
        declare @state int;

        select @severity=error_severity(), @state=error_state();

        RAISERROR(@@Error,@ErrorSeverity,@state);
        ROLLBACK TRANSACTION
    END CATCH

RAISERROR(@@Error, @ErrorSeverity, @state);

この行にはエラーが表示されますが、そのような機能が必要です。これにより、エラー番号50000のエラーが発生しますが@@error、渡したエラー番号をスローしたいのですが、

フロントエンドでこのエラーをキャプチャしたいと思います。

すなわち

catch (SqlException ex)
{
    if ex.number==2627
    MessageBox.show("Duplicate value cannot be inserted");
}

この機能が欲しいです。これは、を使用して達成することはできませんraiseerror。バックエンドでカスタムエラーメッセージを表示したくありません。

RAISEERROR ErrorNoを渡すと、以下のエラーが返され、catchにスローされます。

Msg 2627, Level 14, State 1, Procedure spOTest_DomainInsert,

14行目UNIQUEKEY制約「UK_DomainCode」への違反。オブジェクト 'Tags.tblDomain'に重複するキーを挿入できません。ステートメントは終了されました。

編集:

ストアドプロシージャに実行する必要のある複数のクエリが含まれていることを考慮して、フロントエンドで例外を処理する場合、try catchブロックを使用しないことの欠点は何ですか?

回答:


120

これは、エラーが発生した場合に一連のステートメントをロールバックしてエラーメッセージを報告する、完全に機能するクリーンなコードサンプルです。

begin try
    begin transaction;

    ...

    commit transaction;
end try
begin catch
    if @@trancount > 0 rollback transaction;
    throw;
end catch

SQL2012より前

begin try
    begin transaction;
    
    ...
    
    commit transaction;
end try
begin catch
    declare @ErrorMessage nvarchar(max), @ErrorSeverity int, @ErrorState int;
    select @ErrorMessage = ERROR_MESSAGE() + ' Line ' + cast(ERROR_LINE() as nvarchar(5)), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE();
    if @@trancount > 0 rollback transaction;
    raiserror (@ErrorMessage, @ErrorSeverity, @ErrorState);
end catch

8
ストアドプロシージャの途中でこれを使用していたところ、。の後に実行され続けることがわかりましたraiserror。これは、c#がthrow。の後に終了する方法とは異なります。その動作に合わせたかっreturncatchので、内部を追加しました。
ブライアンJ

@BogdanBogdanovこのコードのポイントは最小限であり、代わりに入力された実際のコードを損なうことはないため、編集をロールバックしました...
Ben Gripka 2016

わかりました、問題ありません、@ BenGripka。画面上で読みやすくするようにしています。ロールバックを行う理由を指摘していただきありがとうございます。
ボグダンボグダノフ

1
@BrianJ:通常、実行が停止するかどうかは、元のエラーの重大度によって異なります。重大度が11以上の場合、実行は停止する必要があります。重大度> = 11のcatchブロック内のレイザーが実行を停止しないため、これは本当に奇妙です。あなたの観察は非常に良く、少なくとも2008r2のSQLサーバーが脳死していることを示しています。新しいバージョンの方が良いようです。
コスタ2017

1
@costaRAISERROR()のドキュメントを参照してください。重大度が11以上のCATCH場合、ブロック内にある場合にのみブロックにジャンプしTRYます。したがって、フロー制御に影響を与えたい場合は、コードを回避する必要ありBEGIN TRY…END CATCHますRAISERROR()
binki

137

SQL 2012では、throwステートメントが導入されています。

http://msdn.microsoft.com/en-us/library/ee677615.aspx

THROWステートメントがパラメーターなしで指定されている場合は、CATCHブロック内に表示する必要があります。これにより、キャッチされた例外が発生します。

BEGIN TRY
    BEGIN TRANSACTION
    ...
    COMMIT TRANSACTION
END TRY
BEGIN CATCH
    ROLLBACK TRANSACTION;
    THROW
END CATCH

2
注意してください、このソリューションはSQL Server 2012以降でのみ機能するようです:msdn.microsoft.com/en-us/library/ee677615.aspx
Adi

3
@BogdanBogdanovエラーをログに記録できるように、場合によってはいくつかの状況を処理できますが、それができない場合は、エラーを再スローして、より高い試行/キャッチで処理できるようにします
Robert McKee

はい、@ RobertMcKeeです。私はそれを理解します。申し訳ありませんが、このコメントをクリアするのを忘れていました。
ボグダンボグダノフ

3
ROLLBACK行上のそのセミコロンは重要です!それがないと、が発生する可能性がありSQLException: Cannot roll back THROWます。
idontevenseethecode

5

CATCHブロック内での再スロー(SQL2012より前のコード、SQL2012以降ではTHROWステートメントを使用):

DECLARE
    @ErrorMessage nvarchar(4000) = ERROR_MESSAGE(),
    @ErrorNumber int = ERROR_NUMBER(),
    @ErrorSeverity int = ERROR_SEVERITY(),
    @ErrorState int = ERROR_STATE(),
    @ErrorLine int = ERROR_LINE(),
    @ErrorProcedure nvarchar(200) = ISNULL(ERROR_PROCEDURE(), '-');
SELECT @ErrorMessage = N'Error %d, Level %d, State %d, Procedure %s, Line %d, ' + 'Message: ' + @ErrorMessage;
RAISERROR (@ErrorMessage, @ErrorSeverity, 1, @ErrorNumber, @ErrorSeverity, @ErrorState, @ErrorProcedure, @ErrorLine)

4

私はあなたの選択は次のとおりだと思います:

  • エラーをキャッチしないでください(バブルアップさせてください)
  • カスタムのものを上げる

ある時点で、SQLはおそらく再起動コマンド、または特定のエラーのみをキャッチする機能を導入します。ただし、今のところ、回避策を使用してください。ごめんなさい。


7
SQL 2012では、新しいTHROWキーワードを使用して例外を再発生させることができます
sergiom 2013

5
はい。もちろん、この質問がされたとき、それは利用できませんでした。
ロブファーリー

例外を適切に処理するには、クリーンアップ、修正アクション、およびクロージャアクティビティが必要になる可能性があるため、新しいエラーをキャッチしてスローすることは、キャッチせずに「バブルアップ」させるよりも重要です。明らかな例は、カーソルを閉じて破棄することです。他の例としては、ロギングプロシージャを実行したり、一部のデータをリセットしたりする場合があります。
アントニーブース

1

あなたはできません。唯一のエンジンは、より少ないあなたが行うことができます50000全てがという例外を投げるよりも、エラーを投げることができるルックスそれのような...

ここで私の答えを見てください

ここでの質問者は、クライアント側のトランザクションを使用して、彼が望んでいたことを実行しましたが、これはちょっとばかげていると思います...


0

わかりました、これは回避策です... :-)

DECLARE @Error_Number INT
BEGIN TRANSACTION 
    BEGIN TRY
    INSERT INTO Test(Id, Name) VALUES (newID(),'Ashish') 
    /* Column 'Name' has unique constraint on it*/
    END TRY
    BEGIN CATCH

            SELECT ERROR_NUMBER()
            --RAISERROR (@ErrorMessage,@Severity,@State)
            ROLLBACK TRAN
    END CATCH

catchブロックに注意すると、エラーは発生しませんが、実際のエラー番号が返されます(また、トランザクションがロールバックされます)。.NETコードでは、例外をキャッチする代わりに、ExecuteScalar()を使用すると、必要な実際のエラー番号を取得し、適切な番号を表示します。

int errorNumber=(int)command.ExecuteScalar();
if(errorNumber=<SomeNumber>)
{
    MessageBox.Show("Some message");
}

お役に立てれば、

編集:-注意:影響を受けるレコードの数を取得してExecuteNonQueryを使用しようとすると、上記の解決策が機能しない場合があります。そうでなければ、私はそれがあなたが必要とするものに合うと思います。お知らせ下さい。


@Ashish Gupta:Thxで助けてください。ただし、データベースからフロントエンドに例外をスローする必要があります。そうしないと、print error_number()、return error_number、1 uの提案など、多くのオプションが開かれます
Shantanu Gupta

0

エラーが発生した後にストアドプロシージャの実行を停止し、エラーを呼び出し元のプログラムにバブルバックする方法は、次のコードでエラーをスローする可能性のある各ステートメントに従うことです。

If @@ERROR > 0
Return

エラーが発生した後もストアドプロシージャでの実行を続行できることに驚きました。これに気付かないと、バグの追跡が困難になる可能性があります。

このタイプのエラー処理は、Visual Basic 6と類似しています(.Netより前)。SQLServer2012のThrowコマンドを楽しみにしています。


0

まだ2012に移行していないことを考えると、元のエラーコードのバブリングを実装する1つの方法は、catchブロックから(再)スローする例外のテキストメッセージ部分を使用することです。呼び出し元コードがcatchブロックで解析するためのXMLテキストなど、いくつかの構造を含めることができることに注意してください。


0

トランザクション内でSQLステートメントを実行し、エラーをコードにフィードする場合は、これらのシナリオのラッパーストアドプロシージャを作成することもできます。

CREATE PROCEDURE usp_Execute_SQL_Within_Transaction
(
    @SQL nvarchar(max)
)
AS

SET NOCOUNT ON

BEGIN TRY
    BEGIN TRANSACTION
        EXEC(@SQL)
    COMMIT TRANSACTION
END TRY

BEGIN CATCH
    DECLARE @ErrorMessage nvarchar(max), @ErrorSeverity int, @ErrorState int
    SELECT @ErrorMessage = N'Error Number: ' + CONVERT(nvarchar(5), ERROR_NUMBER()) + N'. ' + ERROR_MESSAGE() + ' Line ' + CONVERT(nvarchar(5), ERROR_LINE()), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE()
    ROLLBACK TRANSACTION
    RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState)
END CATCH

GO

-- Test it
EXEC usp_Execute_SQL_Within_Transaction @SQL = 'SELECT 1; SELECT 2'
EXEC usp_Execute_SQL_Within_Transaction @SQL = 'SELECT 1/0; SELECT 2'
EXEC usp_Execute_SQL_Within_Transaction @SQL = 'EXEC usp_Another_SP'

-2

設計の観点から、元のエラー番号とカスタムメッセージで例外をスローすることのポイントは何ですか?ある程度、アプリケーションとデータベース間のインターフェース契約を破ります。元のエラーをキャッチして上位のコードで処理する場合は、データベースで処理しないでください。次に、例外をキャッチすると、ユーザーに表示されるメッセージを任意のメッセージに変更できます。データベースコードが「正しくない」ので、私はそれをしません。他の人が言ったように、あなたはあなた自身のエラーコードのセット(50000以上)を定義し、代わりにそれらを投げるべきです。次に、潜在的なビジネス上の問題とは別に、整合性の問題(「重複する値は許可されません」)を処理できます-「郵便番号が無効です」、「基準に一致する行が見つかりませんでした」など。


9
元のエラー番号とカスタムメッセージで例外をスローするポイントは何ですか?1つまたは2つの特定の(予期される)エラーをcatchブロックで直接処理し、残りを上位層に残したいとします。したがって、処理しなかった例外を再スローできる必要があります...できれば、他の特別な方法でエラーを報告して処理する必要はありません。
ジェンダ2012

1
@Jendaが説明したことに加えて、C#の場合と同様に、try-catchを使用して、例外後にコードの実行が継続されないようにします。C#:try { code(); } catch (Exception exc) { log(exc); throw; } finally { cleanup(); }ではthrow;、元のコンテキストで元の例外が発生します。
R. Schreurs 2013

エラーをキャッチし、SQLでカスタムエラーメッセージを再スローして、エラーが発生した行を説明する詳細や、後でエラーを追跡するのに役立つその他の詳細(挿入しようとしているデータなど)を追加します。
ラッセルハンキンス2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.