SQL Server-SQLスクリプトの実行の停止または中断
-
20-08-2019 - |
質問
<!> quot; break <!> quot;のように、SQLサーバーでSQLスクリプトの実行をすぐに停止する方法はありますか?または<!> quot; exit <!> quot;コマンド?
挿入を開始する前に検証とルックアップを行うスクリプトがあり、検証またはルックアップのいずれかが失敗した場合に停止したい。
解決
raiserror メソッド
raiserror('Oh no a fatal error', 20, -1) with log
これにより接続が終了し、残りのスクリプトの実行が停止します。
この方法で機能するには、重大度レベル20以上とWITH LOG
オプションの両方が必要であることに注意してください。
これは、たとえば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」ロール)としてログインしている場合にのみ機能し、データベース接続もありません。
- adminとしてログインしていない場合、RAISEERROR()呼び出し自体は失敗し、スクリプトは実行を継続します。
- sqlcmd.exeで起動すると、終了コード2745が報告されます。
noexecメソッド
GOステートメントで機能する別のメソッドは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.
他のヒント
RETURNを使用するだけです(ストアドプロシージャの内部と外部の両方で機能します)。
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は解析時間エラーさえも過ぎてしまうので、完全に防弾ではありません!それでも、コマンドラインからそれらを実行している場合、これは問題ありません。
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
検証がより複雑な場合でも、最終チェックに含めるフラグ変数はわずかです。
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
どうやらコンパイラ<!> quot;理解<!> quot;エラーが発生して実行が無効にされた場合でも、IFの@finished変数。ただし、実行が無効にされていない場合にのみ、値が1に設定されます。したがって、トランザクションを適切にコミットまたはロールバックできます。
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
SQL 2012+では、 THROW を使用できます。
THROW 51000, 'Stopping execution because validation failed.', 0;
PRINT 'Still Executing'; -- This doesn't execute with THROW
MSDNから:
例外を発生させ、TRY <!>#8230; CATCHコンストラクトのCATCHブロックに実行を転送します... TRY <!>#8230; CATCHコンストラクトが利用できない場合、セッションは終了します。例外が発生する行番号と手順が設定されます。重大度は16に設定されます。
これはストアドプロシージャですか?もしそうなら、<!> quot; Return NULL <!> quot ;;
のように、単にReturnを実行できると思います。 Sglassesメソッドをさらに改良し、上記の行はSQLCMDモードの使用を強制し、SQLCMDモードを使用しない場合はscirptを軽減するか、エラーで終了するために: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-------------
適切なコードブロックをtry catchブロックにラップすることをお勧めします。その後、必要に応じてcatchブロックにブレークするために、重大度11のRaiserrorイベントを使用できます。発生させたいだけで、tryブロック内で実行を継続する場合は、より低い重大度を使用します。
理にかなっていますか
乾杯、 ジョン
[BOL参照を含むように編集]
http://msdn.microsoft.com/en -us / library / ms175976(SQL.90).aspx
GOTO ステートメントを使用して、実行フローを変更できます。
IF @ValidationResult = 0
BEGIN
PRINT 'Validation fault.'
GOTO EndScript
END
/* our code */
EndScript:
RAISERROR を使用できます。
これらのいずれも「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 ...
のようなものを使用します。)
これは私の解決策でした:
...
BEGIN
raiserror('Invalid database', 15, 10)
rollback transaction
return
END
GOTOステートメントを使用できます。これを試して。これは完全に使用できます。
WHILE(@N <= @Count)
BEGIN
GOTO FinalStateMent;
END
FinalStatement:
Select @CoumnName from TableName
ここでは常にRETURN
を使用し、スクリプトで動作するか、Stored Procedure
トランザクションがある場合は必ずROLLBACK
を実行してください。そうでない場合は、すぐに<=>によってコミットされていないトランザクションが開かれます
答えに感謝!
raiserror()
は正常に動作しますが、return
ステートメントを忘れないでください。そうしないと、スクリプトはエラーなしで続行します。 (つまり、レイザーは<!> quot; throwerror <!> quot;ではありません;;))もちろん、必要に応じてロールバックを行います!
<=>は、スクリプトの実行者に何か問題が発生したことを伝えるのに便利です。
Management Studioで単にスクリプトを実行していて、最初のエラーで実行またはロールバックトランザクション(使用している場合)を停止する場合、私が考える最善の方法はtry catchブロック(SQL 2005以降)を使用することです。 スクリプトファイルを実行している場合、これはManagement Studioでうまく機能します。 ストアドプロシージャでも常にこれを使用できます。
以前は、次のものを使用していました...最高に機能しました:
RAISERROR ('Error! Connection dead', 20, 127) WITH LOG