2010-12-29 29 views
11

Tôi đang sử dụng SQL Server 2008 và khi tôi chạy bản Tuyên Bố này trong studio Management Select tuyên bố trong khối catch được thực hiện như mong đợiĐiều gì là sai với Try Catch của tôi trong T-SQL?

BEGIN TRY 
INSERT INTO IDontExist(ProductID) 
VALUES(1) 
END TRY 
BEGIN CATCH 
SELECT 'There was an error! ' + ERROR_MESSAGE() 
END CATCH 

Tuy nhiên khi tôi chạy tuyên bố này tuyên bố trong khối catch không bao giờ được thực thi và thay vào đó các lỗi được chỉ hiển thị trong kết quả tab

BEGIN TRY 
    Select * from IDontExist 
END TRY 
BEGIN CATCH 
    SELECT 'There was an error! ' + ERROR_MESSAGE() 
END CATCH 

cả hai đều trả về số lỗi tương tự '208' 'không hợp lệ Object Name: IDontExist' vậy tại sao người ta sẽ được xử lý và người kia không?

+0

Bất kỳ phản hồi nào về câu trả lời của chúng tôi, vui lòng? Tôi khá tò mò về cách bạn nhận được ... – gbn

+0

Câu hỏi muộn: nếu bạn chạy DBCC FREEPROCCACHE để buộc biên dịch lại – gbn

Trả lời

5

Điều này đã cắn tôi trong quá khứ.

Không phải tất cả các lỗi được tạo bên trong các câu lệnh khối TRY đều được chuyển vào khối CATCH. Bất kỳ lỗi nào có mức độ nghiêm trọng là 10 hoặc ít hơn được coi là cảnh báo và không kiểm soát lưu lượng vào khối CATCH. Ngoài ra, bất kỳ lỗi nào phá vỡ kết nối cơ sở dữ liệu sẽ không gây ra khối CATCH. Cũng có thể có những tình huống khác.

+0

+1 Tốt đẹp! Mọi thứ bắt đầu có ý nghĩa bây giờ ... –

+3

Điều này liên quan đến vụ kiện của OP như thế nào? Lỗi 208 có mức độ nghiêm trọng 16 và không chấm dứt kết nối. –

+0

..yes, và đó là lý do tại sao đôi khi giải pháp tốt để lưu trữ lỗi trong bảng @var nên giữ giá trị lỗi có sẵn trong khối CATCH – Milan

3

Trực tiếp từ http://msdn.microsoft.com/en-us/library/ms175976.aspx.

USE AdventureWorks2008R2; 
GO 

BEGIN TRY 
    -- Table does not exist; object name resolution 
    -- error not caught. 
    SELECT * FROM NonexistentTable; 
END TRY 
BEGIN CATCH 
    SELECT 
     ERROR_NUMBER() AS ErrorNumber 
     ,ERROR_MESSAGE() AS ErrorMessage; 
END CATCH 

Lỗi này không bị bắt và kiểm soát được chuyển ra khỏi TRY ... CATCH xây dựng lên cấp cao hơn tiếp theo.

Chạy câu lệnh SELECT bên trong quy trình được lưu trữ sẽ khiến lỗi xảy ra ở mức thấp hơn khối TRY. Lỗi sẽ được xử lý bởi cấu trúc TRY… CATCH.

5

Tôi hoàn toàn không nhận được khối CATCH.

Đó là vì mã sẽ không biên dịch, vì đối tượng không tồn tại, không có kế hoạch nào được tạo nên không có gì chạy để đạt khối CATCH.

Bạn có thể không bao giờ nhấn khối catch này vì vậy somethign là sai với thử nghiệm/ví dụ của bạn. Bạn có thể nhấn một khối ngoài bắt trong một phạm vi khác nhau (ví dụ như lồng procs lưu trữ)

Chỉnh sửa: Tôi đang sử dụng SQL Server 2005 SP3

