SQL Server - SQL komut dosyasının yürütülmesini durdurma veya durdurma


325

SQL sunucusunda "break" veya "exit" komutu gibi bir SQL komut dosyasının yürütülmesini derhal durdurmanın bir yolu var mı?

Ekler yapmaya başlamadan önce bazı doğrulama ve aramalar yapan bir komut dosyası var ve doğrulama veya aramalardan herhangi biri başarısız olursa durdurmak istiyorum.

Yanıtlar:


371

raiserror yöntemi

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

Bu bağlantıyı sonlandıracak ve betiğin geri kalanının çalışmasını durduracaktır.

WITH LOGBu şekilde çalışması için hem şiddet seviyesi 20 veya daha yüksek hem de seçeneğin gerekli olduğunu unutmayın.

Bu, GO ifadeleriyle bile çalışır, örn.

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

Çıktı verecek:

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' yazılmadığına dikkat edin.

YETERSİZLİKLER:

  • Bu yalnızca admin ('sysadmin' rolü) olarak oturum açtıysanız ve sizi veritabanı bağlantısı olmadan bırakırsanız çalışır.
  • Yönetici olarak oturum açmadıysanız, RAISEERROR () çağrısının kendisi başarısız olur ve komut dosyası yürütülmeye devam eder .
  • Sqlcmd.exe ile çağrıldığında, çıkış kodu 2745 rapor edilecektir.

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

Noexec yöntemi

GO ifadeleriyle çalışan başka bir yöntem set noexec on. Bu, komut dosyasının geri kalanının atlanmasına neden olur. Bağlantıyı sonlandırmaz, ancak noexecherhangi bir komut yürütülmeden önce tekrar kapatmanız gerekir .

Misal:

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
Bu harika! Bu biraz "büyük bir sopa" yaklaşım, ama gerçekten ihtiyacınız olan zamanlar vardır. Hem şiddeti 20 (ya da daha yüksek) hem de "LOG" (LOG (LOG) İLE) gerektirir.
Rob Garrison

5
Noexec yöntemiyle komut dosyasının geri kalanının hala yorumlandığını unutmayın, bu nedenle sütun yok gibi derleme zamanı hataları almaya devam edersiniz. Koşullu olarak, bazı kodları atlayarak eksik sütunları içeren bilinen şema değişiklikleriyle uğraşmak istiyorsanız, bunu yapmanın tek yolu harici dosyalara başvurmak için sqlcommand modunda r kullanmaktır.
David Eison

20
Noexec şey harika. Çok teşekkürler!
Gaspa79

2
"Bu bağlantıyı kesecek" - öyle görünmüyor, en azından ben görüyorum.
jcollum

6
Bu yöntemi
deniyordum

187

Sadece bir RETURN kullanın (saklı yordamın içinde ve dışında çalışacaktır).


2
Bir nedenden dolayı, dönüşün senaryolarda işe yaramadığını düşünüyordum, ama sadece denedim ve işe yarıyor! Teşekkürler
Andy White

4
Bir komut dosyasında, saklı yordamdaki gibi bir değere sahip bir RETURN yapamazsınız, ancak bir RETURN yapabilirsiniz.
Rob Garrison

53
Hayır sadece bir sonraki
GO'ya

2
bir sonraki GO'dan sonra da devam edeceğini varsaymak tehlikeli.
Justin

1
GO bir kod sonlandırıcı veya sınırlayıcıdır; SQL kodu değil. GO, veritabanı motoruna GO sınırlayıcıdan sonra yeni bir komut dosyasının başlatıldığını komut göndermek için kullandığınız istemciye yönelik bir talimattır.
Ters Mühendis

50

SQLCMD modunu kullanabiliyorsanız, büyütme

:on error exit

(İki nokta üst üste DAHİL) RAISERROR'un komut dosyasını gerçekten durdurmasına neden olur. Örneğin,

: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'

çıktı olacak:

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

ve parti duracaktır. SQLCMD modu açık değilse, iki nokta üst üste hakkında ayrıştırma hatası alırsınız. Ne yazık ki, komut dosyası SQLCMD modunda olmadan çalıştırılıyormuş gibi tamamen kurşun geçirmez değil, SQL Managment Studio ayrıştırılmış zaman hatalarını bile geçiyor! Yine de, bunları komut satırından çalıştırıyorsanız, sorun değil.


