มีวิธีหยุดการทำงานของสคริปต์ SQL ใน SQL server ทันทีเช่นคำสั่ง "break" หรือ "exit" หรือไม่?
ฉันมีสคริปต์ที่ทำการตรวจสอบความถูกต้องและการค้นหาก่อนที่จะเริ่มการแทรกและฉันต้องการให้สคริปต์หยุดหากการตรวจสอบความถูกต้องหรือการค้นหาล้มเหลว
มีวิธีหยุดการทำงานของสคริปต์ SQL ใน SQL server ทันทีเช่นคำสั่ง "break" หรือ "exit" หรือไม่?
ฉันมีสคริปต์ที่ทำการตรวจสอบความถูกต้องและการค้นหาก่อนที่จะเริ่มการแทรกและฉันต้องการให้สคริปต์หยุดหากการตรวจสอบความถูกต้องหรือการค้นหาล้มเหลว
คำตอบ:
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' ไม่ได้ถูกพิมพ์ออกมา
คำเตือน:
วิธี 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 เพื่อทำให้ส่วนแทรกมีเงื่อนไข
คุณไม่จำเป็นต้องตรวจสอบผลลัพธ์ที่เปลี่ยนแปลงได้ของการทดสอบการตรวจสอบความถูกต้องทุกครั้ง โดยปกติคุณสามารถทำสิ่งนี้ได้ด้วยตัวแปรแฟลกเดียวเพื่อยืนยันว่าทุกเงื่อนไขผ่านไปแล้ว:
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
แม้ว่าการตรวจสอบของคุณจะมีความซับซ้อนมากขึ้นคุณควรต้องการตัวแปรการตั้งค่าสถานะเพียงเล็กน้อยเพื่อรวมไว้ในการตรวจสอบขั้นสุดท้ายของคุณ
RAISERROR
โดยเฉพาะอย่างยิ่งถ้าคุณไม่รู้ว่าใครกำลังจะเรียกใช้สคริปต์และมีสิทธิ์ใดบ้าง
declare @i int = 0; if @i=0 begin select '1st stmt in IF block' go end else begin select 'ELSE here' end go
THROW 51000, 'Stopping execution because validation failed.', 0;
PRINT 'Still Executing'; -- This doesn't execute with THROW
จาก MSDN:
เพิ่มข้อยกเว้นและถ่ายโอนการดำเนินการไปยังบล็อก CATCH ของ TRY … CATCH สร้าง ... หากโครงสร้าง TRY … CATCH ไม่พร้อมใช้งานเซสชันจะสิ้นสุดลง หมายเลขบรรทัดและโพรซีเดอร์ที่ยกข้อยกเว้นขึ้นมา ความรุนแรงตั้งไว้ที่ 16
ฉันขยายโซลูชันเปิด / ปิด noexec สำเร็จด้วยธุรกรรมเพื่อเรียกใช้สคริปต์ในลักษณะทั้งหมดหรือไม่ก็เลย
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
เห็นได้ชัดว่าคอมไพเลอร์ "เข้าใจ" ตัวแปร @finished ใน IF แม้ว่าจะมีข้อผิดพลาดและการดำเนินการถูกปิดใช้งาน อย่างไรก็ตามค่าจะถูกตั้งค่าเป็น 1 เฉพาะในกรณีที่การดำเนินการไม่ได้ถูกปิดใช้งาน ดังนั้นฉันสามารถกระทำหรือย้อนกลับธุรกรรมตาม
IF (XACT_STATE()) <> 1 BEGIN Set NOCOUNT OFF ;THROW 525600, 'Rolling back transaction.', 1 ROLLBACK TRANSACTION; set noexec on END;
แต่การดำเนินการไม่เคยหยุดและฉันสิ้นสุดด้วยข้อผิดพลาด "ย้อนกลับธุรกรรม" สามข้อ ความคิดใด ๆ
คุณสามารถตัดคำสั่ง SQL ของคุณในขณะที่วนซ้ำและใช้ 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
DECLARE @ST INT; SET @ST = 1; WHILE @ST = 1; BEGIN; SET @ST = 0; ...; END
verbose เพิ่มเติม แต่ heck มันเป็น TSQL อยู่แล้ว ;-)
คุณสามารถเปลี่ยนลำดับการดำเนินการโดยใช้คำสั่งGOTO :
IF @ValidationResult = 0
BEGIN
PRINT 'Validation fault.'
GOTO EndScript
END
/* our code */
EndScript:
เพิ่มเติมวิธี Sglasses refinig เส้นข้างต้นบังคับใช้โหมด SQLCMD และทั้ง scirpt ทมิฬถ้าไม่ใช้โหมด 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-------------
นี่เป็นขั้นตอนการจัดเก็บหรือไม่ ถ้าเป็นเช่นนั้นฉันคิดว่าคุณสามารถทำ Return ได้เช่น "Return NULL";
ฉันขอแนะนำให้คุณใส่บล็อคโค้ดที่เหมาะสมในบล็อก catch ลอง จากนั้นคุณสามารถใช้เหตุการณ์ Raiserror ด้วยระดับความรุนแรง 11 เพื่อทำลายบล็อก catch ถ้าคุณต้องการ หากคุณเพียงแค่ต้องการผู้ก่อ แต่ดำเนินการต่อในการลองบล็อกแล้วใช้ความรุนแรงที่ต่ำกว่า
ทำให้รู้สึก?
ไชโยจอห์น
[แก้ไขเพื่อรวมการอ้างอิง BOL]
http://msdn.microsoft.com/en-us/library/ms175976(SQL.90).aspx
ไม่สามารถใช้งานได้กับคำสั่ง '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 ...
)
ฉันใช้ RETURN
ที่นี่ตลอดเวลาทำงานในสคริปต์หรือStored Procedure
ตรวจสอบให้แน่ใจว่าคุณROLLBACK
ทำธุรกรรมหากคุณเป็นหนึ่งในนั้นมิฉะนั้นRETURN
ทันทีจะส่งผลให้การทำธุรกรรมเปิดปราศจากข้อผูกมัด
นี่คือทางออกของฉัน:
...
BEGIN
raiserror('Invalid database', 15, 10)
rollback transaction
return
END
คุณสามารถใช้คำสั่ง GOTO ลองสิ่งนี้ นี่คือการใช้งานเต็มรูปแบบสำหรับคุณ
WHILE(@N <= @Count)
BEGIN
GOTO FinalStateMent;
END
FinalStatement:
Select @CoumnName from TableName
ขอบคุณสำหรับคำตอบ!
raiserror()
ทำงานได้ดี แต่คุณไม่ควรลืม return
คำสั่งมิฉะนั้นสคริปต์จะดำเนินต่อไปโดยไม่มีข้อผิดพลาด! (การเพิ่มเรนเดอร์ไม่ใช่ "นักเลง" ;-)) และแน่นอนว่าการทำโรลแบ็คถ้าจำเป็น!
raiserror()
ดีใจที่ได้บอกคนที่รันสคริปต์ว่ามีบางอย่างผิดปกติ
หากคุณเพียงแค่เรียกใช้งานสคริปต์ใน Management Studio และต้องการหยุดการดำเนินการหรือการย้อนกลับ (ถ้าใช้) ในข้อผิดพลาดครั้งแรกวิธีที่ดีที่สุดที่ฉันคาดไว้คือใช้ try catch block (SQL 2005 เป็นต้นไป) สิ่งนี้ทำงานได้ดีในสตูดิโอการจัดการหากคุณกำลังเรียกใช้งานไฟล์สคริปต์ Proc ที่เก็บไว้สามารถใช้สิ่งนี้ได้เช่นกัน
ย้อนกลับไปในวันที่เราใช้สิ่งต่อไปนี้ ... ทำงานได้ดีที่สุด:
RAISERROR ('Error! Connection dead', 20, 127) WITH LOG
ใส่ในบล็อก catch 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;