error handling inside a while loop - sql-server

I have 8 tables . One parent and 7 children . Inside the while loop and delete from table one by one.
If any error during the loop all transaction rollback . Is it possible inside the while loop.
Example :
declare #Count int, #intFlag int
begin try
set #Count = (select count(ID) from MyTable where [Date] between getdate()-1 and getdate())
if #Count > 0
begin
set #intFlag = 1
begin transaction
while (#intFlag <= #Count)
begin
update MyTable1
set column1 = 1
where [Date] between getdate()-1 and getdate();
update MyTable2
set column2 = 1
where [Date] between getdate()-1 and getdate();
set #intFlag = #intFlag + 1
end;
commit
end
end try
begin catch
if ##trancount > 0 rollback
end catch
If any error during the processes its roll back all child table transaction

try catch block above the loop like following
BEGIN TRANSACTION
BEGIN TRY
/*
* YOUR LOOP
*/
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
END CATCH

Related

Data Purging in SQL Server Table

I have the a Requirement where I have to Delete in batches in sql server , and also track the number of count affected in the end. My Sample Code is as Follows:
Declare #count int
Declare #deletecount int
set #count=0
While(1=1)
BEGIN
BEGIN TRY
BEGIN TRAN
DELETE TOP 1000 FROM --CONDITION
SET #COUNT = #COUNT+##ROWCOUNT
IF (##ROWCOUNT)=0
Break;
COMMIT
END CATCH
BEGIN CATCH
ROLLBACK;
END CATCH
END
set #deletecount=#COUNT
Above Code Works fine, but how to keep track of #deletecount if Rollback happens in one of the batch.
Some potential issues with the code :
while loop never exits/breaks
declare #tbl table(foo bit); --deleting from an empty table
declare #COUNT int;
declare #iteration int = 0;
While(1=1)
BEGIN
--loop safety net
if #iteration >= 100
begin
break --exit after 100 iterations
end
select #iteration = #iteration+1
BEGIN TRY
DELETE TOP (1000) FROM #tbl; --#tbl is empty
SET #COUNT = #COUNT+##ROWCOUNT;-- <-- this always sets ##rowcount = 1
IF (##ROWCOUNT)=0 -- ... so this can never be 0 --> will never exit the loop
Break;
END TRY
BEGIN CATCH
END CATCH
END --end while
select #iteration as iterations;
Open transaction at the end of the batch
declare #tbl table(foo bit); --<-- empty table
declare #COUNT int;
declare #myrowcount int;
declare #iteration int = 0;
While(1=1)
BEGIN
--loop safety net
if #iteration >= 100
begin
break --exit after 100 iterations
end
select #iteration = #iteration+1
BEGIN TRY
BEGIN TRAN --<-- transaction begins on each iteration
DELETE TOP (1000) FROM #tbl; --#tbl is empty
set #myrowcount = ##rowcount;
SET #COUNT = #COUNT+#myrowcount;
IF (#myrowcount)=0
Break; --<-- if this breaks... transaction is neither committed nor rolledback
COMMIT --<-- commited on each iteration, when no error
END TRY
BEGIN CATCH
ROLLBACK --<-- or rolled back on each iteration error
END CATCH
END --end while
select #iteration as iterations, ##trancount as open_transactions;
rollback transaction;
loop keeps trying to delete the same erroneous batch (over and over again) and never exits
create table parent (id int primary key clustered);
create table child (parentid int references parent(id));
go
--some parents
insert into parent(id)
select top (200) row_number() over(order by ##spid)
from sys.all_objects;
--and a child
insert into child(parentid) values (75)
go
declare #COUNT int;
declare #myrowcount int;
declare #errormsg nvarchar(max);
declare #iteration int = 0;
While(1=1)
BEGIN
--loop safety net
if #iteration >= 100
begin
break --exit after 100 iterations
end
select #iteration = #iteration+1
BEGIN TRY
DELETE TOP (10) t --changed the batch size..for the example
from (select top (10000) * from parent order by id) as t;
set #myrowcount = ##rowcount;
SET #COUNT = #COUNT+#myrowcount;
IF (#myrowcount)=0
Break;
END TRY
BEGIN CATCH
select #errormsg = isnull(#errormsg, '') + error_message();
END CATCH
END --end while
select #iteration as iterations, #errormsg as errormsg;
--some parents have been deleted (8th batch kept failing, there is an fk to parent 75)
select *
from parent
go
drop table child
drop table parent
go
You could rectify all the above issues...(last one, to skip erroneous batches being a bit more "difficult"), by implementing logic in the where clause (eg. exclude rows which are referenced etc) according to your model. Trying to implement a straight deletion, without any rules, and skipping failures makes it a bit harder.
Just an example: when a batch fails, after 3 attempts, take another deletion route. Since each deletion is atomic, transactions are not really needed here (besides, if the batch is called from a parent module, a plain ROLLBACK in the batch would "invalidate" any transactions opened in the parent module, before the batch execution)
create table parent (id int primary key clustered);
create table child (parentid int references parent(id));
go
--some parents
insert into parent(id)
select top (200) row_number() over(order by ##spid)
from sys.all_objects;
--and two children
insert into child(parentid) values (75), (115) --, (9), (18) --: add 9 and 18 and nothing gets deleted, alternative route in the example does not work
go
declare #COUNT int;
declare #myrowcount int;
declare #iteration int = 0;
declare #errormsg nvarchar(max);
declare #ierrorcnt int=0;
declare #deletedids table(id int);
declare #lastsuccessfullydeletedid int = 0;
select #COUNT = 0;
While(1=1)
BEGIN
--loop safety net
if #iteration >= 100
begin
break --exit after 100 iterations
end
select #iteration = #iteration+1
BEGIN TRY
--when 10 consecutive errors in a single iteration..just quit the loop
if #ierrorcnt >= 10
begin
break;
end
BEGIN TRAN --<-- transaction begins on each iteration
if #ierrorcnt >= 3 --when 3 consecutive errors in the iteration..try to bypass the current batch
begin
delete top (10) t
output deleted.id into #deletedids(id)
from (select top (10) * from (select top (2*10) * from parent where id > #lastsuccessfullydeletedid order by id) as a order by id desc) as t
select #myrowcount = count(*), #lastsuccessfullydeletedid = max(id)
from #deletedids;
delete from #deletedids;
end
else
begin
DELETE TOP (10) FROM parent where id > #lastsuccessfullydeletedid;
set #myrowcount = ##rowcount;
end
SET #COUNT = #COUNT+#myrowcount;
COMMIT --<-- commited on each iteration, when no error
IF (#myrowcount)=0 and #ierrorcnt = 0
Break;
set #ierrorcnt = 0; --everything ok, set iteration error counter to 0
END TRY
BEGIN CATCH
ROLLBACK --<-- or rolled back on each iteration error
if #ierrorcnt = 0
begin
select #errormsg = isnull(#errormsg, '') +';'+ error_message();
end
set #ierrorcnt = #ierrorcnt + 1; --error, increase the iteration error counter
END CATCH
END --end while
select #iteration as iterations, ##trancount as open_transactions;
select #iteration as iterations, #errormsg as errormsg;
--some parents have been deleted
select * /*2 failed batches, 20 rows left*/
from parent
select #COUNT as [count/deleted] --<--this is also the deleted count
go
drop table child
drop table parent
go
..without any errors, in your original code, #count = #deletedcount
declare #tbl table(foo int); --<-- empty table
insert into #tbl(foo)
select top (100000) row_number() over(order by ##spid)
from sys.all_objects as a
cross join sys.all_objects as b;
--batch 1000, max iterations for the #tbl count
declare #maxiterations int;
select #maxiterations = 1+count(*) / 1000
from #tbl
declare #COUNT int;
declare #deletedcount int;
declare #myrowcount int;
declare #iteration int = 0;
select #COUNT = 0, #deletedcount = 0;
While(1=1)
BEGIN
--loop safety net
if #iteration >= #maxiterations
begin
break --exit after #maxiterations
end
select #iteration = #iteration+1;
BEGIN TRY
BEGIN TRAN
DELETE TOP (1000) FROM #tbl
where foo%5 = 0 ;
set #myrowcount = ##rowcount;
SET #COUNT = #COUNT+#myrowcount;
set #deletedcount = #deletedcount + #myrowcount;
COMMIT
IF #myrowcount=0
Break;
END TRY
BEGIN CATCH
ROLLBACK
END CATCH
END --end while
select #iteration as iterations, ##trancount as open_transactions;
select #count as _count, #deletedcount as deletedcount;

Insert values statement can contain only constant literal values or variable references error while running below procedure

I am getting:
Insert values statement can contain only constant literal values or variable references error
while running the below procedure at insertion in dbo.DB_time_log table.
CREATE PROCEDURE [dbo].[sp_chm_dedupe_import] AS
BEGIN
SET NOCOUNT ON;
DECLARE #StartTime DATETIME;
DECLARE #EndTime DATETIME;
SET #StartTime = GETDATE();
PRINT 'Procedure Started at :'
+ RTRIM(CAST(GETDATE() AS nvarchar(30)));
BEGIN TRAN
BEGIN TRY
INSERT INTO [CHM].[UCIC_PAN_INDIA_TEST_TS]
SELECT
CAST(UCID AS bigint),CAST(UCIC_OLD AS bigint),CAST(HID AS bigint),
CAST(HID_OLD AS bigint),CAST(CID AS VARCHAR(20)),CAST(BU AS VARCHAR(10)),
CAST(TIME_STAMP AS VARCHAR(20))
FROM [dbo].[CHM_EXT_All_India_DL]
OPTION (LABEL = 'CHM_AllIndia_EXT_Data_Load');
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
BEGIN
ROLLBACK TRAN;
PRINT 'ROLLBACK';
END
END CATCH;
IF ##TRANCOUNT >0
BEGIN
PRINT 'COMMIT';
COMMIT TRAN;
END
SET #EndTime = GETDATE();
INSERT INTO dbo.DB_time_log
VALUES
(
'SP_CHM_DEDUPE_IMPORT',
#StartTime,
#EndTime,
GETDATE()
);
PRINT 'Procedure Ended at :'
+ RTRIM(CAST(GETDATE() AS nvarchar(30)));
SET NOCOUNT OFF;
END
GO

SQL Transactions - two inserts

I wrote a SQL procedure that inserts data into two tables. I started with begin transaction but some data violate constraint of second table and in the end data are added into first table, but not in second. What is wrong with my code?
create procedure [dbo].[procAddConference]
#Conf_name varchar(50),
#Begin_date date,
#End_date date,
#Price money,
#Student_disc numeric(3,2),
#Limit int,
#Disc numeric(3,2),
#Until int
as
set nocount on
begin transaction;
begin try
IF (#Begin_date > #End_date or #Limit < 0 or #Disc <0 or #Disc >1 or #Student_disc < 0 or #Student_disc > 1)
BEGIN
; throw 52000, 'Wrong data', 1
END
insert into Conferences (ConferenceName, BeginDate, EndDate, Price, StudentDiscount)
values (#Conf_name, #Begin_date, #End_date, #Price, #Student_disc)
IF ##ERROR <> 0
begin
RAISERROR('Error, transaction not completed!',16,-1)
rollback transaction;
end
declare #confID INT
set #confID = ##IDENTITY
declare #duration int
declare #iterator int
set #duration = DATEDIFF(dd, #Begin_date, #End_date)
set #iterator = 0
while #iterator <= #duration
begin
insert into ConferenceDay (ConferenceID, Date, Limit)
values (#confID, cast(DATEADD(dd, #iterator, #Begin_date) as date), #Limit)
IF ##ERROR <> 0 or ##ROWCOUNT = 0
begin
rollback transaction;
RAISERROR('Error, transaction not completed!',16,-1)
end
set #iterator = #iterator + 1
end
commit transaction;
END TRY
BEGIN CATCH
DECLARE #errorMsg nvarchar (2048) = 'Cannot add conference . Error message : ' + ERROR_MESSAGE () ;
; THROW 52000 , #errorMsg ,1
rollback transaction;
END CATCH
END

Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 2

I writed this trigger:
ALTER TRIGGER [dbo].[trg_abort_insert]
ON [dbo].[F_DOCCURRENTPIECE]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON
SET XACT_ABORT ON
BEGIN TRY
BEGIN TRAN
IF EXISTS (SELECT *
FROM Inserted i
INNER JOIN Deleted d ON i.CBMARQ= d.CBMARQ
WHERE i.DC_Piece <> d.DC_Piece
AND i.DC_Domaine = 0
AND i.DC_IdCol = 6)
COMMIT TRAN
END TRY
BEGIN CATCH
SELECT ERROR_MESSAGE()
IF ##TRANCOUNT>0
ROLLBACK
END CATCH;
END
I'am having this error:
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 2
Pleaze help me
This is because you have nested transaction. I mean a transaction was open when trigger was fired. You need to modify your trigger to handle nested transaction like mentioned below :
create TRIGGER [dbo].[trg_abort_insert]
ON [dbo].[F_DOCCURRENTPIECE]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON
SET XACT_ABORT ON
declare #trancount int = ##trancount
BEGIN TRY
if #trancount > 0
begin
save transaction t1
end
else
begin
begin transaction
end
IF EXISTS (SELECT *
FROM Inserted i
INNER JOIN Deleted d ON i.CBMARQ= d.CBMARQ
WHERE i.DC_Piece <> d.DC_Piece
AND i.DC_Domaine = 0
AND i.DC_IdCol = 6)
if #trancount = 0
begin
COMMIT TRAN
end
END TRY
BEGIN CATCH
if #trancount > 0
rollback transaction t1
if #trancount = 0
rollback transaction
SELECT ERROR_MESSAGE()
END CATCH;
end
This should work for you. Let me know if this helps.

How to rollback if any update is not success?

I have written a transaction like:
BEGIN TRAN
UPDATE [Table1]
SET [Name] = 'abcd'
WHERE [ID] = 1
UPDATE [Table2]
SET [Product] = 'efgh'
WHERE [ID] = 10
UPDATE [Table3]
SET [Customar] = 'ijkl'
WHERE [ID] = 11
Now I want to rollback if any UPDATE is not success. For example in Table2 if there is no Product with ID=10 the transaction should be rolled back. How to do this?Please note that I am using SQLServer 2000.
SQL Server 2000. You don't need rollback if you use SET XACT_ABORT ON
SET XACT_ABORT ON --to ensure rollback
BEGIN TRAN -- ##TRANCOUNT + 1
UPDATE [Table1]
SET [Name] = 'abcd'
WHERE [ID] = 1
IF ##ROWCOUNT = 0 ROLLBACK TRAN
IF ##TRANCOUNT > 0
BEGIN
UPDATE [Table2]
SET [Product] = 'efgh'
WHERE [ID] = 10
IF ##ROWCOUNT = 0 ROLLBACK TRAN
END
IF ##TRANCOUNT > 0
BEGIN
UPDATE [Table3]
SET [Customar] = 'ijkl'
WHERE [ID] = 11
IF ##ROWCOUNT = 0 ROLLBACK TRAN
END
IF ##TRANCOUNT > 0 COMMIT TRAN
Before each UPDATE statement you need to do BEGIN TRAN and after each of your UPDATE statement, you need to do this -
if ##Error > 0
THEN
BEGIN
ROLLBACK TRAN
END
Else
BEGIN
COMMIT TRAN
END

Resources