SQL Server-SQLスクリプトの実行を停止または中断する


325

「break」または「exit」コマンドのように、SQLサーバーでSQLスクリプトの実行をすぐに停止する方法はありますか?

挿入を開始する前に検証と検索を行うスクリプトがあり、検証または検索のいずれかが失敗した場合はスクリプトを停止します。

回答:


371

RAISERRORの方法

raiserror('Oh no a fatal error', 20, -1) with log

これにより接続が終了し、残りのスクリプトの実行が停止します。

WITH LOGこの方法で機能するには、重大度レベル20以上とオプションの両方が必要であることに注意してください。

これはGOステートメントでも機能します。

print 'hi'
go
raiserror('Oh no a fatal error', 20, -1) with log
go
print 'ho'

あなたに出力を与えるでしょう:

hi
Msg 2745, Level 16, State 2, Line 1
Process ID 51 has raised user error 50000, severity 20. SQL Server is terminating this process.
Msg 50000, Level 20, State 1, Line 1
Oh no a fatal error
Msg 0, Level 20, State 0, Line 0
A severe error occurred on the current command.  The results, if any, should be discarded.

「ho」は出力されないことに注意してください。

警告:

  • これは、admin( 'sysadmin'ロール)としてログインしている場合にのみ機能し、データベース接続もありません。
  • 管理者としてログインしていない場合、RAISEERROR()呼び出し自体は失敗し、スクリプトは実行を続けます。
  • sqlcmd.exeで呼び出されると、終了コード2745が報告されます。

リファレンス:http : //www.mydatabasesupport.com/forums/ms-sqlserver/174037-sql-server-2000-abort-whole-script.html#post761334

noexecメソッド

GOステートメントで機能するもう1つの方法は、set noexec onです。これにより、スクリプトの残りの部分がスキップされます。これは、接続を終了しませんが、有効にする必要がありますnoexec任意のコマンドを実行する前に、再びオフ。

例:

print 'hi'
go

print 'Fatal error, script will not continue!'
set noexec on

print 'ho'
go

-- last line of the script
set noexec off -- Turn execution back on; only needed in SSMS, so as to be able 
               -- to run this script again in the same session.

14
すごい!少し「大きな棒」のアプローチですが、本当に必要な場合があります。重大度20(またはそれ以上)と「WITH LOG」の両方が必要なことに注意してください。
Rob Garrison

5
noexecメソッドを使用しても、スクリプトの残りの部分は引き続き解釈されるため、列が存在しないなどのコンパイル時エラーが発生することに注意してください。一部のコードをスキップして、欠落している列を含む既知のスキーマの変更に条件付きで対応したい場合は、sqlcommandモードで:rを使用して外部ファイルを参照するのが唯一の方法です。
David Eison

20
noexecは素晴らしいです。どうもありがとう!
Gaspa79 2013

2
「これで接続が終了します」-そうではないようです、少なくとも私が見ているのはそうです。
jcollum 2014年

6
私はこの方法を試していて、気づいたときに正しい結果が得られなかった...レイザーにEが1つしか存在しない...
bobkingof12vs

187

RETURNを使用するだけです(ストアドプロシージャの内部と外部の両方で機能します)。


2
なんらかの理由で、スクリプトではreturnが機能しないと思っていましたが、試してみましたが、機能しました。ありがとう
アンディホワイト

4
スクリプトでは、ストアドプロシージャのように値でRETURNを実行することはできませんが、RETURNは実行できます。
Rob Garrison

53
いいえ、次のGOまで終了します。次のバッチ(GO後)は通常どおり実行されます
mortb

2
それが次のGOの後に続くので、想定するのは危険です。
Justin

1
GOはスクリプトのターミネータまたはデリミタです。SQLコードではありません。GOは、GO区切り文字の後に新しいスクリプトが開始されることをデータベースエンジンにコマンドを送信するために使用しているクライアントへの単なる指示です。
リバースエンジニア

50

SQLCMDモードを使用できる場合は、呪文

:on error exit

(コロンを含む)を指定すると、RAISERRORはスクリプトを実際に停止します。例えば、

:on error exit

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[SOMETABLE]') AND type in (N'U')) 
    RaisError ('This is not a Valid Instance Database', 15, 10)
GO

print 'Keep Working'

出力されます:

Msg 50000, Level 15, State 10, Line 3
This is not a Valid Instance Database
** An error was encountered during execution of batch. Exiting.

バッチは停止します。SQLCMDモードがオンになっていない場合、コロンに関する解析エラーが発生します。残念なことに、SQLCMDモードでなくてもスクリプトが実行される場合、SQL Managment Studioは解析時間エラーさえも通過してしまうので、完全に防弾ではありません。それでも、コマンドラインから実行している場合は問題ありません。


