A simple transaction doesn't work on SQL Server 2005 - sql-server-2005

I am doing this in SQL Server 2005. I have a table, there is only one column of type int, o you cannot insert char in it.
DECLARE #intErrorCode INT
BEGIN TRAN
Update TestTable set A='a' where A=3 --UPDATE TO CHAR, FAIL
SELECT #intErrorCode = ##ERROR
IF (#intErrorCode <> 0) GOTO PROBLEM
COMMIT TRAN
PROBLEM:
IF (#intErrorCode <> 0) BEGIN
PRINT 'Unexpected error occurred!'
ROLLBACK TRAN
END
I am expecting to see 'Unexpected error occurred!' as result.
But what I actually see is:
Conversion failed when converting the varchar value 'a' to data type int.
It seems that my SQL Server doesn't take my code as a transaction at all... It hit the fail line and quit running right after that.
What am I doing wrong here?

First use Try catch blocks instead.
However, no sql error trapping will work for all types of errors.
Read about error handling using the try catch block in Books online and you will see a discussion of what types of errors are not trapped.
Also it is better to return the actual error than a general message usually. It is much easier to track down teh error issue when you know the real error.

Related

Linkserver update not working in Transaction

I am trying to do operation on my linked server
created one linked server , everything is ok
just observed one thing which I am not able to understand
if I run
update SERVER.testdb.[AAA].MyTable
set MyName= 'abc#pqr.com'
where ID=999
This works fine ,but
when i execute same code in following way
BEGIN TRY
BEGIN TRANSACTION TRF
update SERVER.testdb.[AAA].MyTable
set MyName= 'abc#pqr.com'
where ID=999
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION TRF
END CATCH
It shows The parameter is incorrect
Can anyone tell me why this is hapeening and what is solution ?

Execute update clause even with errors

I'm doing some script testing and I want to make sure that if the Update Clause I'm testing in this Try Catch :
BEGIN TRY
BEGIN TRAN
UPDATE NAME
SET NAME.ADDBY =
(CASE WHEN NAME.ADDBY = 'CONVERSION' THEN 'CONVERTED'
WHEN NAME.ADDBY = 'CJDOG'THEN 'CJDAREME'
WHEN NAME.ADDBY = 'npalerm' THEN 'REALLYLONGDETAILEDTEXT'
ELSE NAME
END)
COMMIT TRAN
END TRY
BEGIN CATCH
IF ##TRANCOUNT >0
PRINT ERROR_MESSAGE()
ROLLBACK TRAN
END CATCH
PRINT ##TRANCOUNT
fails, that other rows are updated and only the ones that didn't error go through.
Currenty because it is in the TRY CATCH there is a ROLLBACK that makes sure nothing goes through.
But when I try to run just the UPDATE part the errors terminates the whole script, instead of updating those that do not cause an error, in this case npalerm is too long in the case.
Any Ideas?
I know its a long desc I could've just included the Update but for detailed purpose I included it in the Catch.
In order to update the rows individually and catch the errors on each row rather than catching errors on the entire table/view update, you would need to wrap the whole thing in a cursor.
Be aware that cursors are more resource intensive and slower than set-based commands.

When does RAISERROR fire in a stored procedure?

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.
);

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.

SQL Try catch purpose unclear

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