4
Harika yorum, teşekkürler. SSMS SQLCmd modunda Sorgu menüsü altında geçiş olduğunu ekleyeceğim.
David Peters

bu yararlı - çalışırken -b seçeneğine ihtiyacınız olmadığı anlamına gelir
JonnyRaa

2
sonra büyüyü ... ama Magic Missle'ı nasıl kullanırım ?!
JJS

1
mükemmel. sysadmin ultra ekstra kullanıcı hakları gerektirmez
Pac0

21

Ben RAISERROR kullanmazdım-SQL bu amaçla kullanılabilecek IF deyimleri vardır. Doğrulamanızı ve aramalarınızı yapın ve yerel değişkenleri ayarlayın, ardından ekleri koşullu hale getirmek için IF ifadelerindeki değişkenlerin değerini kullanın.

Her doğrulama testinin değişken bir sonucunu kontrol etmenize gerek yoktur. Geçtiğimiz tüm koşulları onaylamak için bunu genellikle yalnızca bir bayrak değişkeniyle yapabilirsiniz:

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

Doğrulamanız daha karmaşık olsa bile, son kontrollerinize dahil etmek için yalnızca birkaç bayrak değişkenine ihtiyacınız vardır.


Evet, komut dosyasının diğer bölümlerinde IFs kullanıyorum, ancak bir ekleme yapmaya çalışmadan önce her yerel değişkeni kontrol etmek istemiyorum. Ben sadece tüm script durdurmak ve kullanıcıyı girişleri kontrol etmeye zorlamak istiyorum. (Bu sadece hızlı ve kirli bir senaryo)
Andy White

4
Bu cevabın teknik olarak doğru olduğu için neden işaretlendiğinden emin değilim, sadece posterin "yapmak istediği" şey değil.
John Sansom

Begin..End içinde birden fazla blok olması mümkün mü? Anlamı STATEMENT; GİT; BEYAN; GİT; vs vs? Hata alıyorum ve bunun nedeni de olabilir.
Nenotlep

3
Bu RAISERROR, özellikle komut dosyalarını kimin çalıştıracağını ve hangi ayrıcalıklarla çalışacağını bilmiyorsanız çok daha güvenilirdir .
Cypher

@John Sansom: Burada gördüğüm tek sorun, bir GO ifadesi üzerinde dallamaya çalışıyorsanız IF ifadesinin çalışmadığıdır. Komut dosyalarınız GO deyimlerine (ör. DDL deyimleri) bağlıysa, bu büyük bir sorundur. İşte ilk git ifadesi olmadan çalışan bir örnek :declare @i int = 0; if @i=0 begin select '1st stmt in IF block' go end else begin select 'ELSE here' end go
James Jensen

16

SQL 2012+, sen kullanabilirsiniz atmak .

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

MSDN'den:

Bir istisna oluşturur ve yürütmeyi bir TL… CATCH yapısının bir CATCH bloğuna aktarır ... Bir TL… CATCH yapısı mevcut değilse, oturum sonlandırılır. Kural dışı durumun oluştuğu satır numarası ve yordamı belirlenir. Şiddet 16'ya ayarlanmıştır.


1
THROW, RAISERROR'un yerini alacaktır, ancak aynı komut dosyasında sonraki toplu işlemleri engelleyemezsiniz.
NReilingh

Doğru @NReilingh. Blorgbeard'ın cevabı gerçekten tek çözüm budur. Yine de sysadmin gerektirir (önem seviyesi 20) ve senaryoda birden fazla parti yoksa oldukça ağırdır.
Jordan Parker

2
geçerli işlemeyi de iptal etmek istiyorsanız xact abort'u açık olarak ayarlayın.
nurettin

13

Komut dosyasını bir ya da hiçbir şekilde çalıştırmak için bir işlemle noexec açma / kapama çözümünü başarıyla genişlettim.

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

