这是因为你的错误是绑定错误不能被捕获在TRY..CATCH
块。
当您引用一个不存在的对象时,SQL Server甚至不会尝试检查其列,它不会编译这段代码,而是将它留给执行时间。 这叫做deferred name resolution
。
只有在执行此语句时,它才会检查表并引发错误。
这是编译错误,不能在同一范围内捕获(仅在外部范围内)。
有一个连接项目,你可以检查: 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
在这里你可以看到,有没有真正的插入方案:
这是计划插入如何真正的样子:
UPDATE
我试图重现与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%'
你抓不到'无效的对象name'错误。 – DavidG
@DavidG,为什么第二次以后没有错误?脚本没有改变,只是重新编译。 –