When does RAISERROR fire in a stored procedure? - sql

I've got a stored procedure that contains a try-catch block. In the catch block I call raiserror() to rethrow the error with some context.
I was expecting that if an error occurred the raiserror() would be called and execution would immediately return from the stored procedure to the calling code. However, this doesn't appear to be the case. It looks like execution of the stored procedure continues until it hits a return statement, then the raiserror() takes effect.
Is this correct - that raiserror() won't have an effect until return is called or the end of the stored procedure is reached?
I'm using SQL Server 2012.
EDIT:
in reply to request for details of the stored procedure, here's the relevant snippet of code:
DECLARE #ErrMsg VARCHAR(127) = 'Error in stored procedure ' + OBJECT_NAME(##PROCID) + ': %s';
declare #UpdateDateRecordCount table (LastUpdated datetime, NumberRecords int);
begin try;
insert into #UpdateDateRecordCount (LastUpdated, NumberRecords)
exec sp_ExecuteSql
#UpdateCountQuery,
N'#LastUpdated datetime',
#LastUpdated = #LastUpdated;
if ##rowcount <= 0
begin;
return 0;
end;
end try
begin catch;
declare #InsertError varchar(128) = 'Error getting updated date record count: '
+ ERROR_MESSAGE();
RAISERROR (#ErrMsg, 16, 1, #InsertError);
end catch;
-- Attempt to loop through the records in #UpdateDateRecordCount...
The #UpdateCountQuery argument will be set to something like:
N'select LastUpdated, count(*) from dbo.Part where LastUpdated > #LastUpdated group by LastUpdated;'

As I understand it, if you want the execution to stop, you need to raise the error within the TRY block, and then raise the error again in your CATCH block this will make sure that the error is "raised" to the caller.
Or you could add a RETURN statement after your RAISERROR statement in the CATCH block. This will exit the procedure and return to the caller.
Also, as suggested by MSDN you should try to use the THROW statement instead of RAISERROR since it (the RAISERROR) will be phased out.

That's not how it works in T-SQL. Nothing in the documentation for TRY...CATCH or RAISERROR specifies any special cases that would override:
When the code in the CATCH block finishes, control passes to the statement immediately after the END CATCH statement. Errors trapped by a CATCH block are not returned to the calling application. If any part of the error information must be returned to the application, the code in the CATCH block must do so by using mechanisms such as SELECT result sets or the RAISERROR and PRINT statements.
If you want the stored proc to exit, you need a RETURN statement as well.

It depends on the severity level that you use. There's a lot more information in the below link:
http://technet.microsoft.com/en-us/library/ms178592.aspx
But to quote the article:
The errors generated by RAISERROR operate the same as errors generated
by the Database Engine code. The values specified by RAISERROR are
reported by the ERROR_LINE, ERROR_MESSAGE, ERROR_NUMBER,
ERROR_PROCEDURE, ERROR_SEVERITY, ERROR_STATE, and ##ERROR system
functions. When RAISERROR is run with a severity of 11 or higher in a
TRY block, it transfers control to the associated CATCH block. The
error is returned to the caller if RAISERROR is run...
So if your severity level is 11 or higher then the control will be immediately transferred to the CATCH block.
The below example shows a severity level of 16:
RAISERROR ('Error raised in TRY block.', -- Message text.
16, -- Severity.
1 -- State.
);

Related

HANDLING ERRORS IN SQL PROCEDURES - ORACLE DATABASE

How can I catch an error in a procedure and the procedure will continue running to the end without failing.
Usually a procedure fails when it encounters an error during the execution of the procedure. but there is a way that you can add a condition to the procedure to write the error somewhere and the procedure will continue to the end without aborting.
Add an EXCEPTION handling clause within an nested BEGIN/END block then you can continue after the nested block:
CREATE PROCEDURE procedure_name
IS
BEGIN
BEGIN
DBMS_OUTPUT.PUT_LINE('Do something');
RAISE ZERO_DIVIDE;
DBMS_OUTPUT.PUT_LINE('This will be skipped');
EXCEPTION
WHEN OTHERS THEN
-- Do something to handle the execption (or, in this case, nothing)
NULL;
END;
DBMS_OUTPUT.PUT_LINE('Continue');
END;
/
Note: Catching OTHERS is generally bad practice as you should catch specific, expected errors and then if there is an unexpected error it is propagated and can be debugged. If you catch all errors then you cannot tell what error occurred.
CREATE PROCEDURE procedure_name
IS
BEGIN
BEGIN
DBMS_OUTPUT.PUT_LINE('Do something');
RAISE ZERO_DIVIDE;
DBMS_OUTPUT.PUT_LINE('This will be skipped');
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- Do something to handle the execption (or, in this case, nothing)
NULL;
WHEN ZERO_DIVIDE THEN
-- Do something to handle the execption (or, in this case, nothing)
NULL;
END;
DBMS_OUTPUT.PUT_LINE('Continue');
END;
/
[is there] a way that you can add a condition to the procedure to write the error somewhere?
Instead of using NULL in the EXCEPTION clause you can use an INSERT statement to write the error to a table or you can call an AUTONOMOUS_TRANSACTION to handle writing the error.
db<>fiddle here

Stop a stored procedure from further execution once the condition is violated?

I have a following stored procedure and i want to abort it once the number of rows in fact table are not similar to the historical data. So i'm trying to do something like, till now it only sends the email to the users that the data is not same but i want to add an another feature, i.e to abort/stop the SP if the rows difference is more than 5%. But it's not working properly. Can anyone explain where i'm making the mistake? Following is the chunk of stored procedure where i'm making modifications:
SET XACT_ABORT ON;
Begin Try
Begin Transaction;
if (#delta_ok = 0)
set #email_body = #email_body+char(13)+char(10)+'ETL process has been stopped.'
Return
else
set #email_body = #email_body+char(13)+char(10)+'ETL process is copying the data .'
Commit Transaction;
Begin Catch
Rollback Transaction
End Catch
End Try
So my question is that if put "RETURN" inside the "if" statement would it stop the SP? Once the condition is violated? And pass to "ELSE" if the condition isn't fulfilled?
Your syntax is all over the place here. Using some formatting to add clarity will help considerably. Also you have an anti-pattern in your code I call Try/Squelch. You catch an error but then you swallow it and don't tell anybody it happened. When exceptions happen you need to handle them. That means you need to tell the calling program something went wrong, not just silently ingest the details so nobody knows it happened or how to fix it.
Begin Try
Begin Transaction;
if (#delta_ok = 0)
begin
set #email_body = #email_body + char(13) + char(10) + 'ETL process has been stopped.'
Return
end
else
begin
set #email_body = #email_body + char(13) + char(10) + 'ETL process is copying the data .'
Commit Transaction;
end
End Try
Begin Catch
Rollback Transaction
--you really need something here to log/audit and probably tell the calling program something failed.
End Catch

Catch is not working in my Stored Procedure - What is wrong with my code?

ALTER PROCEDURE uspTryCatchTest
AS
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
When i am execute this procedure i get object name resolution error...
How to fix it
Assuming we're talking about MS SQL Server, TRY/CATCH blocks can only deal with certain types of errors. Your stored proc will cause a compile time error, whether or not you're using deferred name resolution. If you had a TRY/CATCH block encapsulating the call to this procedure, however, it would catch the error.
... interestingly, it looks like you've taken the code directly from the reference page I was about to direct you to.
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:
Compile errors, such as syntax errors, that prevent a batch from running.
Errors that occur during statement-level recompilation, such
as object name resolution errors that occur after compilation because
of deferred name resolution.
I would rather use this to check whether the table exists
IF NOT (EXISTS (SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'dbo'
AND TABLE_NAME = 'YourTable'))
BEGIN
PRINT 'NOT Exists';
END
ALTER PROCEDURE uspTryCatchTest
AS
BEGIN TRY
-- Table does not exist; object name resolution
-- error not caught.
EXEC('SELECT * FROM NonexistentTable')
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber
,ERROR_MESSAGE() AS ErrorMessage;
END CATCH
Execute the Select Query Using EXEC makes sense
Thanks

why the same try catch block have two different results

I want to rollback all statements in the CATCH block when the error is encountered in the TRY block:
BEGIN TRY
begin transaction
create table t3(a int )
insert into t3 values(1)
insert into t3 values(1,2) --error occur
insert into t3 values(3)
END TRY
BEGIN CATCH
--just take care of rollback
IF ##TRANCOUNT <> 0
BEGIN
PRINT 'in catch,ROLLING BACK';
ROLLBACK
END
END CATCH
go
At first the error is caught since the PRINT in the CATCH block works. However, after several changes back and forth, the error seems not be caught in the CATCH any more since no more printing happens.
Therefore, I open a new query and execute the same thing. This time error can be caught again!!
Sorry about the big images
When you run this code in a tool like management studio, your transaction is mantained for your SPID (assigned to a query window).
So the inconsistent reult issue is because you don't close the transaction on all of your code paths (lets say the rollback dosn't get reached), your transaction is still active the next time you run the script.
If you add IF ##TRANCOUNT <> 0 rollback transaction to the begining of your script you will have a consistent output.
Also notice that the try catch block is not meant to catch errors at statement compile level.
If you replace your error by a division by zero for example (print 1 / 0) the catch will work properly.
In MSDN
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:
Compile errors, such as syntax errors, that prevent a batch from
running.
Errors that occur during statement-level recompilation, such as object name resolution
Errors that occur after compilation because of deferred name resolution.

RAISERROR from Catch Block in TSQL Passed to Calling Batch - Need that Passed to Calling Application

I have been researching the TRY/CATCH block and I am a little stumped on how to pass an error the way I need to do so. From what I have read and if I understand correctly, a RAISERROR in a CATCH block in SQL will be passed to the calling batch OR the calling application. The application is running a stored procedure which has a transaction in it. The transaction is wrapped in a TRY/CATCH block. In the CATCH block, I am raising the error if something in the transaction fails causing it to jump to CATCH. If running the procedure via SSMS, the error shows up fine and the transaction rolls back. However, if the application calls the stored procedure and the same error occurs, the application never knows about the error and thus doesn't now the procedure failed.
First of all, am I understanding correctly how the RAISERROR in CATCH works? If so, how can I get that error raised back to the calling application?
BEGIN TRY
BEGIN TRAN
...............
COMMIT
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK
DECLARE #ErrMsg NVARCHAR(4000)
SELECT #ErrMsg = ERROR_MESSAGE()
RAISERROR(#ErrMsg, 16, 1)
END CATCH
I am Running Windows 7, SQL Server 2005
The easiest way to do what your trying to do would be to declare #ErrMsg as an output parameter of your procedure and handle that in your calling application.
You can read up on how RAISEERROR handles it's output here if you wish to continue using it to handle your error outputs. http://msdn.microsoft.com/en-us/library/ms178592.aspx