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'는 인쇄되지 않습니다.

주의 사항 :

  • 이것은 관리자 ( 'sysadmin'역할)로 로그인 한 경우에만 작동하며 데이터베이스 연결이없는 상태로 유지됩니다.
  • admin으로 로그인하지 않으면 RAISEERROR () 호출 자체가 실패 하고 스크립트는 계속 실행 됩니다.
  • sqlcmd.exe로 호출하면 종료 코드 2745가보고됩니다.

참조 : http://www.mydatabasesupport.com/forums/ms-sqlserver/174037-sql-server-2000-abort-whole-script.html#post761334

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.

14
대단해! 약간의 "큰 스틱"접근 방식이지만, 실제로 필요할 때가 있습니다. 심각도 20 이상과 "WITH LOG"가 모두 필요합니다.
Rob Garrison

5
noexec 메소드를 사용하면 스크립트의 나머지 부분이 여전히 해석되므로 열이 존재하지 않는 등의 컴파일 타임 오류가 계속 발생합니다. 일부 코드를 건너 뛰고 누락 된 열과 관련된 알려진 스키마 변경 사항을 조건부로 처리하려면 내가 아는 유일한 방법은 sqlcommand 모드에서 : r을 사용하여 외부 파일을 참조하는 것입니다.
David Eison

20
noexec 것은 훌륭합니다. 고마워요!
Gaspa79

2
"이것은 연결을 종료 할 것입니다."-적어도 그것이보고있는 것이 아닌 것 같습니다.
jcollum

6
나는이 방법을 시도했지만 내가 깨달았을 때 올바른 결과를 얻지 못했다 ... raiserror에 하나의 E만이있다 ...
bobkingof12vs

187

RETURN을 사용하십시오 (저장 프로 시저 내부 및 외부에서 모두 작동 함).


2
어떤 이유로 든 스크립트에서 return이 작동하지 않는다고 생각했지만 방금 시도했지만 작동했습니다! 감사합니다
Andy White

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 모드에 있지 않고 실행되는 것처럼 완전히 방탄하지는 않습니다. 여전히 명령 행에서 실행하고 있다면 괜찮습니다.


4
좋은 의견 감사합니다. SSMS SQLCmd 모드에서 쿼리 메뉴 아래를 토글한다고 덧붙입니다.
David Peters

이것은 유용하다-당신이 실행할 때 -b 옵션이 필요 없다는 것을 의미한다
JonnyRaa

2
그 다음에 주술 ...하지만 매직 미사일을 어떻게 시전합니까?!
JJS

1
완전한. sysadmin ultra extra user 권한이 필요하지 않습니다
Pac0

21

RAISERROR- SQL을 사용하지 않습니다. SQL에는이 목적으로 사용할 수있는 IF 문이 있습니다. 유효성 검사 및 조회를 수행하고 로컬 변수를 설정 한 다음 IF 문에서 변수 값을 사용하여 삽입을 조건부로 만듭니다.

모든 유효성 검사 테스트의 변수 결과를 확인할 필요는 없습니다. 일반적으로 하나의 플래그 변수 만 사용하여 전달 된 모든 조건을 확인할 수 있습니다.

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를 사용하고 있지만 삽입을 시도하기 전에 모든 로컬 변수를 확인하고 싶지는 않습니다. 오히려 전체 스크립트를 중지하고 사용자가 입력을 확인하도록 강요하고 싶습니다. (이것은 단지 빠르고 더러운 스크립트입니다)
Andy White

4
이 답변이 왜 포스터 "원하는"것이 아니라 기술적으로 정확하기 때문에 왜이 답변이 표시되어 있는지 잘 모르겠습니다.
John Sansom

Begin..End 내에 여러 블록을 가질 수 있습니까? 의미 진술; 가다; 성명서; 가다; 등? 오류가 발생하여 그 이유 일 수 있습니다.
Nenotlep

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

@NReilingh를 수정하십시오. 바로 Blorgbeard의 답변이 유일한 해결책입니다. 그래도 sysadmin (심각도 레벨 20)이 필요하며 스크립트에 여러 개의 배치가 없으면 상당히 무겁습니다.
Jordan Parker

2
현재 변환도 취소하려면 xact 중단을 설정하십시오.
nurettin

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; 그러나 실행이 멈추지 않았고 결국 "롤링 백 트랜잭션"오류가 발생했습니다. 어떤 아이디어?
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
나는 이런 모습을 좋아합니다. 오류를 일으키는 것보다 조금 더 좋습니다. 끝에서 휴식을 잊고 싶지 않습니다!
Andy White