Görünüşe göre derleyici, bir hata olsa ve yürütme devre dışı bırakılmış olsa bile, IF'deki @finished değişkenini "anlar". Ancak, değer yalnızca yürütme devre dışı bırakılmamışsa 1 olarak ayarlanır. Bu nedenle işlemi buna göre güzel bir şekilde yerine getirebilir veya geri alabilirim.


Ben anlamıyorum. Talimatları takip ettim. Her GO'dan sonra aşağıdaki SQL'i girdim. IF (XACT_STATE()) <> 1 BEGIN Set NOCOUNT OFF ;THROW 525600, 'Rolling back transaction.', 1 ROLLBACK TRANSACTION; set noexec on END; Ancak yürütme hiç durmadı ve üç "İşlemin Geri Alınması" hatasıyla sonuçlandım. Herhangi bir fikir?
user1161391

12

SQL ifadenizi bir WHILE döngüsüne sarabilir ve gerekirse BREAK kullanabilirsiniz

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
Bunun gibi görünüyor, hatayı yükseltmekten biraz daha hoş görünüyor. Kesinlikle sonunda mola unutmak istemiyorum!
Andy White

1
Ayrıca bir değişken kullanabilir ve "bölünmeyi" önlemek için hemen döngünün en üstüne ayarlayabilirsiniz. DECLARE @ST INT; SET @ST = 1; WHILE @ST = 1; BEGIN; SET @ST = 0; ...; ENDDaha ayrıntılı, ama heck, yine de TSQL ;-)

Bazı insanlar gotoyu böyle yaparlar, ancak takip etmek gitmekten daha kafa karıştırıcıdır.
nurettin

Bu yaklaşım beklenmedik bir ara GO'dan korur. Takdir eden.
it3xl

10

GOTO ifadelerini kullanarak yürütme akışını değiştirebilirsiniz :

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

/* our code */

EndScript:

2
goto kullanmak istisnayı ele almak için kabul edilebilir bir yoldur. Değişken ve iç içe yerleştirme miktarını azaltır ve bağlantı kesilmesine neden olmaz. Muhtemelen SQL Server komut dosyalarının izin verdiği arkaik istisna işleme tercih edilir.
Antonio Drusin

Buradaki diğer önerilerin TÜMÜ gibi, "kodumuz" bir "GO" ifadesi içeriyorsa bu çalışmaz.
Mike Gledhill

9

Sglasses yönteminin daha da hassaslaştırılması, yukarıdaki satırlar SQLCMD modunun kullanımını zorlar ve SQLCMD modunu kullanmazsa yamultmayı başlatır veya durumu izlemek :on error exitiçin herhangi bir hatadan çıkmak için
CONTEXT_INFO kullanılır.

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'nin senaryoyu iptal edememesinin etrafında çalışmanın tek yolu budur. Ama başlangıçta 'SET NOEXEC OFF' ve SQLCMD modunda değilse 'SET NOEXEC ON' i ekledim, aksi takdirde günlük ile 20. seviyedeki bir hatayı artırmazsanız gerçek komut dosyası çalışmaya devam edecektir.
Mark Sowul

8

Bu saklı bir prosedür mü? Eğer öyleyse, "Return NULL" gibi bir Dönüş yapabileceğinizi düşünüyorum;


Cevabınız için teşekkürler, bunu bilmek güzel, ancak bu durumda depolanmış bir proc değil, sadece bir senaryo dosyası
Andy White

