I am using SQL Server 2008 and when I run this Statement in Management studio the Select statement in the Catch Block is executed as expected
BEGIN TRY
INSE
This behaviour happens if you previously had a table IDontExist and compiled a plan for it that is still in the cache then drop the table.
It also happens if you run the individual statement twice even without the table ever existing. The first run raises an error that is not caught. The second run (after the first plan is cached) succeeds.
/*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
The INSERT statement gets auto parameterised.
If you change your Select * from IDontExist statement to Select * from IDontExist WHERE ProductID = 1 this also becomes auto parameterised and they behave the same.
I'm not absolutely certain why the auto parameterisation makes a difference here. I think that it is explained by the below extract from BOL however.
The following types of errors are not handled by a
CATCHblock when they occur at the same level of execution as theTRY…CATCHconstruct ... [those] that occur during statement-level recompilation ... If an error occurs during compilation or statement-level recompilation at a lower execution level (for example, when executingsp_executesqlor a user-defined stored procedure) inside theTRYblock, the error occurs at a lower level than theTRY…CATCHconstruct and will be handled by the associatedCATCHblock.
I presume the auto parametrization of that statement means that it gets recompiled at a lower execution level and is catchable.