There is a Procedure like below
CRAETE PROCEDURE dbo.procname
AS
BEGIN
--BEGIN TRY
SELECT 1/0
--END TRY
--BEGIN CATCH
--END CATCH
END
We are calling this procedure in UNIX SQLCMD as below.
retval=`
DECLARE #val INT
EXEC #val=dbo.procname
SELECT #val`
echo $retval
When we using TRY CATCH block and call the procedure , #val is giving -6 but when we remove TRY CATCH block from procedure, then we are NOT getting any value in #val and it is displaying the SSMS generated Message like
Divide by zero error.
As it is known that, in SQL server, procedures return 0 by default if it runs successfully and Non zero value if it fails.
My requirement is to capture #val int value when it fails as I am using this value to proceed further in UNIX shell script.
So, is there any way that we can capture procedure return value in #val variable if we do not use TRY CATCH block in procedure.
We are working on Migration project and there are number of Procedure in it and we cannot check each and every proc to see if TRY CATCH block is present or not.
Hoping someone can help. I'm trying to stop a stored procedure from returning a recordset that has been selected, if an error is encountered later in the stored procedure. I've included some pseudo code below to show what I'm doing. Basically, the SELECT [Foo] is returning a recordset if the or COMMIT actions fail and [Tran1] is rolled back. The client does not support multiple recordsets and the has to come after the SELECT so I'm looking for a command to place in the CATCH block that effectively cancels the SELECT [Foo] and instead enables me to return the recordset created by SELECT -1 AS [Error_Code]
BEGIN TRANSACTION [Tran1]
BEGIN TRY
SET NOCOUNT ON;
<Do some Update>
SELECT [Foo]
FROM [Bar]
<Do some Insert>
COMMIT TRANSACTION [Tran1]
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION [Tran1]
SELECT -1 AS Error_Code
END CATCH
I would like to know the best position of a TRY/CATCH for a T-SQL procedure and why
In the statement:
CREATE PROCEDURE procedure_name
AS
BEGIN
-- Code
BEGIN TRY
sql_statement
END TRY
BEGIN CATCH
-- Handle errors
END CATCH
--Code
END
or in the call:
BEGIN TRY
EXEC procedure_name
END TRY
BEGIN CATCH
-- Handle errors
END CATCH
I would go for the first option.
BEGIN TRY
sql_statement
END TRY
BEGIN CATCH
-- Handle errors
END CATCH
The reason is you would want to catch the errors at the source and then take some appropriate actions.
In second option you are letting the error bubble up and there you would not have access to all the Exact error information returned by the error functions inside the catch block.
For example the ERROR_LINE() function will return the line number of the calling procedure where the it is calling the procedure containing the actual sql code, you would want to know the error line number where the actual exception was thrown, this information is only available in the catch block of the procedure being called.
Moral of the story is try to catch exceptions as close to the source as possible.
I have the following code inside a cursor in a stored procedure I am working with:
SELECT #err_code = ##error
If #err_code <> 0
BEGIN
ROLLBACK TRAN
Select return_status = 'FAIL',
return_msg = 'Insert Into Errs Warnings Failed !!'
return 16
END
Before this error checking there is some table inserts (still inside the cursor). Recently I encountered an error where the table we were inserting into didn't match the insert statement that was coded (Column name or number of supplied values does not match table definition). I would have thought that this error checking code would have caught this and killed the procedure, but for some reason all it did was print an error message to the log and continue running. Column name or number of supplied values does not match table definition was the error message.
So I'm wondering if this has to do with the return being inside of a cursor. Is is possible that in this scenario all the return 16 would have done was exit the cursor and continue to execute the rest of the stored procedure? Or should it have terminated the procedure entirely? I'm using MS SQL Server 2008.
Thank you!
The issue with this ##ERROR function is , its scope is very limited. It will only be populated if an error occurs in the statement that was executed just before calling ##ERROR function.
If any other statement is executed before or called after that error occurred the ##ERROR will return NULL.
Also to anticipate where error may occur, and capture its value intime and stop the execution there, its a bit of a pain and error prone.
The best way of error handling in sql server would be try..catch block.
BEGIN TRY
BEGIN TRANSACTION
-- your code here
COMMIT TRANSACTION --<-- this will only be executed if nothing
-- goes wrong in prior statements
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
Select return_status = 'FAIL',
return_msg = 'Insert Into Errs Warnings Failed !!'
END CATCH
Using Try...Catch block also gives you access to Error functions which can only be used in CATCH BLOCK and can help you to get detailed information about the error occurred in the Try..Block.
error functions like ERROR_MESSAGE(), ERROR_LINE() , ERROR_STATE(), ERROR_PROCEDURE() etc
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
INSERT INTO IDontExist(ProductID)
VALUES(1)
END TRY
BEGIN CATCH
SELECT 'There was an error! ' + ERROR_MESSAGE()
END CATCH
However when I run this statement the statement in the Catch Block is never executed and instead the error is just displayed in the results tab
BEGIN TRY
Select * from IDontExist
END TRY
BEGIN CATCH
SELECT 'There was an error! ' + ERROR_MESSAGE()
END CATCH
They both return the same error number '208' 'Invalid Object Name: IDontExist' so why would one get handled and the other not?
I don't get the CATCH block hit at all.
That's because the code won't compile, because the object doesn't exist, no plan is generated, so nothing runs to hit the CATCH block.
You can never hit this catch block so somethign is wrong with your testing/example. You can hit an outer catch block in a different scope (eg nested stored procs)
Edit: I'm using SQL Server 2005 SP3
It depends when deferred name resolution applies, related to statement level recompilation.
In my case, the whole batch fails both times and no statement level recompilation happens so no deferred name resolution
In OP's case, the batch compiles and runs but then has a statement level recompilation/deferred name resolution error in running code
I'm off to find some references about why it's different, given BOL doesn't say much, neither does Erland Sommarskog
This has bitten me in the past as well.
Not all errors generated inside the TRY block statements are passed into the CATCH block. Any errors with a severity of 10 or less are considered to be warnings and do not cause control to flow to the CATCH block. Also, any errors that break the database connection will not cause the CATCH block to be reached. There may be other situations as well.
Directly from 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
The error is not caught and control passes out of the TRY…CATCH construct to the next higher level.
Running the SELECT statement inside a stored procedure will cause the error to occur at a level lower than the TRY block. The error will be handled by the TRY…CATCH construct.
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 CATCH block when they occur at the same level of execution as the TRY…CATCH construct ... [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 executing sp_executesql or a user-defined stored procedure) inside the TRY block, the error
occurs at a lower level than the TRY…CATCH construct and will be handled by the associated CATCH block.
I presume the auto parametrization of that statement means that it gets recompiled at a lower execution level and is catchable.
Now that we have all the explanations as to why this is happening. Let's see an actual solution to the problem.
First let's take the statements that #d-k-mulligan proposed above and turn them into stored procs.
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
If we run either of them we see the same error.
EXEC dbo.prcIDontExistINSERT
EXEC dbo.prcIDontExistSELECT
Msg 208, Level 16, State 1, Procedure prcIDontExistSELECT, Line 4
Invalid object name 'IDontExist'.
The solution now is to create error handling wrapper procs with the sole purpose of catching any error from the original procs above that are getting the object not found errors.
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
Finally, let's run either of our error handling procs and see the message we expect.
EXEC dbo.prcIDontExistInsert_ERROR_HANDLER
EXEC dbo.prcIDontExistSELECT_ERROR_HANDLER
There was an error! Invalid object name 'IDontExist'.
NOTE: Kalman Toth did all the hard research work here:
http://www.sqlusa.com/articles2008/trycatch/
Workaround with dynamic sql. Maybe it will be helpful for someone.
begin try
exec('
insert into IDontExist(ProductID)
values(1)
')
end try
begin catch
select 'There was an error! ' + error_message()
end catch