SQL Try catch purpose unclear - sql

Let's suppose I want to inform the application about what happened / returned the SQL server. Let's have this code block:
BEGIN TRY
-- Generate divide-by-zero error.
SELECT 1/0;
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_STATE() as ErrorState,
ERROR_PROCEDURE() as ErrorProcedure,
ERROR_LINE() as ErrorLine,
ERROR_MESSAGE() as ErrorMessage;
END CATCH;
GO
and Let's have this code block:
SELECT 1/0;
My question is:
Both return the division by zero error. What I don't understand clearly is that why I should surround it with the try catch clausule when I got that error in both cases ?
Isn't it true that this error will be in both cases propagated to the client application ?

Yes, the only reason for a Try Catch, (as in ordinary code) is if you can "Handle" the error, i.e., you can correct for the error and successfully complete whatever function the procedure was tasked to do, or, if want to do something with the error before returning it to the client (like modify the message, or store it in an error log table or send someone an email, etc. (althought i'd prefer to do most of those things from the DAL layer )
Technically, however, the catch clause is not returning an error. it is just returning a resultset with error information. This is very different, as it will not cause an exception in client code. This is why your conclusion is correct, ou should just let the original error propagate directly back to the client code.
As you have written it, no error will be returned to the client. As in ordinary code, if you do not handle (correct for) the error in a catch clause, you should always rethrow it (in sql that means Raiserror function) in a catch clause. What you have done above, in general is bad, the client code may or may not have any capability to properly deal with
a completely different recordset (one with error info) from what it was expecting. Some calls (like Inserts updates or deletes) may not be expecting or looking for a returned recordset at all... Instead, if you want or need to do something with the error in the procedure before returning it to the client, use Raiserror() function
BEGIN TRY
-- Generate divide-by-zero error.
SELECT 1/0;
END TRY
BEGIN CATCH
-- Other code to do logging, whatever ...
Raiserror(ERROR_MESSAGE(), ERROR_NUMBER(), ERROR_STATE() )
END CATCH;

Both return the division by zero
error.
Yes, but using different return paths.
The difference is that in the first example, you are anticipating the error and dealing with it in some way. The error enters the application as a regular result - it is not propagated via the error handling mechanism. In fact, if the application doesn't look specifically as the shape of the result, then it may be unaware that an error has occurred.
In the second instance, the error will propagate to your application typically via an error reporting mechanism, such as an exception. This will abort the operation. How big an impact this has will depend upon the application's exception handling. Maybe it will abort just the current operation, or the entire app may fail, depending upon the app's design and tolerance to exceptions.
You choose what makes sense for your application. Can the app meaningfully handle the error - if so, propagate the error (2nd example), or is it best handled in the query (1st example), with errors being "smoothed over" by returning default results, such as an empty rowset.

Try Catch is not as useful when all you have in the try portion is a select. However if you have a transaction with multiple steps, the catch block is used to roll all the steps back and possibly to record details about what caused the problem in a log. But the most important part is the rollback to ensure data integrity.
If you are creating dynamic SQl within the Try block, it is also helpful to log the dynamic SQl variable that failed and any parameters passed in. This can help resolve some hard-to-catch, "we don't have any idea what the user actually did to cause the problem" errors.

No, by executing Select 1/0 in a TRY/CATCH block the select statement returns nothing and the select statement in the catch block displays the error details gracefully. The query completes successfully - no errors are thrown.
If you run Select 1/0 on it's own the query does not complete successfully - it bombs out with an error.
Using a catch block within SQL gives you the chance to do something about it there and then not just let the error bubble up to the application.
The only reason you see the error details is because you are selecting them. If there was no code within the Catch block you wouldn't see any error information.

Using the first method, you wont get the error from SQL Server directly
The second method may stop the execution of the statements that follow it
So it is better you catch it in advance

Related

Python: If error occurs anywhere, do specific line of code

I have a script I'm trying to write to process a large amount of data. There are, of course, potential for errors. In the script I need to connect to databases. If the script encounters an error, the code never reaches the point where the connection to the database is terminated. I'd like to have something in my python code that will recognize an error occurs, not matter where, and if nothing else at least close those databases. Does something like this exist? I know I can use try/except, but that would only work if I know exactly where I could get the error? I'm basically looking for a catchall to close my databases in the event an error occurs in a location I didn't anticipate.
To run certain cleanup code even if there is an error, use the finally block:
try:
# do stuff, possible exception
except:
# run this if exception
finally:
# always run this, even if exception
Reference: https://docs.python.org/3/tutorial/errors.html#defining-clean-up-actions

SQL Developer DBMS_OUTPUT without 'PL/SQL procedure successfully completed.'

In SQL Developer, when running some PL/SQL, when the procedure is completed, the message 'PL/SQL procedure successfully completed.' is returned.
The PL/SQL that is run may return error messages to the user if the operation could not be completed for any reason through DBMS_OUTPUT.PUT_LINE, however, the user will also see 'PL/SQL procedure successfully completed.', which could be misleading (especially if the Script Output window is small enough that the DBMS_OUTPUT is not visible).
Is there any way to have the DBMS_OUTPUT return what it should whilst also having the script not return 'PL/SQL procedure successfully completed.'?
If not, are there any alternatives in SQL Developer that I may be unaware of to provide instant personalised feedback to the user?
declare
testex exception;
begin
if 1=1 then
raise testex;
end if;
exception when testex then
dbms_output.put_line('Error msg');
end;
Below code works in my end. Did you try to run your code like below?
Copied text from a website to explain the SET FEEDBACK OFF command.
Source link: https://docs.oracle.com/cd/B19306_01/server.102/b14357/ch12040.htm
SET FEED[BACK] {6 | n | ON | OFF} -
Displays the number of records returned by a script when a script selects at least n records.
ON or OFF turns this display on or off. Turning feedback ON sets n to 1. Setting feedback to zero is equivalent to turning it OFF.
SET FEEDBACK OFF also turns off the statement confirmation messages such as 'Table created' and 'PL/SQL procedure successfully completed' that are displayed after successful SQL or PL/SQL statements.
Add a RAISE statement in the error handler to re-raise the exception so that any outer handler can deal with it:
declare
testex exception;
begin
if 1=1 then
raise testex;
end if;
exception when testex then
dbms_output.put_line('Error msg');
RAISE; -- re-raise the exception to an outer handler
end;
Best of luck.
You have coded it explicitly to complete successfully in the event of a testex exception. The code says: if this happens, then print a message and end. If you want it to actually fail then it needs to raise an exception.
I would just use something like this:
begin
if 1=1 then
raise_application_error(-20000, 'Bananas are not available on a Tuesday');
end if;
end;
/
begin
*
ERROR at line 1:
ORA-20000: Bananas are not available on a Tuesday
ORA-06512: at line 3
This will cause an actual error, rather than just printing out a message that happens to talk about an error, allowing you to control deployments and build scripts etc.
It will also roll back any uncommitted transactions within the block, which your current approach does not, and it will show the actual line number.

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

How to throw message and commit transaction using RAISERROR?

I need to use RAISERROR to throw a message(pop up message) and commit that transaction.Is there any option?
For severity 18 the transaction got rollback.I have changed the severity to 10 and tried like
RAISERROR('Your Reference Number is %s',10,0,#param);
this but it commits the transaction but doesnt show message.
What i need here is Message has to be thrown and transaction has to be commited
Any suggestion?
Don't use exceptions to pass back "OK" messages. You wouldn't in a c# or java program. Exception means "I've aborted because SHTF"
You'd use thsi to return meaningful data
SELECT 'Your Reference Number is ' + #param
In a typical template (from my answer Nested stored procedures containing TRY CATCH ROLLBACK pattern?)
SET XACT_ABORT, NOCOUNT ON
BEGIN TRY
BEGIN TRANSACTION
[...Perform work, call nested procedures...]
COMMIT TRANSACTION
SELECT 'Your Reference Number is ' + #param
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0
ROLLBACK TRANSACTION
RAISERROR [rethrow caught error using #ErrorNumber, #ErrorMessage, etc]
END CATCH
RAISERROR with severity above 10 will be treated like an exception by the ADO.Net client. Depending on how your call context looks like, this may or may not rollback the transaction. If you use a SqlTransaction or a TransactionScope in the client, or a BEGIN TRY/BEGIN CATCH block on the server side, this will likely roll back the transaction. Point is that RAISERROR neither commits nor rolls back the transaction, is your own code that rolls back or commits and we cannot know what you're doing from your post.
RAISERROR with severity bellow 10 will be considered a informational message and not cause an exception. See Database Engine Error Severities. This is probably why you say that the 'it doesn't show the message' (whatever that means). Client side frameworks treat the informational messages differently, for instance ADO.Net will raise an SqlConnection.InfoMessage event on the connection but will not raise an exception. You probably don't have anything set up in your application for this event and your code is simply ignoring the info messages. For example how to use the InfoMessage event see Connection Events (ADO.NET)
It sounds like you need to use the WITH NOWAIT parameter for RAISERROR - this will output it to the message window immediately:
RAISERROR('Your Reference Number is %s',10,0,#param) WITH NOWAIT