SQL Transactions - two inserts - sql-server

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

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 Server query optimization for stored procedure

I need to improve the performance ofa stored procedure in SQL Server 2014.
Here is my procedure and I want optimise this procedure because this running to slowly.
Can anybody explain with some examples, which kind of query is better over what and in what situation?
CREATE PROCEDURE [dbo].[sp_Customer_SendMoney]
#CustomerId bigint,
#MobileNo nvarchar(15),
#Amount money,
#Comment nvarchar(250),
#PassPhrase nvarchar(50),
#Type varchar(50) = null
AS
BEGIN
DECLARE #returnCode INT
DECLARE #RetVal BIGINT, #CustomerBalance MONEY,
#CustomerMoneyRequestedId BIGINT,
#ToCustomerId BIGINT = 0,
#TransactionId BIGINT, #CustomerAccount BIGINT,
#FromCustomerBalance MONEY = 0,
#ToCustomerBalance MONEY = 0
IF EXISTS (SELECT Id FROM Customer
WHERE Id = #CustomerId AND IsDeleted = 0 AND IsActive = 1)
BEGIN
SELECT
#CustomerBalance = Balance
FROM
Customer
WHERE
Id = #CustomerId AND IsDeleted = 0 AND IsActive = 1
select #ToCustomerId = Id, #CustomerAccount = AccountNo,#ToCustomerBalance=Balance From Customer where convert(nvarchar,DecryptByPassPhrase(#PassPhrase, MobileNo)) = #MobileNo and IsDeleted = 0 and IsActive = 1
if(#ToCustomerId > 0)
begin
if( lower(isnull(#Type,'regular')) <> 'suspention')
begin
set #ToCustomerBalance=#ToCustomerBalance+#Amount
end
END
set #FromCustomerBalance=#CustomerBalance-#Amount
if((#CustomerBalance > 0) and (#CustomerBalance >= #Amount) )
Begin
BEGIN TRAN TxnsenMoney
BEGIN TRY
select #TransactionId = TransactionW2W+1 from MstGenerateTransactionID
where [year]=datepart(yyyy,getdate()) and [month]=DATENAME(month,getdate())
update MstGenerateTransactionID set TransactionW2W= #TransactionId
where [year]=datepart(yyyy,getdate()) and [month]=DATENAME(month,getdate())
--set #TransactionId = CONVERT(bigint,replace(convert(varchar, getdate(),111),'/','') + replace(convert(varchar, getdate(),114),':',''))
IF(#ToCustomerId > 0)
BEGIN
--Update sender Customer
update Customer set Balance = Balance - #Amount where Id = #CustomerId
--Update receiver Customer
if(lower(isnull(#Type,'regular')) <> 'suspention')
begin
update Customer set Balance = Balance + #Amount where Id = #ToCustomerId
end
else
begin
update Customer set SuspentionAccount = isnull(SuspentionAccount,0) + #Amount where Id = #ToCustomerId
end
INSERT INTO [TransactionW2W]
([TransactionId]
,[FromCustomerId]
,[ToCustomerId]
,[MobileNo]
,[Amount]
,[Comments]
,[CreatedOn]
,[FromCustomerBalance]
,[ToCustomerBalance])
VALUES
(#TransactionId
,#CustomerId
,#ToCustomerId
,#MobileNo
,#Amount
,#Comment
,GETDATE()
,#FromCustomerBalance
,#ToCustomerBalance)
End --end IF #ToCustomerId > 0
ELSE
BEGIN
--Update sender Customer
update Customer set Balance = Balance - #Amount where Id = #CustomerId
--print 'ELSE'
INSERT INTO [TransactionW2W]
([TransactionId]
,[FromCustomerId]
,[ToCustomerId]
,[MobileNo]
,[Amount]
,[Comments]
,[CreatedOn]
,[FromCustomerBalance])
VALUES
(#TransactionId
,#CustomerId
,#ToCustomerId
,#MobileNo
,#Amount
,#Comment
,GETDATE()
,#FromCustomerBalance)
INSERT INTO [NewCustomer]
([FromCustomerId]
,[MobileNo]
,[Amount]
,[CreatedOn]
)
VALUES
(#CustomerId
,#MobileNo
,#Amount
,GETDATE()
)
END --end ELSE #ToCustomerId > 0
print #RetVal
IF(##TRANCOUNT >0 )
begin
set #RetVal = #TransactionId
print #RetVal
end
else
begin
RAISERROR('records not executed',16,1)
END
COMMIT TRAN TxnsenMoney
END TRY
BEGIN CATCH
ROLLBACK TRAN TxnsenMoney
set #RetVal = -1
declare #error varchar(max)
set #error= ERROR_MESSAGE()
-- RAISERROR(#error,16,1)
print #error
END CATCH
select #RetVal
End
END
End
CREATE PROCEDURE [dbo].[sp_Customer_SendMoney]
#CustomerId bigint,
#MobileNo nvarchar(15),
#Amount money,
#Comment nvarchar(250),
#PassPhrase nvarchar(50),
#Type varchar(50) = null
AS
Begin
DECLARE #returnCode INT ,#RetVal bigint,#CustomerBalance money, #CustomerMoneyRequestedId bigint,#ToCustomerId bigint = 0,#TransactionId bigint,#CustomerAccount bigint,
#FromCustomerBalance money=0,#ToCustomerBalance money=0
IF EXISTS (select Id from Customer WITH(NOLOCK) where Id = #CustomerId and IsDeleted = 0 and IsActive = 1) Begin
select #CustomerBalance = Balance from Customer WITH(NOLOCK) where Id = #CustomerId and IsDeleted = 0 and IsActive = 1
select #ToCustomerId = Id, #CustomerAccount = AccountNo,#ToCustomerBalance=Balance From Customer WITH(NOLOCK)
where convert(nvarchar,DecryptByPassPhrase(#PassPhrase, MobileNo)) = #MobileNo and IsDeleted = 0 and IsActive = 1
IF(#ToCustomerId > 0)
BEGIN
if( lower(isnull(#Type,'regular')) <> 'suspention')
BEGIN
SET #ToCustomerBalance=#ToCustomerBalance+#Amount
END
END
SET #FromCustomerBalance=#CustomerBalance-#Amount
if((#CustomerBalance > 0) and (#CustomerBalance >= #Amount) )
Begin
BEGIN TRAN TxnsenMoney
BEGIN TRY
SELECT #TransactionId = TransactionW2W+1
FROM MstGenerateTransactionID WITH(NOLOCK)
WHERE [year]=datepart(yyyy,getdate()) and [month]=DATENAME(month,getdate())
update MstGenerateTransactionID set TransactionW2W= #TransactionId
where [year]=YEAR(GETDATE()) and [month]= MONTH(GETDATE())
--set #TransactionId = CONVERT(bigint,replace(convert(varchar, getdate(),111),'/','') + replace(convert(varchar, getdate(),114),':',''))
IF(#ToCustomerId > 0) BEGIN
--Update sender Customer
UPDATE Customer set Balance = Balance - #Amount where Id = #CustomerId
--Update receiver Customer
if(lower(isnull(#Type,'regular')) <> 'suspention') BEGIN
UPDATE Customer set Balance = Balance + #Amount where Id = #ToCustomerId
END ELSE BEGIN
UPDATE Customer set SuspentionAccount = isnull(SuspentionAccount,0) + #Amount where Id = #ToCustomerId
END
INSERT INTO [TransactionW2W]
([TransactionId] ,[FromCustomerId],[ToCustomerId] ,[MobileNo] ,[Amount],[Comments],[CreatedOn],[FromCustomerBalance],[ToCustomerBalance])
SELECT #TransactionId ,#CustomerId ,#ToCustomerId ,#MobileNo ,#Amount ,#Comment,GETDATE(),#FromCustomerBalance ,#ToCustomerBalance
END --end IF #ToCustomerId > 0
ELSE BEGIN
--Update sender Customer
UPDATE Customer set Balance = Balance - #Amount where Id = #CustomerId
--print 'ELSE'
INSERT INTO [TransactionW2W]
([TransactionId] ,[FromCustomerId],[ToCustomerId] ,[MobileNo] ,[Amount] ,[Comments] ,[CreatedOn],[FromCustomerBalance])
SELECT #TransactionId ,#CustomerId ,#ToCustomerId ,#MobileNo ,#Amount ,#Comment ,GETDATE() ,#FromCustomerBalance
INSERT INTO [NewCustomer]
([FromCustomerId] ,[MobileNo] ,[Amount] ,[CreatedOn] )
SELECT #CustomerId,#MobileNo ,#Amount,GETDATE()
END --end ELSE #ToCustomerId > 0
print #RetVal
IF(##TRANCOUNT >0 )BEGIN
set #RetVal = #TransactionId
print #RetVal
END ELSE BEGIN
RAISERROR('records not executed',16,1)
END
COMMIT TRAN TxnsenMoney
END TRY
BEGIN CATCH
ROLLBACK TRAN TxnsenMoney
SET #RetVal = -1
DECLARE #error varchar(max)
SET #error= ERROR_MESSAGE()
-- RAISERROR(#error,16,1)
print #error
END CATCH
select #RetVal
End
END
End

Parallel insert stored procedure fails in SQL SERVER

The below SP fails when running parallely in multiple threads.
Tried to use different isolation levels in the SP, but still i face the same error.
Violation of UNIQUE KEY constraint
'AK_MerchantTransactionEnd_MerchantReferenceCode_BankReferenceCode'.
Cannot insert duplicate key in object 'dbo.EpayTransaction'. The
duplicate key value is (20160503171525689435, 20160329221725169, 0).
Table has UNIQUE Constraint for MerchantReferenceCode_BankReferenceCode
CREATE PROCEDURE [dbo].[uspUpdateEpayConfirmation]
#merchantReferenceCode VARCHAR(30) ,
#bankGatewayCode VARCHAR(30) ,
#transactionNumber VARCHAR(100)
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRY
DECLARE #timestamp DATETIME;
SET #timestamp = GETDATE();
IF EXISTS ( SELECT 1
FROM [dbo].EpayTransaction WITH (NOLOCK)
WHERE [dbo].EpayTransaction.[MerchantReferenceCode] = #merchantReferenceCode
)
BEGIN
RETURN 0;
END;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRANSACTION;
-- update the epayment transaction information with the merchent reference code
-- updating the status of the transaction
UPDATE [dbo].[CustomerTransaction]
SET [dbo].[CustomerTransaction].[ModifiedBy] = 1 ,
[dbo].[CustomerTransaction].[ModifiedOn] = #timestamp
WHERE [dbo].[CustomerTransaction].[MerchantReferenceCode] = #merchantReferenceCode;
-- adding a record to EpayTransaction table to conclude the transaction is successful
INSERT INTO [dbo].EpayTransaction
( [dbo].EpayTransaction.[BankReferenceCode] ,
[dbo].EpayTransaction.[BankTransactionDate] ,
[dbo].EpayTransaction.[MerchantReferenceCode]
)
VALUES ( #bankGatewayCode ,
#timestamp ,
#merchantReferenceCode
);
COMMIT TRANSACTION;
RETURN 1;
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION;
-- Raise an error with the details of the exception
DECLARE #errMsg NVARCHAR(4000) ,
#errSeverity INT;
SELECT #errMsg = ERROR_MESSAGE() ,
#errSeverity = ERROR_SEVERITY();
RAISERROR(#errMsg, #errSeverity, 1);
END CATCH;
END;

error handling inside a while loop

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

Resources