4
素晴らしいコメント、ありがとう。SSMS SQLCmdモードでは、[クエリ]メニューの下にあるトグルであることを追加します。
David Peters、

これは便利です。つまり、実行時に-bオプションが必要ないことを意味します
JonnyRaa

2
その後、呪文...しかし、どのように私はマジックミスルをキャストしますか?!
JJS 2017

1
完璧。sysadminウルトラ追加のユーザー権限は必要ありません
Pac0

21

私はRAISERRORを使用しません。SQLには、この目的で使用できるIFステートメントがあります。検証とルックアップを行い、ローカル変数を設定してから、IFステートメントで変数の値を使用して、挿入を条件付きにします。

すべての検証テストのさまざまな結果をチェックする必要はありません。通常は、フラグ変数を1つだけ使用してこれを実行し、渡されたすべての条件を確認できます。

declare @valid bit

set @valid = 1

if -- Condition(s)
begin
  print 'Condition(s) failed.'
  set @valid = 0
end

-- Additional validation with similar structure

-- Final check that validation passed
if @valid = 1
begin
  print 'Validation succeeded.'

  -- Do work
end

検証がより複雑な場合でも、最終チェックに含めるフラグ変数は数個必要です。


ええ、スクリプトの他の部分でIFを使用していますが、挿入を試みる前にすべてのローカル変数を確認する必要はありません。スクリプト全体を停止して、ユーザーに入力を確認させるだけです。(これは簡単で汚いスクリプトです)
アンディホワイト

4
なぜポスターが「したい」のかではなく、技術的に正しいので、なぜこの回答がマークダウンされたのかはよくわかりません。
John Sansom

Begin..End内に複数のブロックを含めることは可能ですか?意味STATEMENT; GO; ステートメント; GO; などなど?エラーが発生していますが、それが原因かもしれません。
Nenotlep 2014年

3
これはRAISERROR、特に誰がスクリプトを実行するのか、どの特権で実行するのかわからない場合よりもはるかに信頼性が高くなります。
Cypher

@John Sansom:ここで私が目にする唯一の問題は、GOステートメントに分岐しようとするとIFステートメントが機能しないことです。スクリプトがGOステートメント(DDLステートメントなど)に依存している場合、これは大きな問題です。ここで働く一例であるなしに最初に行くの声明:declare @i int = 0; if @i=0 begin select '1st stmt in IF block' go end else begin select 'ELSE here' end go
ジェームズ・ジェンセン

16

SQL 2012+では、THROWを使用できます。

THROW 51000, 'Stopping execution because validation failed.', 0;
PRINT 'Still Executing'; -- This doesn't execute with THROW

MSDNから:

例外を発生させ、TRY…CATCH構文のCATCHブロックに実行を転送します... TRY…CATCH構文が使用できない場合、セッションは終了します。例外が発生した行番号と手続きが設定されます。重大度は16に設定されています。


1
THROWはRAISERRORを置き換えることを目的としていますが、それを使用して同じスクリプトファイル内の後続のバッチを防ぐことはできません。
NReilingh 2016年

@NReilinghを修正してください。それがBlorgbeardの答えが本当に唯一の解決策であるところです。ただし、sysadminが必要です(重大度レベル20)。スクリプトに複数のバッチが含まれていない場合は、かなり手間がかかります。
ジョーダンパーカー

2
現在のトランザクションもキャンセルしたい場合は、xact abortをオンに設定します。
ヌレティン

13

noexec on / offソリューションをトランザクションで正常に拡張し、スクリプトをオールオアナッシング方式で実行しました。

set noexec off

begin transaction
go

<First batch, do something here>
go
if @@error != 0 set noexec on;

<Second batch, do something here>
go
if @@error != 0 set noexec on;

<... etc>

declare @finished bit;
set @finished = 1;

SET noexec off;

IF @finished = 1
BEGIN
    PRINT 'Committing changes'
    COMMIT TRANSACTION
END
ELSE
BEGIN
    PRINT 'Errors occured. Rolling back changes'
    ROLLBACK TRANSACTION
END

エラーが発生して実行が無効になった場合でも、コンパイラーはIFの@finished変数を「理解」しているようです。ただし、値が1に設定されるのは、実行が無効になっていない場合のみです。したがって、トランザクションを適切にコミットまたはロールバックできます。


理解できません。私は指示に従いました。各GOの後に次のSQLを入力しました。 IF (XACT_STATE()) <> 1 BEGIN Set NOCOUNT OFF ;THROW 525600, 'Rolling back transaction.', 1 ROLLBACK TRANSACTION; set noexec on END; しかし、実行が停止することはなく、3つの「ロールバックトランザクション」エラーが発生しました。何か案は?
user1161391