Nó phụ thuộc khi độ phân giải tên hoãn áp dụng, liên quan đến mức tuyên bố biên dịch lại.

  • Trong trường hợp của tôi, toàn bộ hàng loạt thất bại cả hai lần và không có mức tuyên bố biên dịch lại xảy ra như vậy không có tên hoãn lại độ phân giải

  • Trong trường hợp OP, những biên dịch hàng loạt và chạy nhưng sau đó có một biên dịch lại mức tuyên bố/thu nhập hoãn lại lỗi phân giải tên trong chạy mã

tôi đi tìm một số tài liệu tham khảo về lý do tại sao nó khác nhau, do BOL không nói nhiều, cũng như thế Erland Sommarskog

+0

Tôi đang sử dụng SQL-2008 và trong SSMS tôi thấy bắt khối thực thi: Đã xảy ra lỗi! Ungültiger Objektname 'IDontExist'. –

+1

@bernd_k: Tôi đang sử dụng SQL Server 2005.Hành vi đã thay đổi – gbn

+1

Tôi đang sử dụng SQL Server 2008 và nhận được hành vi tương tự như bạn. '@@ VERSION = Microsoft SQL Server 2008 (SP2) - 10.0.4000.0 (X64) Sep 16 2010 19:43:16 Bản quyền (c) 1988-2008 Microsoft Corporation Developer Edition (64 bit) trên Windows NT 6.1 (Xây dựng 7600:) ' –

3

Hành vi này xảy ra nếu trước đó bạn đã có một bảng IDontExist và biên soạn một kế hoạch cho nó vẫn còn trong bộ nhớ cache sau đó thả bảng.

Điều này cũng xảy ra nếu bạn chạy câu lệnh riêng lẻ hai lần ngay cả khi không có bảng nào tồn tại. Lần chạy đầu tiên làm phát sinh lỗi không bị bắt.Lần chạy thứ hai (sau khi gói đầu tiên được lưu trữ) thành công.

/*Clear Cache*/ 
DBCC FREEPROCCACHE 

GO 

BEGIN TRY 
INSERT INTO IDontExist(ProductID) 
VALUES(1) 
END TRY 
BEGIN CATCH 
SELECT 'There was an error! ' + ERROR_MESSAGE() 
END CATCH 

GO 
/*Plan now Cached*/ 

SELECT query_plan 
FROM sys.dm_exec_cached_plans cp 
     OUTER APPLY sys.dm_exec_sql_text(plan_handle) t 
     OUTER APPLY sys.dm_exec_query_plan(plan_handle) qp 
WHERE t.text LIKE '%IDontExist%' 
OPTION (RECOMPILE) 

GO 

BEGIN TRY 
INSERT INTO IDontExist(ProductID) 
VALUES(1) 
END TRY 
BEGIN CATCH 
SELECT 'There was an error! ' + ERROR_MESSAGE() 
END CATCH 

GO 

Tuyên bố INSERT được tự động tham số hóa.

Nếu bạn thay đổi tuyên bố Select * from IDontExist của bạn để Select * from IDontExist WHERE ProductID = 1 này cũng trở nên tự động parameterised và họ cư xử như vậy.

Tôi không chắc chắn lý do tại sao tham số tự động tạo sự khác biệt ở đây. Tôi nghĩ rằng nó được giải thích bởi các bên dưới extract from BOL tuy nhiên.

Các loại sau đây của các lỗi không được xử lý bởi một khối CATCH khi chúng xảy ra ở cùng một mức độ thực hiện như TRY…CATCH xây dựng ... [những] xảy ra trong tuyên bố cấp biên dịch lại ... Nếu một lỗi xảy ra trong quá trình biên dịch hoặc biên dịch ở mức thấp hơn (ví dụ, khi thực hiện sp_executesql hoặc thủ tục được lưu trữ do người dùng xác định) trong khối TRY, lỗi xảy ra ở mức thấp hơn cấu trúc TRY…CATCH và sẽ được xử lý bởi khối CATCH được liên kết.

