2017-10-12 2 views
0

catch 블록은 처음 스크립트를 편집 한 후에는 실행되지 않지만 두 번째 이후부터 정상적으로 작동합니다.CATCH 블록이 처음 실행되지는 않지만 두 번째로 실행됩니다 - 이유는 무엇입니까?

다음은이 문제를 보여주는 스크립트입니다.

use master 
go 

print 'rollback demo' 

declare @errordemo bit = 1 
select 'Transaction Count Before =', @@TRANCOUNT 
begin try 
    begin transaction 
    if (@errordemo = 0) select 'abc' as column1 into MyTestTable 
    insert into MyTestTable values ('xyz') 
    commit transaction 
end try 
begin catch 
    rollback transaction 
end catch 
go 

select 'Transaction Count After =', @@TRANCOUNT 
go 

-- this is only to bring back the system to its previous state 
if (@@TRANCOUNT > 0) rollback transaction 
go 
if exists(select * from sys.tables where name ='MyTestTable') drop table MyTestTable 
go 

위의 스크립트를 SQL Server Management Studio에 복사하고 실행하십시오. 다음과 같은 결과가 나타납니다.

Transaction Count Before = 0 
Transaction Count After = 1 

눌러 F5 거듭하며 성공적으로 실행된다 : 실행 catch 블록 수단

Transaction Count Before = 0 
Transaction Count After = 0 

.

이제 첫 번째 줄 print 'rollback demo'을 주석 처리하거나 텍스트를 변경하십시오. 오류가 다시 발생합니다. 여러 번 F5 키를 누르면 오류가 없습니다. 해당 라인의 주석 처리를 제거하거나 스크립트의 다른 변경 작업을 반복하면 예측 가능/재현 가능 패턴을 볼 수 있습니다.

여기에 무슨 일이 일어나고 있습니까?

다음은 어떤 일이 벌어지고 있는지 보여주는 몇 가지 스크린 샷입니다.

성공하면 :

successful result 1 successful result 2

때 실패 : 오류가 TRY..CATCH 블록에 잡을 수 없습니다 바인딩 오류 때문에

unsuccessful result 1 unsuccessful result 2

+0

당신은'잘못된 개체 name' 오류를 잡을 수 없습니다. – DavidG

+0

@DavidG, 왜 오류가 두 번째 이상입니까? 스크립트가 재 컴파일 된 것을 제외하고는 스크립트에 변경이 없습니다. –

답변

1

이입니다.

존재하지 않는 개체를 참조 할 때 SQL Server는 해당 열을 검사하지도 않고이 코드를 컴파일하지 않고 실행 시간 동안 만 처리합니다. 이것을 deferred name resolution이라고합니다.

이 명령문 실행시에만 테이블을 검사하여 오류가 발생합니다.

이것은 컴파일 오류이므로 동일한 범위 (외부 범위에서만)에서 catch 할 수 없습니다.

확인할 수있는 연결 항목이 있습니다 : Try-catch should capture the parse errors.

사실이 오류가 발생하면 catch 블록에 도달 할 수 없습니다.

다음 실행시 쿼리의 아무 문자도 변경되지 않은 경우 캐시 된 계획이 사용됩니다. 따라서 두 번째 실행에서는 컴파일되지 않습니다.당신이 당신의 쿼리 텍스트를 변경하는 경우

하지만 (추가 할 - 그것의 어떤 부분에) 같이, 또는 계획을 캐시하지 않도록하기 위해 서버를 지시 할 경우 (recompile 옵션을 사용) :

declare @errordemo bit = 1 
select 'Transaction Count Before =', @@TRANCOUNT 
begin try 
    begin transaction 
    if (@errordemo = 0) select 'abc' as column1 into dbo.MyTestTable 
    print @errordemo 
    insert into dbo.MyTestTable values ('xyz') 
    option (recompile) -------------------------------!!!!!!!!!!!!!!!!!!! 
    commit transaction 
end try 
begin catch 
    print 'catch block' 
    rollback transaction 
end catch 
go 

select 'Transaction Count After =', @@TRANCOUNT 
go 

if (@@TRANCOUNT > 0) rollback transaction 

계획이 캐싱되지 않고 쿼리가 매번 컴파일되고 매번 컴파일 오류가 표시되고 catch 블록에 도달하지 않습니다. 여기

는 계획 캐시입니다 : plan

그리고 여기 당신은 실제 삽입 계획이 없다고 볼 수 enter image description here

:

enter image description here

는이 계획이 어떻게 보이는지 실제 삽입입니다

업데이트

SELECT 쿼리를 사용하여 동일한 내용을 재현하려했지만 계획의 차이점을 발견했지만 계획 캐시에서 SELECT에 대한 계획을 추출 할 수 없었습니다. 해당 항목이 존재하고 크기는 INSERT이고 계획은 볼 수 없지만 캐시되지는 않았지만 항목이 존재합니다 ...

재현하려면 다음을 사용할 수 있습니다. 다음 코드를

/*select query F7CA8D53-E171-4B5F-8CEA-B19461819C0D*/ 
declare @errordemo bit = 1 
select 'Transaction Count Before =', @@TRANCOUNT 
begin try 
    begin transaction 
    if (@errordemo = 0) select 'abc' as column1 into dbo.MyTestTable 
    print @errordemo 
    select * from MyTestTable 
    commit transaction 
end try 
begin catch 
    print 'catch block' 
    rollback transaction 
end catch 
go 

select 'Transaction Count After =', @@TRANCOUNT 
go 

if (@@TRANCOUNT > 0) rollback transaction; 
go 
------------------------------------------- 
------------------------------------------- 
/*insert query C7D24848-E2BB-46E7-8B1B-334406789CF9*/ 
declare @errordemo bit = 1 
select 'Transaction Count Before =', @@TRANCOUNT 
begin try 
    begin transaction 
    if (@errordemo = 0) select 'abc' as column1 into dbo.MyTestTable 
    print @errordemo 
    insert into MyTestTable values(1) 
    commit transaction 
end try 
begin catch 
    print 'catch block' 
    rollback transaction 
end catch 
go 

select 'Transaction Count After =', @@TRANCOUNT 
go 

if (@@TRANCOUNT > 0) rollback transaction 
go 
----------------------- 
----------------------- 
select * 
from sys.dm_exec_cached_plans p 
cross APPLY sys.dm_exec_query_plan(p.plan_handle) pl 
cross apply sys.dm_exec_sql_text (p.plan_handle) t 
where (t.text like '%F7CA8D53-E171-4B5F-8CEA-B19461819C0D%' -- select 
    or t.text like '%C7D24848-E2BB-46E7-8B1B-334406789CF9%')-- insert 
    and t.text not like '%sys.dm_exec_cached_plans%' 

enter image description here

+0

나는 바인딩이나 컴파일 오류를 잡을 수 없다는 것을 알고 있습니다. 그러나 컴파일 오류가 발생하면 계획을 캐시해서는 안됩니다. 그렇지 않습니까? –

+0

예. 그것은 그 부분을 삽입하지 않고 캐싱 했으므로 삭제되었습니다. 나는 계획을 업로드 할 것이고 당신은 그것을 볼 것이다 – sepupic

+0

++ 좋고 도움이되는 대답. –

관련 문제