Set output parameter when commit transaction fails - sql

I need to roll back the transaction whenever any one of the query fails. If all the transaction is OK, it has to set the output parameter.
So far I have done this.
create PROCEDURE [dbo].[sp_InsertAll]
-- Add the parameters for the stored procedure here
#WO_Type varchar(25),
#WO_Operation varchar(25),
#WO_Source varchar(25),
#RETVAL BIT OUT
AS
BEGIN
SET NOCOUNT ON;
SET #RETVAL = 0
SET XACT_ABORT ON
BEGIN TRAN;
INSERT INTO tblTabl1(WO_Type , WO_Operation , WO_Source )
VALUES (#WO_Type, #WO_Operation, #WO_Source, )
IF #UPDATESOURCE = 1
BEGIN
UPDATE tblT2
SET SM_SaddleStatus = #SOURCESTATUS
WHERE SM_SaddleID = #WO_SourceID
END
IF #UPDATEDESTINATION = 1
BEGIN
UPDATE tblT3
SET SM_SaddleStatus = #DESTINATIONSTATUS
WHERE SM_SaddleID = #WO_DestinationID
END
SET #RETVAL = 1
COMMIT TRAN;
END
Is this the right way to return value? Is there any problem with the method. So far it is working fine for me. Before moving to production I need to cross check.

This is what I'd suggest, using a TSQL Try Catch block to roll back the transaction and give you a different return value:
create PROCEDURE [dbo].[sp_InsertAll]
-- Add the parameters for the stored procedure here
#WO_Type varchar(25),
#WO_Operation varchar(25),
#WO_Source varchar(25),
--#RETVAL BIT OUT
AS
BEGIN
SET NOCOUNT ON;
SET #RETVAL = 0
SET XACT_ABORT ON
BEGIN TRAN;
BEGIN TRY
INSERT INTO tblTabl1(WO_Type , WO_Operation , WO_Source )
VALUES (#WO_Type, #WO_Operation, #WO_Source, )
IF #UPDATESOURCE = 1
BEGIN
UPDATE tblT2
SET SM_SaddleStatus = #SOURCESTATUS
WHERE SM_SaddleID = #WO_SourceID
END
IF #UPDATEDESTINATION = 1
BEGIN
UPDATE tblT3
SET SM_SaddleStatus = #DESTINATIONSTATUS
WHERE SM_SaddleID = #WO_DestinationID
END
return 0
COMMIT TRAN;
END TRY
BEGIN CATCH
rollback Tran;
return -1
END CATCH
END

Related

A lot of queries get suspended. What should I do?

I'm handling an application used by around 2000 people. Everyday users insert around 300 rows/user at the same time (around 7 am to 11 am). I handle it using stored procedures that contain only insert statements, but I use begin tran to prevent the primary key getting duplicate.
Currently suspended transactions frequently happen, so my stored procedure takes around 1-2minutes to be done and this causes our users to wait for a long time to insert every data.
I already checked:
Disk speed normal around read : 600mb/s write 744mb/s.
Procesor usage between 20 - 40 % with 10 cores.
Memory usage only 6gb, I used 12gb of memory.
Check from sys.dm_exec_requests,sp_who2, and sys.dm_os_waiting_tasks.
Result from number 3 is I found that my stored procedure suspends each other (same stored procedures different executor)
This is my stored procedures (Sorry, for the naming, because it is confidential for my company):
ALTER PROC [dbo].[SP_DESTIONATION_TABLE_INSERT]
{params}
WITH RECOMPILE
AS
BEGIN
BEGIN TRAN InsertT
DECLARE #ERRORNMBR INT
SET #ERRORNMBR = 0
IF #T = ''
BEGIN
-------------------------------------------------
DECLARE #TCID VARCHAR(15)
SELECT #TCID = ID
FROM DESTIONATION_TABLE
WHERE NIK = #NIK AND
CUSTOMERID = #CUSTOMERID AND
CUSTOMERTYPE = #CUSTOMERTYPE AND --edit NvA 20180111
DATEDIFF(day,DATE,#DATE) = 0
--IF THERE IS ALREADY A CALL IN SERVER
IF #TCID IS NOT NULL
BEGIN
IF #INTERFACE <> 'WEB' BEGIN
--GET EXISTING CALL ID
SET #ID = #TCID
BEGIN TRAN UBAH
UPDATE DESTIONATION_TABLE
SET
columns=value
WHERE ID = #ID
AND employeeid = #employeeid
AND CUSTOMERID = #CUSTOMERID
SET #ERRORNMBR = #ERRORNMBR + ##ERROR
IF #ERRORNMBR = 0
BEGIN
COMMIT TRAN UBAH
SELECT
columns
FROM DESTIONATION_TABLE WHERE ID = #ID
END
ELSE
BEGIN
ROLLBACK TRAN UBAH
END
END
COMMIT TRAN InsertT
RETURN
END
--------------------------------------------------
-- CHECK #DEVICECONTROLID
IF #DEVICECONTROLID IS NOT NULL
AND #INTERFACE <> 'WEB'
AND EXISTS(SELECT 1 FROM DESTIONATION_TABLE WHERE DEVICECONTROLID = #DEVICECONTROLID)
BEGIN
IF NOT EXISTS(SELECT 1 FROM DESTIONATION_TABLE_TEMP WHERE DEVICECONTROLID = #DEVICECONTROLID)
BEGIN
INSERT INTO DESTIONATION_TABLE_TEMP
(COLUMNS)
VALUES
(VALUES)
END
SELECT * FROM DESTIONATION_TABLE WHERE _DEVICECONTROLID = #_DEVICECONTROLID
END
ELSE
BEGIN
some logic to make primary key formula{string+date+employeeid+increment}
END
END
ELSE
BEGIN
BEGIN TRAN UBAH
IF #PARAMS = 'WEB'
BEGIN
UPDATE DESTIONATION_TABLE
SET
COLUMNS = PARAMS
WHERE ID = #ID
END
ELSE IF PARAMS = 'MOBILE'
BEGIN
UPDATE DESTIONATION_TABLE
SET
COLUMNS = PARAMS
WHERE ID = ID
END
SET #ERRORNMBR = #ERRORNMBR + ##ERROR
IF #ERRORNMBR = 0
BEGIN
COMMIT TRAN UBAH
SELECT
COLUMNS
FROM DESTIONATION_TABLE WHERE ID = ID
END
ELSE
BEGIN
ROLLBACK TRAN UBAH
END
END
COMMIT TRAN InsertT
END
I need a suggestion, what should I check next to get what's wrong with my server is.
Is begin tran is the issue here?
I'm not an expert but a googling of "BEGIN TRAN" seems to show that it "locks a table until the transaction is committed with a "COMMIT TRAN" ... meaning that it is a blocking process. So if you're table is locked when all these inserts are attempting to execute, some of them would obviously not be successful.
https://www.mssqltips.com/sqlservertutorial/3305/what-does-begin-tran-rollback-tran-and-commit-tran-mean/
I would suggest building a work queue on its own thread that listens for INSERTS/UPDATES. Then the queue can empty into the DB at it's leisure.

how to catch multiple transaction error and set it to output parameter

I am new to T-SQL programming. I have write the following sub program to implement two transactions. I need to use three output parameter to record the error. I need to initially set #P_Return_Status = 'S'.
If On error, in the CATCH block, I need to ROLLBACK the transaction; Set #P_Return_Status = ‘E’;Set #P_Error_Code = Error_Number();Set #P_Error_Messages = Error_Message()
How could I output the error for multiple (here is two) transactions and being caught by the main program. Forgive me if my code are totally wrong since I am very new to T-SQL.
IF OBJECT_ID('dbo.sub_Initialize') IS NULL -- check if SP Exist
EXE('CREATE PROCEDURE dbo.sub_Initialize AS SET NOCOUNT ON;')
GO
ALTER PROCEDURE dbo.sub_Initialize
(
#P_CurrentPeriod VARCHAR(12)
#P_Return_Status VARCHAR(1) OUT,
#P_Error_Code INT OUT,
#P_Error_Messages VARCHAR(2000) OUT
)
AS
BEGIN
SET #P_Return_Status VARCHAR(1) = 'S'
SET #P_Error_Code INT = NULL
SET #P_Error_Messages VARCHAR(2000) = NULL
BEGIN TRANSACTION
BEGIN TRY
IF EXISTS (SELECT 1 FROM Rollup_Elemental_Costs WHERE Rpt_Period = #P_CurrentPeriod)
DELETE FROM Rollup_Elemental_Costs
WHERE Rpt_Period = #P_CurrentPeriod
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
SET #P_Return_Status = ‘E’
SET #P_Error_Code = ERROR_CODE()
SET #P_Error_Messages = ERROR_MESSAGE()
END CATCH
BEGIN TRANSACTION
BEGIN TRY
IF EXISTS (SELECT 1 FROM Rollup_Batch_Detail_Trx WHERE Rpt_Period = #P_CurrentPeriod)
DELETE FROM Rollup_Batch_Detail_Trx
WHERE Rpt_Period = #P_CurrentPeriod
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
SET #P_Return_Status = 'E'
SET #P_Error_Code = ERROR_NUMBER()
SET #P_Error_Messages = ERROR_MESSAGE()
END CATCH
END
I would suggest you to create a table for storing error messages. Then insert each transaction error message into the table so that you can query the error table later to find out the error messages in transaction.
Create table error_Message
(
Transaction_identification Varchar(20) --To identify which trans generated error message
Return_Status VARCHAR(1),
Error_Code INT,
Error_Messages VARCHAR(2000)
)
You may have to add some more columns to identify transaction details
Note : If you don't want to store error messages in physical table then convert the same to temp table inside procedure. Then at the end of procedure you can return the temp table results
Then inside CATCH block you can insert into the error_Message table
BEGIN CATCH
ROLLBACK TRANSACTION;
Insert into error_Message(Transaction_identification,Return_Status,Error_Code,Error_Me
select 'Trans 1',‘E’,ERROR_CODE(),ERROR_MESSAGE()
END CATCH

Returns of an Update Stored Procedure

I have this stored procedure on my SQL SERVER 2008:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[SAVE1]
#IDKEY float,
#CPF nvarchar(20)
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
UPDATE people SET NINUMBER = #CPF WHERE IDKEY = (SELECT IDKEY from contact where IDKEY= #IDKEY)
SELECT 1 as retorno
END TRY
BEGIN CATCH
SELECT 0 as retorno
END CATCH
END
But if I execute with a invalid IDKEY, the return is "1", but I thought would enter in the "CATCH" and return "0". What I'm doing wrong?
It won't throw an exception in this case. Your subquery simply would return no records, and therefore your update statement would execute against zero records. It would not fail though.
Your query is not failing instead it is returning no rows and hence it is returning 1. Try like this:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[SAVE1]
#IDKEY float,
#CPF nvarchar(20)
AS
BEGIN
SET NOCOUNT ON;
UPDATE people SET NINUMBER = #CPF WHERE IDKEY = (SELECT IDKEY from contact where IDKEY= #IDKEY)
END
IF (##ROWCOUNT > 0)
BEGIN
SELECT 1 as retorno
END
ELSE
BEGIN
SELECT 0 as retorno
END

Concurrent table creation

Have a situation when table can be created from different places.
So, I have ~10 working applications, that can simultaneously try to create a same table.
Question. How can I synchronize them ? So I don't have any exceptions or errors ?
All instances of the application are trying to create a new table when the day ends, so when there is something like 00:00:00 they all will try to create it.
Sorry, for possible 'stupid question', have been googling for a while, no results.
Thank you.
You can use sp_getapplock to take arbitrary locks. You could make your app take such a lock before creating the table. Like that:
exec sp_getapplock
if tabledoesnotexist
create table ...
As alluded to in the comments, your first step is to perform an existence check. Then, on the off chance that there are two simultaneous creations you can use TRY...CATCH.
IF Object_ID('test', 'U') IS NULL
BEGIN
BEGIN TRY
CREATE TABLE test ( a int )
END TRY
BEGIN CATCH
SELECT Error_Message()
END CATCH
END
UPDATE
You do not want to create a table every day. Seriously. This is very poor database design.
Instead you want to add a datetime column to your table that indicates when each record was created.
Can you please go through the following code ... Concurrent execution is handled using ISOLATION LEVEL SERIALIZABLE.
CREATE PROCEDURE [dbo].[GetNextID](
#IDName nvarchar(255)
)
AS
BEGIN
/*
Description: Increments and returns the LastID value from tblIDs
for a given IDName
Author: Max Vernon
Date: 2012-07-19
*/
DECLARE #Retry int;
DECLARE #EN int, #ES int, #ET int;
SET #Retry = 5;
DECLARE #NewID int;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SET NOCOUNT ON;
WHILE #Retry > 0
BEGIN
BEGIN TRY
BEGIN TRANSACTION;
SET #NewID = COALESCE((SELECT LastID FROM tblIDs WHERE IDName = #IDName),0)+1;
IF (SELECT COUNT(IDName) FROM tblIDs WHERE IDName = #IDName) = 0
INSERT INTO tblIDs (IDName, LastID) VALUES (#IDName, #NewID)
ELSE
UPDATE tblIDs SET LastID = #NewID WHERE IDName = #IDName;
COMMIT TRANSACTION;
SET #Retry = -2; /* no need to retry since the operation completed */
END TRY
BEGIN CATCH
IF (ERROR_NUMBER() = 1205) /* DEADLOCK */
SET #Retry = #Retry - 1;
ELSE
BEGIN
SET #Retry = -1;
SET #EN = ERROR_NUMBER();
SET #ES = ERROR_SEVERITY();
SET #ET = ERROR_STATE()
RAISERROR (#EN,#ES,#ET);
END
ROLLBACK TRANSACTION;
END CATCH
END
IF #Retry = 0 /* must have deadlock'd 5 times. */
BEGIN
SET #EN = 1205;
SET #ES = 13;
SET #ET = 1
RAISERROR (#EN,#ES,#ET);
END
ELSE
SELECT #NewID AS NewID;
END
GO

Receiving an error: The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION in SQL Script

Was trying to perform an update to a database for an application, and then SQL server threw me a Commit has no corresponding being transaction. It was kind enough to point me to the procedure but I cannot seem to find what the issue is.
This is my code:
ALTER PROCEDURE [dbo].[sp_tblUser_Update] #UserID INTEGER,
#UserName NVARCHAR(20),
#Password NVARCHAR(10),
#Yard NVARCHAR(50),
#NewRole NVARCHAR(50),
#FullName NVARCHAR(50),
#Email NVARCHAR(50),
#BCConEmails BIT,
#CrewStatusReadOnly BIT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
UPDATE tblUser
SET UserName = #UserName,
UserPassword = #Password,
Yard = #Yard,
Fullname = #FullName,
EmailAddress = #Email,
NewRole = #NewRole,
BCConEmails = #BCConEmails,
CrewStatusReadOnly = #CrewStatusReadOnly
WHERE ( UserID = #UserID )
END
GO
COMMIT
You don't need the COMMIT line as the END in your code is what terminates the previous BEGIN. If you want to add a transaction you should put a BEGIN TRANSACTION before the update statement and move the COMMIT before the END
begin tran
delete from Customerages
where Age=25
commit;
begin tran
delete from Customerages
where Age=25
rollback;