12

SQLステートメントをWHILEループでラップし、必要に応じてBREAKを使用できます

WHILE 1 = 1
BEGIN
   -- Do work here
   -- If you need to stop execution then use a BREAK


    BREAK; --Make sure to have this break at the end to prevent infinite loop
END

5
私はこれの見た目が好きです、エラーを発生させるよりも少しいいようです。間違いなく最後の休憩を忘れたくない!
アンディホワイト

1
また、変数を使用して、すぐにループの先頭に設定し、「分割」を回避することもできます。DECLARE @ST INT; SET @ST = 1; WHILE @ST = 1; BEGIN; SET @ST = 0; ...; ENDもっと冗長ですが、とにかく、それはTSQLです;-)

これは一部の人々がgotoを実行する方法ですが、gotoよりも従うのはより混乱します。
ヌレティン

このアプローチは、予期しない偶発的なGOから保護します。感謝しています。
it3xl

10

GOTOステートメントを使用して、実行のフローを変更できます。

IF @ValidationResult = 0
BEGIN
    PRINT 'Validation fault.'
    GOTO EndScript
END

/* our code */

EndScript:

2
gotoを使用すると、例外を処理できます。変数とネストの量を減らし、切断を引き起こしません。SQL Serverスクリプトで許可されている古い例外処理よりも望ましいでしょう。
Antonio Drusin

ここでの他のすべての提案と同様に、「コード」に「GO」ステートメントが含まれている場合は機能しません。
Mike Gledhill

9

Sglassesメソッドをさらに洗練すると、上記の行はSQLCMDモードの使用を強制し、SQLCMDモードを使用していない場合はスクリプト:on error exitを終了するか、エラーで終了するために使用して
CONTEXT_INFOを使用して状態を追跡します。

SET CONTEXT_INFO  0x1 --Just to make sure everything's ok
GO 
--treminate the script on any error. (Requires SQLCMD mode)
:on error exit 
--If not in SQLCMD mode the above line will generate an error, so the next line won't hit
SET CONTEXT_INFO 0x2
GO
--make sure to use SQLCMD mode ( :on error needs that)
IF CONTEXT_INFO()<>0x2 
BEGIN
    SELECT CONTEXT_INFO()
    SELECT 'This script must be run in SQLCMD mode! (To enable it go to (Management Studio) Query->SQLCMD mode)\nPlease abort the script!'
    RAISERROR('This script must be run in SQLCMD mode! (To enable it go to (Management Studio) Query->SQLCMD mode)\nPlease abort the script!',16,1) WITH NOWAIT 
    WAITFOR DELAY '02:00'; --wait for the user to read the message, and terminate the script manually
END
GO

----------------------------------------------------------------------------------
----THE ACTUAL SCRIPT BEGINS HERE-------------

2
これは、スクリプトを中止できないというSSMSの狂気を回避するために私が見つけた唯一の方法です。しかし、最初に「SET NOEXEC OFF」を追加し、SQLCMDモードでない場合は「SET NOEXEC ON」を追加しました。そうしないと、ログでレベル20のエラーが発生しない限り、実際のスクリプトは続行します。
Mark Sowul、2012

8

これはストアドプロシージャですか?もしそうなら、 "Return NULL"のように、単にReturnを実行できると思います。


答えてくれてありがとう。知っておくと便利ですが、この場合はストアドプロシージャではなく、スクリプトファイルです
Andy White

1
@Gordon常にではありません(ここで検索しています)。他の回答を参照してください(GOが1つの理由としてそれをトリップします)
Mark Sowul '27年

6

適切なコードブロックをtry catchブロックでラップすることをお勧めします。その後、必要に応じて重大度11のRaiserrorイベントを使用して、catchブロックにブレークできます。単にレイラーを発生させたいが、tryブロック内で実行を継続したい場合は、重大度を低くしてください。

理にかなっていますか?

乾杯、ジョン

[BOLリファレンスを含むように編集]

http://msdn.microsoft.com/en-us/library/ms175976(SQL.90).aspx


SQLでtry-catchを見たことがありません-意味の簡単な例を投稿していただけませんか?
アンディホワイト

2
2005年の新機能です。BEGINTRY {sql_statement | statement_block} END TRY BEGIN CATCH {sql_statement | statement_block} END CATCH [; ]
サム・

@Andy:リファレンスが追加され、例が含まれています。
John Sansom

2
TRY-CATCHブロックはitelf内のGOを許可しません。
AntonK 2013

4

RAISERRORを使用できます。


