I created a stored procedure that has 9 inserts in summarized tables and the last statement is a delete.
This procedure takes around 9 minutes to execute for each given date, and one thing that I noticed is that if an error occurs and the first 3 inserts executed, it keeps the inserted data. To handle this, I created a begin try and begin transaction and a test that I did was to start the procedure using SSMS and after it started, I cancelled the command but the transaction was kept. How can I avoid this?
the procedure body is very simple... something like:
insert into...
insert into...
insert into...
insert into...
insert into...
insert into...
insert into...
insert into...
insert into...
insert into...
delete from....
thanks
Two things:
1) The basic syntax is:
BEGIN TRY
BEGIN TRAN
INSERT...
DELETE...
COMMIT TRAN
END TRY
BEGIN CATCH
IF (##TRANCOUNT > 0)
BEGIN
ROLLBACK TRAN
END
DECLARE #ErrorMessage NVARCHAR(4000)
SET #ErrorMessage = 'Line ' + CONVERT(VARCHAR(20), ERROR_LINE()) + ': '
+ ERROR_MESSAGE()
RAISERROR(#SQLErrString, 16, 1)
END CATCH
2) If you cancel a batch in process, you might need to run the ROLLBACK TRAN manually. You can test if you need to do this by doing a SELECT ##TRANCOUNT and if it is > 0 then call ROLLBACK TRAN.
Related
I have to insert and delete from multiple tables and I wanted to do all these in one single stored procedure so can I have the multiple transactions blocks as below and what would be the best way to handle errors in each transaction.Please advise
BEGIN TRANSACTION [TRANS1]
BEGIN TRY
Insert into ArchiveTable select * from Completed (nolock)
delete from Completed(nolock) where convert(date,LastModified,101) > getdate()
COMMIT TRANSACTION [TRANS1]
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION [TRANS1]
PRINT ERROR_MESSAGE()
END CATCH
BEGIN TRANSACTION [TRANS2]
BEGIN TRY
Insert into ArchiveTable2 select * from New (nolock)
delete from New (nolock) where convert(date,LastModified,101) > getdate()
COMMIT TRANSACTION [TRANS2]
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION [TRANS2]
PRINT ERROR_MESSAGE()
END CATCH
I am using Stored procedures to do CRUD operations on Mysql DB
DELIMITER $$
CREATE PROCEDURE `transaction_sp` ()
BEGIN
DECLARE exit handler for sqlexception
BEGIN
-- ERROR
ROLLBACK;
END;
DECLARE exit handler for sqlwarning
BEGIN
-- WARNING
ROLLBACK;
END;
START TRANSACTION;
INSERT INTO table_name (id, name, address) values ('1','Test','xpert.com');
SET #LID = LAST_INSERT_ID();
INSERT INTO table_name3 (id, job, responsibilities) values
(#LID,'Sr. Developer','Coding,mentoring etc');
COMMIT;
END
$$
Now, i want that if Second INSERT statement fails then second SQL statement shall not execute and first Insert shall be rolled back.
With my above approach 1st transaction is not rolled back. Do i need to set any flags?
How to handle it? Well explained answer will help here.
Please see my answer below. I believe if you check ##ROWCOUNT or ##Error after each insert you can rollback if there is no rows affected. Sorry if the method I used is wrong.
DELIMITER $$
CREATE PROCEDURE `transaction_sp` ()
BEGIN
DECLARE #nRowCount1 int,
#nRowCount2 int
DECLARE exit handler for sqlexception
BEGIN
-- ERROR
ROLLBACK;
END;
DECLARE exit handler for sqlwarning
BEGIN
-- WARNING
ROLLBACK;
END;
START TRANSACTION;
INSERT INTO table_name (id, name, address) values ('1','Test','xpert.com');
SET #LID = LAST_INSERT_ID();
SET #nRowCount1 = ##ROWCOUNT ;
INSERT INTO table_name3 (id, job, responsibilities) values
(#LID,'Sr. Developer','Coding,mentoring etc');
SET #nRowCount2 =##ROWCOUNT
if(#nRowCount1 <=0 and #nRowCount2<=0)
begin
ROLLBACK ;
end
COMMIT;
END
$$
The goal here is to write a script attempts to insert a new category into categories table and if the insert was successful the message:
1 row was inserted
If the update is unsuccessful, the procedure should display this message:
Row was not inserted - duplicate entry
Whenever I run this script, I keep getting the second message no matter how many times I run it, when really it should display the first message, followed by the second. Here is my script. Can someone please help me spot what I'm overlooking? Thank you.
use my_guitar_shop;
/*drop procedure if exists add_category;*/
DELIMITER //
CREATE PROCEDURE add_category(
in categories VARCHAR(100)
)
begin
declare duplicate_entry_for_key TinyINT DEFAULT FALSE;
declare continue handler for 1062
SET duplicate_entry_for_key = TRUE;
insert into categories values (5, 'Electric');
select '1 row was inserted.' as message;
if duplicate_entry_for_key = true then
select 'Row was not inserted - duplicate entry.' as message;
end if;
end //
DELIMITER ;
/* call the stored procedure with 'Gibson' */
call add_category('Gibson');
call add_category('Gibson');
You should set duplicate_entry_for_key to True. If data insert is successful.
Do something like below after insert operation.
SET duplicate_entry_for_key = last_insert_id();
OR see the below example for bit check.
For example:
START TRANSACTION; -- Begin a transaction
INSERT INTO categories
VALUES
(
5
,'Electric'
);
IF ROW_COUNT() > 0 THEN -- ROW_COUNT() returns the number of rows updated/inserted/deleted
SET duplicate_entry_for_key = TRUE;
COMMIT; -- Finalize the transaction
ELSE
SET duplicate_entry_for_key = False;
--You can ROLLBACK the transaction also - Revert all changes made before the transaction began
END IF
Suppose I have more than one SQL to change the data in database in a SP. I create a transaction at the beginning, then commit or roll back the transaction at the end. What I did is something like:
declare #HasError bit
BEGIN TRANSACTION
set #HasError = 0;
Insert into Table1....
if(##ERROR>0)
set #HasError = 1;
Insert into Table2....
if(##ERROR>0)
set #HasError = 1;
Insert into Table3....
if(##ERROR>0)
set #HasError = 1;
...
if #HasError = 1
Rollback;
else
Commit;
It's working fine. But need to capture error for each U/I/D sql. Is there anyway I can know if there is any error at the end for only one piece of code like
if(##ERROR>0)
set #HasError = 1;
if #HasError = 1
Rollback;
else
Commit;
No need to do so many error detection for each U/I/D sql?
Not sure I follow the question, but something like this might work for you
BEGIN TRAN
BEGIN TRY
Insert into Table1....
Insert into Table2....
Insert into Table3....
END TRY
BEGIN CATCH
ROLLBACK TRAN
RETURN
END CATCH
COMMIT TRAN
I have a huge script for creating tables and porting data from one server. So this sceipt basically has -
Create statements for tables.
Insert for porting the data to these newly created tables.
Create statements for stored procedures.
So I have this code but it does not work basically ##ERROR is always zero I think..
BEGIN TRANSACTION
--CREATES
--INSERTS
--STORED PROCEDURES CREATES
-- ON ERROR ROLLBACK ELSE COMMIT THE TRANSACTION
IF ##ERROR != 0
BEGIN
PRINT ##ERROR
PRINT 'ERROR IN SCRIPT'
ROLLBACK TRANSACTION
RETURN
END
ELSE
BEGIN
COMMIT TRANSACTION
PRINT 'COMMITTED SUCCESSFULLY'
END
GO
Can anyone help me write a transaction which will basically rollback on error and commit if everything is fine..Can I use RaiseError somehow here..
Don't use ##ERROR, use BEGIN TRY/BEGIN CATCH instead. See this article: Exception handling and nested transactions for a sample procedure:
create procedure [usp_my_procedure_name]
as
begin
set nocount on;
declare #trancount int;
set #trancount = ##trancount;
begin try
if #trancount = 0
begin transaction
else
save transaction usp_my_procedure_name;
-- Do the actual work here
lbexit:
if #trancount = 0
commit;
end try
begin catch
declare #error int, #message varchar(4000), #xstate int;
select #error = ERROR_NUMBER(), #message = ERROR_MESSAGE(), #xstate = XACT_STATE();
if #xstate = -1
rollback;
if #xstate = 1 and #trancount = 0
rollback
if #xstate = 1 and #trancount > 0
rollback transaction usp_my_procedure_name;
raiserror ('usp_my_procedure_name: %d: %s', 16, 1, #error, #message) ;
return;
end catch
end
As per http://msdn.microsoft.com/en-us/library/ms188790.aspx
##ERROR: Returns the error number for the last Transact-SQL statement executed.
You will have to check after each statement in order to perform the rollback and return.
Commit can be at the end.
HTH
Avoid direct references to '##ERROR'.
It's a flighty little thing that can be lost.
Declare #ErrorCode int;
... perform stuff ...
Set #ErrorCode = ##ERROR;
... other stuff ...
if #ErrorCode ......