1
@ Gordon Her zaman değil (burada arıyorum). Diğer cevapları görün (GO bir şey için
harekete geçiyor

6

Bir try catch bloğunda uygun kod bloğunuzu sarmanızı öneririm. Daha sonra, isterseniz catch bloğunu kırmak için Raiserror olayını 11 şiddette kullanabilirsiniz. Sadece rarorrorror'ları yükseltmek istiyorsanız, ancak try bloğu içinde yürütmeye devam etmek istiyorsanız, daha düşük bir önem derecesi kullanın.

Mantıklı olmak?

Şerefe John

[BOL Referansını içerecek şekilde düzenlendi]

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


Hiç SQL'de bir deneme yakalama görmedim - ne demek istediğinize hızlı bir örnek gönderir misiniz?
Andy White

2
2005 için yeni. BEYİN {sql_statement TRY | ekstre_block} SON DENEYİN CATCH {sql_statement | bildirimi_block} END CATCH [; ]
Sam

@Andy: Referans eklendi, örnek dahil.
John Sansom

2
TRY-CATCH bloğu GO'nun kendi içinde olmasına izin vermez.
AntonK

4

RAISERROR'u kullanabilirsiniz .


3
Bu, önlenebilir bir hatayı (burada referans doğrulamadan bahsettiğimizi varsayarak) belirtmek için hiçbir anlam ifade etmiyor.
Dave Swersky

2
raiserror, düşük önem ayarına sahip bilgi mesajı olarak kullanılabilir.
Mladen Prajdic

2
Komut dosyası, kabul edilen yanıtta belirtilen belirli koşullar karşılanmadığı sürece devam edecektir.
Eric J.

4

Bunların hiçbiri 'GO' ifadeleriyle çalışmaz. Bu kodda, önem derecesi 10 veya 11 olsun, son PRINT deyimini alırsınız.

Test Betiği:

-- =================================
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

Sonuçlar:

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

Bu işi yapmanın tek yolu komut dosyasını deyimsiz yazmaktır GO. Bazen bu kolay. Bazen oldukça zor. (Gibi bir şey kullanın IF @error <> 0 BEGIN ....)


CREATE PROCEDURE vb. İle bunu yapamam. Bir çözüm için cevabım bakın.
Blorgbeard

Blogbeard'ın çözümü harika. SQL Server ile yıllardır çalışıyorum ve bu ilk kez gördüm.
Rob Garrison

4

Kullandığım RETURNBurada her zaman, komut çalışır veyaStored Procedure

İşlemden birindeyseniz işlem ROLLBACKyaptığınızdan emin olun , aksi takdirde RETURNderhal açık ve açık bir işlemle sonuçlanır


5
Birden çok toplu iş içeren bir komut dosyasıyla çalışmaz (GO deyimleri) - bunun nasıl yapılacağı ile ilgili cevabımı görün.
Blorgbeard

1
RETURN yalnızca geçerli ifade bloğundan çıkar. Bir IF END bloğundaysanız, yürütme END'den sonra devam edecektir. Bu, bir koşulu test ettikten sonra yürütmeyi sonlandırmak için RETURN'u kullanamayacağınız anlamına gelir, çünkü her zaman IF END bloğunda olacaksınız.
cdonner

3

Bu benim çözümümdü:

...

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

3

GOTO ifadesini kullanabilirsiniz. Bunu dene. Bu tam sizin için kullanın.

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

FinalStatement:
     Select @CoumnName from TableName

GOTO'nun kötü bir kodlama uygulaması olması gerekiyordu, SQL Server 2008'den bu yana tanıtıldığı için 2012'de THROW ve ardından 2012'de THROW ile birlikte "TRY..CATCH" kullanılması önerilir.
Eddie Kumar

1

Cevap için teşekkürler!

raiserror()iyi çalışıyor ama returndeyimi unutmamalısınız aksi takdirde komut dosyası hatasız devam eder! (hense raiserror bir "atıcı" değil ;-)) ve elbette gerekirse geri alma yapıyor!

raiserror() senaryoyu yürüten kişiye bir şeylerin yanlış gittiğini söylemek güzel.


1

Management Studio'da bir komut dosyası yürütüyorsanız ve ilk hatada yürütme veya geri alma işlemini (kullanılıyorsa) durdurmak istiyorsanız, hesaba katmanın en iyi yolu try catch bloğunu (SQL 2005 sonrası) kullanmaktır. Bir komut dosyası yürütüyorsanız, bu, Management Studio'da iyi çalışır. Saklanan proc her zaman bunu kullanabilir.


1
60+ upvotes ile cevabınız kabul edilen cevaba ne katar? Onu okudun mu? Bu metaSO sorusunu ve doğru cevabı nasıl vereceğiniz konusunda Jon Skeet: Kodlama Blogunu kontrol edin.
Yaroslav

0

Aşağıdakileri kullandığımız güne ... en iyi şekilde çalıştı:

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

0

Bir try catch bloğu içine alın, sonra yürütme yakalamak için aktarılacaktır.

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;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.