1
"분할"을 피하기 위해 변수를 사용하고 즉시 루프 맨 위에 설정할 수도 있습니다. DECLARE @ST INT; SET @ST = 1; WHILE @ST = 1; BEGIN; SET @ST = 0; ...; END더 장황하지만 도대체 그것은 어쨌든 TSQL입니다 ;-)

이것은 어떤 사람들이 goto를 수행하는 방법이지만 goto보다 따르기가 더 혼란 스럽습니다.
nurettin

이 접근 방식은 예기치 않은 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

추가 refinig 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-------------

2
이것이 스크립트를 중단 할 수 없다는 SSMS 미치광이를 해결하는 유일한 방법입니다. 그러나 처음에는 'SET NOEXEC OFF'를 추가하고 SQLCMD 모드가 아닌 경우 'SET NOEXEC ON'을 추가했습니다. 그렇지 않으면 로그로 레벨 20에서 오류가 발생하지 않으면 실제 스크립트가 계속 진행됩니다.
Mark Sowul

8

저장 프로 시저입니까? 그렇다면 "Return NULL"과 같은 Return 만 할 수 있다고 생각합니다.


답을 주셔서 감사합니다. 알아두면 좋지만,이 경우에는 저장 프로 시저가 아니며 스크립트 파일 일뿐입니다.
Andy White

1
@ 고든 항상 그런 것은 아닙니다 (여기서는 검색 중입니다). 다른 답변보기 (GO가 한 가지로
넘어 감

6

적절한 코드 블록을 try catch 블록으로 감싸는 것이 좋습니다. 그런 다음 원하는 경우 캐치 블록을 해제하기 위해 심각도 11 인 Raiserror 이벤트를 사용할 수 있습니다. 에러를 발생시키고 try 블록 내에서 계속 실행하려면 심각도를 낮추십시오.

말이 되나요?

건배, 존

[BOL 참조를 포함하도록 편집]

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


SQL에서 try-catch를 본 적이 없습니다. 무슨 뜻인지에 대한 간단한 예를 게시 하시겠습니까?
Andy White

2
2005 년에 처음 사용되었습니다. BEGIN TRY {sql_statement | statement_block} 시작 시도 캐치 {sql_statement | statement_block} END CATCH [; ]
Sam

@Andy : 참조 추가, 예제 포함
John Sansom

2
TRY-CATCH 블록은 자체적으로 GO를 허용하지 않습니다.
AntonK

4

RAISERROR 를 사용할 수 있습니다 .


3
이는 삽입이 발생하기 전에 유효성 검사가 가능한 경우 피할 수없는 오류를 발생시키는 것은 의미가 없습니다 (여기서 참조 유효성 검사에 대해 이야기하고 있다고 가정).
Dave Swersky

2
raiserror는 심각도가 낮은 정보 메시지로 사용할 수 있습니다.
Mladen Prajdic 2012 년

2
허용 된 답변에 명시된 특정 조건이 충족되지 않으면 스크립트가 계속 진행됩니다.
Eric 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

3

이것은 내 해결책이었습니다.

...

BEGIN
    raiserror('Invalid database', 15, 10)
    rollback transaction
    return
END

3

GOTO 문을 사용할 수 있습니다. 이 시도. 이것은 당신을 위해 전체 사용입니다.

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

FinalStatement:
     Select @CoumnName from TableName

이 SQL 서버 2008 도입으로 GOTO는 2012 년 THROW 다음, 권장 연습을 코딩 나쁜 "에서는 try..catch"의 사용 있어야하는데
에디 쿠마

1

답변을위한 Thx!

raiserror()잘 작동하지만 return문장을 잊지 않아야합니다 . 그렇지 않으면 스크립트가 오류없이 계속됩니다! (건조 오류는 "throwerror"가 아닙니다 ;-)) 물론 필요한 경우 롤백을합니다!

raiserror() 스크립트를 실행하는 사람에게 문제가 있음을 알리는 것이 좋습니다.


1

Management Studio에서 단순히 스크립트를 실행 중이고 첫 번째 오류에서 실행 또는 롤백 트랜잭션 (사용 된 경우)을 중지하려면 try catch 블록 (SQL 2005 이상)을 사용하는 것이 가장 좋습니다. 스크립트 파일을 실행하는 경우 Management Studio에서 잘 작동합니다. 저장된 proc은 항상 이것을 사용할 수 있습니다.


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.