Cách ngắt thực thi tập lệnh SQL


16

Tôi đang làm việc trên kịch bản sql và tôi có yêu cầu ngừng tiếp tục kịch bản nếu một số điều kiện không được thỏa mãn.

Khi tôi Google nó, tôi thấy RaisError với mức độ nghiêm trọng 20 sẽ chấm dứt nó. Nhưng vì một số lý do tôi không thể sử dụng tùy chọn đó.

Xin vui lòng cung cấp cho tôi các lựa chọn thay thế có thể để dừng thực thi tập lệnh SQL.


1
Tại sao nâng một lỗi không thể chấp nhận? Ngoài ra kịch bản này là một thủ tục lưu trữ?
Namphibian

Tôi không hiểu rõ câu hỏi nắm tay của bạn. Đối với câu hỏi thứ hai; không, đây không phải là SP
Nhà phát triển mới

1
Kịch bản là gì? Nó bao gồm nhiều lô? Bạn đã thấy câu trả lời ở đây?
Martin Smith

Câu trả lời:


8

Từ tài liệu RAISERROR (nhấn mạnh của tôi):

Mức độ nghiêm trọng từ 0 đến 18 có thể được chỉ định bởi bất kỳ người dùng nào. Mức độ nghiêm trọng từ 19 đến 25 chỉ có thể được chỉ định bởi các thành viên của vai trò máy chủ cố định sysadmin hoặc người dùng có quyền ALTER TRACE. Đối với các mức độ nghiêm trọng từ 19 đến 25, tùy chọn VỚI LOG là bắt buộc.

Rất có khả năng hiệu trưởng bạn đang thực thi tập lệnh vì không đáp ứng các tiêu chí này.

Không có gì sai khi sử dụng RAISERROR; bạn chỉ đang sử dụng một mức độ nghiêm trọng quá mức. Tôi sử dụng cấp 16 làm mặc định cho một lỗi được nêu ra và chuỗi sẽ bị chấm dứt. Nếu bạn muốn chính xác hơn, bạn có thể làm theo các mức do chính Microsoft đưa ra:

nhập mô tả hình ảnh ở đây

Bây giờ, đã nói tất cả những điều đó, tùy thuộc vào ngữ cảnh của tập lệnh, sử dụng RAISERRORcó thể là không đủ, vì nó không tự "thoát" tập lệnh (sử dụng mức độ nghiêm trọng thông thường).

Ví dụ:

RAISERROR(N'Test', 16, 1);

SELECT 1;   /* Executed! */

Điều này sẽ vừa nâng cao một lỗi trả về một tập kết quả.

Để chấm dứt tập lệnh ngay lập tức, tôi thích sử dụng RETURN(sử dụng các GOTOcấu trúc -type thường không được khuyến khích trong hầu hết các vòng lập trình nơi có các lựa chọn thay thế):

RAISERROR(N'Test', 16, 1);
RETURN;

SELECT 1;   /* Not executed */

Hoặc xử lý lỗi bằng cách sử dụng TRY/CATCH, điều này sẽ khiến việc thực thi nhảy sang CATCHkhối nếu mức độ nghiêm trọng là 11 hoặc cao hơn:

BEGIN TRY
    RAISERROR(N'Test', 16, 1);
    SELECT 1;   /* Not executed */
END TRY
BEGIN CATCH
    SELECT 2;   /* Executed */
END CATCH

BEGIN TRY
    RAISERROR(N'Test', 10, 1);
    SELECT 1;   /* Executed */
END TRY
BEGIN CATCH
    SELECT 2;   /* Not executed */
END CATCH

Một vấn đề khác là nếu tập lệnh kéo dài nhiều đợt - RETURNsẽ chỉ thoát khỏi :

RAISERROR(N'Test', 16, 1);
RETURN;

SELECT 1;   /* Not executed */
GO

SELECT 2;   /* Executed! */

Để khắc phục điều này, bạn có thể kiểm tra @@ERRORtại đầu mỗi đợt:

RAISERROR(N'Test', 16, 1);
RETURN;

SELECT 1;   /* Not executed */
GO

IF (@@ERROR != 0)
    RETURN;

SELECT 2;   /* Not executed */

Chỉnh sửa: Như Martin Smith đã chỉ ra chính xác trong các bình luận, điều này chỉ hoạt động trong 2 đợt. Để mở rộng lên 3 đợt trở lên, bạn có thể xếp tầng tăng các lỗi như vậy (lưu ý: GOTOphương pháp không giải quyết được vấn đề này vì nhãn đích phải được xác định trong lô):

RAISERROR(N'Test', 16, 1);
RETURN;

SELECT 1;   /* Not executed */
GO

IF (@@ERROR != 0)
BEGIN
    RAISERROR(N'Error already raised. See previous errors.', 16, 1);
    RETURN;
END

SELECT 2;   /* Not executed */
GO

IF (@@ERROR != 0)
BEGIN
    RAISERROR(N'Error already raised. See previous errors.', 16, 1);
    RETURN;
END

SELECT 3;   /* Not executed */

Hoặc, như ông cũng chỉ ra, bạn có thể sử dụng SQLCMDphương pháp nếu phù hợp với môi trường của bạn.


Đó là đề nghị cuối cùng không hoạt động. Xem pastebin . Tôi thích phương pháp sqlcmd ở đây
Martin Smith

6

Bạn có thể sử dụng các GOTOtuyên bố để bỏ qua xung quanh bất cứ nơi nào bạn muốn. Nói cách khác, bạn gặp phải một lỗi hoặc một số điều kiện khác và bạn có thể có một nhãn ở dưới cùng của tập lệnh (tức là TheEndOfTheScript:) và chỉ cần đưa ra một goto TheEndOfTheScript;tuyên bố.

Đây là một mẫu nhanh:

print 'here is the first statement...';

print 'here is the second statement...';

-- substitute whatever conditional flow determining factor
-- you'd like here. I have chosen a dummy statement that will
-- always return true
--
if (1 = 1)
    goto TheEndOfTheScript;

print 'here is the third statement...';

print 'here is the fourth statement...';


TheEndOfTheScript:
print 'here is the end of the script...';

Đầu ra của việc thực hiện này sẽ như sau:

here is the first statement...
here is the second statement...
here is the end of the script...

Như bạn có thể thấy, GOTOđã bỏ qua việc in các câu lệnh thứ ba và thứ tư và nhảy ngay đến nhãn ( TheEndOfTheScript).


7
Chỉ hoạt động khi có một đợt duy nhất, sẽ bị hỏng ngay khi bạn có câu lệnh GO.
Gabriel


0

Đồng ý với SET NOEXEC ON/OFF, tuy nhiên trong Procs được lưu trữ (chứa một khối) tôi chỉ đơn giản sử dụng RETURNcâu lệnh.

Hãy cẩn thận: Trong tệp tập lệnh, nếu bạn có nhiều GOcâu lệnh, thì RETURNsẽ chỉ ra khỏi khối hiện tại và tiếp tục với khối / đợt tiếp theo.

Lưu ý: GOTOđược cho là một cách thực hành mã hóa xấu, nên sử dụng " TRY..CATCH", vì nó được giới thiệu kể từ SQL Server 2008, tiếp theo là THROWvào năm 2012.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.