Tôi đoán tham số tự động của câu lệnh đó có nghĩa là nó được biên dịch lại ở cấp thực thi thấp hơn và có thể bắt được.

0

Bây giờ chúng tôi có tất cả các giải thích về lý do tại sao điều này xảy ra. Hãy xem một giải pháp thực tế cho vấn đề.

Trước tiên, hãy đưa ra các tuyên bố mà @ d-k-mulligan đề xuất ở trên và biến chúng thành các procs được lưu trữ.

IF OBJECT_ID('dbo.prcIDontExistINSERT', 'P') IS NOT NULL DROP PROCEDURE dbo.prcIDontExistINSERT 
GO 
CREATE PROCEDURE dbo.prcIDontExistINSERT 
AS 
BEGIN TRY 
INSERT INTO IDontExist(ProductID) 
VALUES(1) 
END TRY 
BEGIN CATCH 
    SELECT 'There was an error! ' + ERROR_MESSAGE() 
END CATCH 
GO 

IF OBJECT_ID('dbo.prcIDontExistSELECT', 'P') IS NOT NULL DROP PROCEDURE dbo.prcIDontExistSELECT 
GO 
CREATE PROCEDURE dbo.prcIDontExistSELECT 
AS 
BEGIN TRY 
    SELECT * FROM IDontExist 
END TRY 
BEGIN CATCH 
    SELECT 'There was an error! ' + ERROR_MESSAGE() 
END CATCH 
GO 

Nếu chúng tôi chạy một trong số chúng, chúng tôi sẽ thấy lỗi tương tự.

EXEC dbo.prcIDontExistINSERT 
EXEC dbo.prcIDontExistSELECT 

Msg 208, Level 16, State 1, Procedure prcIDontExistSELECT, Line 4 
Invalid object name 'IDontExist'. 

Bây giờ, giải pháp là tạo lỗi xử lý các trình bao bọc với mục đích duy nhất là bắt lỗi từ các procs gốc phía trên đang nhận được lỗi không tìm thấy đối tượng.

IF OBJECT_ID('dbo.prcIDontExistInsert_ERROR_HANDLER', 'P') IS NOT NULL DROP PROCEDURE dbo.prcIDontExistInsert_ERROR_HANDLER 
GO 
CREATE PROCEDURE dbo.prcIDontExistInsert_ERROR_HANDLER 
AS 
BEGIN TRY 
EXEC dbo.prcIDontExistINSERT 
END TRY 
BEGIN CATCH 
    SELECT 'There was an error! ' + ERROR_MESSAGE() 
END CATCH 
GO 

IF OBJECT_ID('dbo.prcIDontExistSELECT_ERROR_HANDLER', 'P') IS NOT NULL DROP PROCEDURE dbo.prcIDontExistSELECT_ERROR_HANDLER 
GO 
CREATE PROCEDURE dbo.prcIDontExistSELECT_ERROR_HANDLER 
AS 
BEGIN TRY 
EXEC dbo.prcIDontExistSELECT 
END TRY 
BEGIN CATCH 
    SELECT 'There was an error! ' + ERROR_MESSAGE() 
END CATCH 
GO 

Cuối cùng, hãy chạy một trong các lỗi xử lý procs của chúng tôi và xem thông báo mà chúng tôi mong đợi.

EXEC dbo.prcIDontExistInsert_ERROR_HANDLER 
EXEC dbo.prcIDontExistSELECT_ERROR_HANDLER 

There was an error! Invalid object name 'IDontExist'. 

LƯU Ý: Kalman Toth đã làm tất cả các công trình nghiên cứu khó khăn ở đây: http://www.sqlusa.com/articles2008/trycatch/

0

Cách giải quyết với sql động. Có lẽ nó sẽ hữu ích cho ai đó.

begin try 
    exec(' 
     insert into IDontExist(ProductID) 
     values(1) 
    ') 
end try 
begin catch 
    select 'There was an error! ' + error_message() 
end catch 
Các vấn đề liên quan