Ceci est parce que votre erreur est Erreur de liaison qui ne peut pas être prise en TRY..CATCH
bloc. Lorsque vous référencez un objet inexistant, SQL Server n'essaie même pas de vérifier ses colonnes, il ne compile pas ce code, il le quitte pour l'exécution. Ceci est appelé deferred name resolution
. Ce n'est que lorsqu'il s'agit de l'exécution de cette instruction, il vérifie la table et déclenche une erreur.
Il s'agit d'une erreur de compilation qui ne peut pas être détectée dans la même étendue (uniquement dans la portée externe). Il existe un élément de connexion que vous pouvez vérifier: Try-catch should capture the parse errors Par conséquent, votre bloc catch ne peut pas être atteint lorsque cette erreur est déclenchée.
Lors de l'exécution suivante, si aucun caractère de votre requête n'a été modifié, le plan mis en cache est utilisé. Donc, il n'est pas compilé lors de la seconde exécution.
Mais si vous changez votre texte de la requête (essayez d'ajouter - dans une partie de celui-ci), ou si vous demandez au serveur de ne pas mettre en cache le plan (en utilisant l'option recompile
) comme ceci:
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
le plan ne sera pas mis en cache et la requête sera compilée à chaque fois, et vous verrez une erreur de compilation à chaque fois et votre bloc catch
ne sera jamais atteint.
Voici le cache plan: plan
Et là, vous pouvez voir qu'il n'y a pas de véritable plan d'insertion:
Voici comment véritable plan d'insertion ressemble:
MISE À JOUR
J'ai essayé de reproduire la même chose avec SELECT
requête et de trouver une différence dans le plan, mais je ne pouvais pas extraire le plan pour SELECT
du cache plan. L'entrée pour elle existe, a la même taille que le plan INSERT
, mais il est impossible de voir ce plan, il semble que ce n'est pas mis en mémoire cache, mais l'entrée existe ...
Pour le reproduire, vous pouvez utiliser la code suivant:
/*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%'
Vous ne pouvez pas attraper les erreurs de 'nAME' d'objet non valide. – DavidG
@DavidG, pourquoi n'y a-t-il pas d'erreur une deuxième fois? Il n'y a pas de changement dans le script, sauf qu'il est juste recompilé. –