3
これは、回避可能なエラー(ここでは参照検証について話していると仮定)を発生させることは意味がありません。挿入が行われる前に検証が可能である場合、これは恐ろしい方法です。
Dave Swersky、

2
raiserrorは、重大度の設定が低い情報メッセージとして使用できます。
Mladen Prajdic 2009年

2
承認された回答に記載されている特定の条件が満たされない限り、スクリプトは続行されます。
エリックJ.

4

これらはいずれも「GO」ステートメントでは機能しません。このコードでは、重大度が10か11かに関係なく、最後のPRINTステートメントを取得します。

テストスクリプト:

-- =================================
PRINT 'Start Test 1 - RAISERROR'

IF 1 = 1 BEGIN
    RAISERROR('Error 1, level 11', 11, 1)
    RETURN
END

IF 1 = 1 BEGIN
    RAISERROR('Error 2, level 11', 11, 1)
    RETURN
END
GO

PRINT 'Test 1 - After GO'
GO

-- =================================
PRINT 'Start Test 2 - Try/Catch'

BEGIN TRY
    SELECT (1 / 0) AS CauseError
END TRY
BEGIN CATCH
    SELECT ERROR_MESSAGE() AS ErrorMessage
    RAISERROR('Error in TRY, level 11', 11, 1)
    RETURN
END CATCH
GO

PRINT 'Test 2 - After GO'
GO

結果:

Start Test 1 - RAISERROR
Msg 50000, Level 11, State 1, Line 5
Error 1, level 11
Test 1 - After GO
Start Test 2 - Try/Catch
 CauseError
-----------

ErrorMessage
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Divide by zero error encountered.

Msg 50000, Level 11, State 1, Line 10
Error in TRY, level 11
Test 2 - After GO

これを機能させる唯一の方法は、GOステートメントなしでスクリプトを記述することです。時にはそれは簡単です。時にはそれはかなり難しいです。(のようなものを使用しますIF @error <> 0 BEGIN ...。)


CREATE PROCEDUREなどではそれはできません。解決策については私の答えを参照してください。
Blorgbeardは

Blogbeardのソリューションは素晴らしいです。私は何年もSQL Serverを使用してきましたが、これを目にしたのはこれが初めてです。
Rob Garrison

4

私はRETURN常にここで使用し、スクリプトまたはStored Procedure

ROLLBACKトランザクションがある場合はトランザクションを確認してください。そうでない場合は、RETURNすぐに未コミットのトランザクションが開かれます。


5
複数のバッチ(GOステートメント)を含むスクリプトでは機能しません。その方法については、私の回答を参照してください。
Blorgbeardは

1
RETURNは、ステートメントの現在のブロックを終了するだけです。IF ENDブロックにいる場合、実行はENDの後で続行されます。つまり、常にIF ENDブロックにいるため、RETURNを使用して、ある条件のテスト後に実行を終了することはできません。
cdonner 2012年


3

GOTOステートメントを使用できます。これを試して。これはあなたのためにいっぱいです。

WHILE(@N <= @Count)
BEGIN
    GOTO FinalStateMent;
END

FinalStatement:
     Select @CoumnName from TableName

「TRY..CATCH」はSQL Server 2008以降に導入され、2012
Eddie Kumar

1

答えはTHX!

raiserror()正常に動作しますが、returnステートメントを忘れないでください。そうしないと、スクリプトはエラーなしで続行します。(レイサーが「throwerror」ではないことを理解してください;-))そしてもちろん、必要に応じてロールバックを実行します!

raiserror() スクリプトを実行する人に何か問題があったことを伝えるのは良いことです。


1

Management Studioで単純にスクリプトを実行していて、最初のエラーで実行またはロールバックトランザクション(使用されている場合)を停止したい場合は、try catchブロック(SQL 2005以降)を使用するのが最善の方法です。スクリプトファイルを実行している場合、これはManagement Studioでうまく機能します。ストアドプロシージャは常にこれも使用できます。


1
あなたの回答は、60以上の賛成投票で受け入れられた回答に何を追加しますか?読んだ?このmetaSOの質問Jon Skeet:Coding Blogで正しい答えを与える方法を確認してください。
Yaroslav

0

当時、私たちは以下を使用しました...最もうまくいきました:

RAISERROR ('Error! Connection dead', 20, 127) WITH LOG

0

それをtry catchブロックで囲むと、実行がcatchに転送されます。

BEGIN TRY
    PRINT 'This will be printed'
    RAISERROR ('Custom Exception', 16, 1);
    PRINT 'This will not be printed'
END TRY
BEGIN CATCH
    PRINT 'This will be printed 2nd'
END CATCH;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.