2010-12-29 12 views
11

Używam programu SQL Server 2008 i gdy uruchamiam to oświadczenie w studio Zarządzania SELECT w bloku catch jest wykonywany zgodnie z oczekiwaniamiCo jest nie tak z moim próbnym połowem w T-SQL?

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

Jednak gdy uruchamiam to oświadczenie oświadczenie w bloku catch nie jest wykonywany a zamiast tego błędu jest wyświetlane na karcie wyniki

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

oboje zwracają taką samą liczbę błędów „208” „Nieprawidłowa nazwa obiektu: IDontExist” więc dlaczego należałoby się obchodzić, a nie inne?

+0

Wszelkie opinie na temat naszych odpowiedzi proszę? Jestem bardzo ciekawa, jak się dostałeś ... – gbn

+0

Późne pytanie: co zrobić, jeśli uruchomisz DBCC FREEPROCCACHE, aby wymusić ponowną kompilację – gbn

Odpowiedz

5

To również mnie ugryzło w przeszłości.

Nie wszystkie błędy wygenerowane w instrukcjach blokowych TRY są przekazywane do bloku CATCH. Wszelkie błędy o sile ważności 10 lub mniejszej są uważane za ostrzeżenia i nie powodują, że kontrola przepływa do bloku CATCH. Ponadto wszelkie błędy powodujące przerwanie połączenia z bazą danych nie spowodują, że blok CATCH zostanie osiągnięty. Mogą być również inne sytuacje.

+0

+1 Nice! Wszystko zaczyna mieć sens ... –

+3

Jak to ma znaczenie w przypadku PO? Błąd 208 ma istotność 16 i nie powoduje przerwania połączenia. –

+0

..Tak, dlatego czasami dobrym rozwiązaniem jest przechowywanie błędów w tabeli @var, które powinny zachować wartości błędów dostępne w bloku CATCH – Milan

3

Bezpośrednio z 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 

Błąd nie został uchwycony, a kontrola przechodzi poza TRY ... Konstrukcja CATCH do następnego wyższego poziomu.

Uruchomienie instrukcji SELECT w procedurze przechowywanej spowoduje wystąpienie błędu na poziomie niższym niż blok TRY. Błąd zostanie obsłużony przez TRY ... konstrukt CATCH.

5

Nie otrzymuję w ogóle trafienia bloku CATCH.

To dlatego, że kod nie zostanie skompilowany, ponieważ obiekt nie istnieje, nie jest generowany plan, więc nic nie działa, aby uderzyć w blok CATCH.

Możesz nigdy trafić w ten blok catch, więc coś jest nie tak z twoim testowaniem/przykładem. Można trafić zewnętrzny blok catch w innym zakresie (np zagnieżdżone przechowywane PROC)

Edycja: używam 2005 z dodatkiem SP3

to zależy kiedy stosuje odroczone rozpoznawanie nazw, związanych z poziomem oświadczenie rekompilacji SQL Server.

  • W moim przypadku, cała partia nie za każdym razem i nie poziom oświadczenie kompilację dzieje się tak nie odroczony nazwa rozdzielczości

  • W przypadku OP, gdy kompiluje partii i działa, ale potem ma rekompilacji poziomu oświadczenie/odroczony rozdzielczości nazwa błędu w prowadzeniu kod

Idę znaleźć jakieś odniesienia o tym, dlaczego jest inaczej, biorąc pod uwagę BOL nie mówi dużo, ani nie Erland Sommarskog

+0

Używam SQL-2008, aw SSMS widzę catch blok wykonany: wystąpił błąd! Ungültiger Objektname 'IDontExist'. –

+1

@bernd_k: Używam programu SQL Server 2005.Zachowanie się zmieniło – gbn

+1

Używam programu SQL Server 2008 i zachowuję się tak samo jak Ty. '@@ VERSION = Microsoft SQL Server 2008 (SP2) - 10.0.4000.0 (X64) 16 września 2010 19:43:16 Copyright (c) 1988-2008 Microsoft Corporation Developer Edition (wersja 64-bitowa) w systemie Windows NT 6.1 (Kompilacja 7600:) ' –

3

To zachowanie występuje, jeśli poprzednio miałeś tabelę IDontExist i skompilowałeś plan, który wciąż znajduje się w pamięci podręcznej, a następnie upuszczasz tabelę.

Zdarza się tak również, jeśli dwukrotnie uruchomisz pojedynczą instrukcję, nawet jeśli tabela nie istnieje. Pierwsze uruchomienie powoduje błąd, który nie zostanie przechwycony.Drugi przebieg (po pierwszym planie jest buforowany) powiedzie się.

/*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 

Instrukcja INSERT zostaje automatycznie sparametryzowana.

Jeśli zmienisz swoje oświadczenie Select * from IDontExist na Select * from IDontExist WHERE ProductID = 1, to również zostanie automatycznie sparametryzowane i zachowują się tak samo.

Nie mam absolutnej pewności, dlaczego automatyczna parametryzacja ma tutaj znaczenie. Myślę, że jest to jednak wyjaśnione poniżej: extract from BOL.

następujące rodzaje błędów nie są obsługiwane przez CATCH bloku, gdy występują one na tym samym poziomie co TRY…CATCH wykonanie konstrukcji ... [tych], które występują podczas stwierdzenie poziomu rekompilacji ... Jeśli błędu występuje podczas kompilacji lub rekompilacji na poziomie instrukcji na niższym poziomie wykonywania (na przykład podczas wykonywania sp_executesql lub zdefiniowanej przez użytkownika procedury przechowywanej) wewnątrz bloku TRY, błąd występuje na poziomie niższym niż konstrukt TRY…CATCH i będzie obsługiwany przez powiązany blok CATCH.

Zakładam, że automatyczna parametryzacja tej instrukcji oznacza, że ​​zostanie ona przekompilowana na niższym poziomie wykonania i można ją odjąć.

0

Teraz, gdy mamy wszystkie wyjaśnienia, dlaczego tak się dzieje. Zobaczmy rzeczywiste rozwiązanie problemu.

Najpierw weźmy oświadczenia, które @ d-k-mulligan zaproponował powyżej i przekształć je w przechowywane proc.

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 

Jeśli uruchomimy którykolwiek z nich, widzimy ten sam błąd.

EXEC dbo.prcIDontExistINSERT 
EXEC dbo.prcIDontExistSELECT 

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

Rozwiązanie teraz jest stworzenie obsługi błędów otoki procs wyłącznie w celu złapania żadnego błędu z oryginalnych proca powyżej, które są coraz obiekt nie znaleziono błędów.

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 

Wreszcie, uruchommy jeden z naszych procesów obsługi błędów i zobaczmy komunikat, którego oczekujemy.

EXEC dbo.prcIDontExistInsert_ERROR_HANDLER 
EXEC dbo.prcIDontExistSELECT_ERROR_HANDLER 

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

UWAGA: Kalman Toth zrobił całą ciężką pracę badawczą tutaj: http://www.sqlusa.com/articles2008/trycatch/

0

Obejście z dynamicznym SQL. Może to będzie pomocne dla kogoś.

begin try 
    exec(' 
     insert into IDontExist(ProductID) 
     values(1) 
    ') 
end try 
begin catch 
    select 'There was an error! ' + error_message() 
end catch 
Powiązane problemy