Concurrent table creation - sql

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

Related

Transactions not being committed although I have "Commit Transaction" statement

I'm using SQL Azure and trying to do conditional delete in batches for a large table, sample:
DECLARE
#LargestKeyProcessed BIGINT =1,
#NextBatchMax BIGINT,
#msg varchar(max) ='';
WHILE (#LargestKeyProcessed <= 1000000)
BEGIN
Begin Transaction
SET #NextBatchMax = #LargestKeyProcessed + 50000;
DELETE From mytable
WHERE Id > #LargestKeyProcessed AND Id <= #NextBatchMax And some logic
SET #LargestKeyProcessed = #NextBatchMax;
set #msg=''+#LargestKeyProcessed;
RAISERROR(#msg, 0, 1) WITH NOWAIT
Commit Transaction
END
After the command gets executed successfully I close the tab but SSMS says there are uncomitted transactions although the commit statement is in every iteration. Also the database size seems to remain the same.
I kindly seek your support in explaining why this happens
Thank you very much
I think you can try to add SET IMPLICIT_TRANSACTIONS OFF to the sql. As follows to see if solved your issue.
DECLARE
#LargestKeyProcessed BIGINT =1,
#NextBatchMax BIGINT,
#msg varchar(max) ='';
WHILE (#LargestKeyProcessed <= 1000000)
BEGIN
SET IMPLICIT_TRANSACTIONS OFF
Begin Transaction
SET #NextBatchMax = #LargestKeyProcessed + 50000;
DELETE From mytable
WHERE Id > #LargestKeyProcessed AND Id <= #NextBatchMax And some logic
SET #LargestKeyProcessed = #NextBatchMax;
set #msg=''+#LargestKeyProcessed;
RAISERROR(#msg, 0, 1) WITH NOWAIT
Commit Transaction
END

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

T-SQL - Incorporating Try-Catch and Error-Handling (Transactions)

I am working on a Transaction using Repeatable Read Isolation Level.
I want to incorporate both Try-Catch and an Error-handler features in this Transaction.
When I run the code, I get an error message that :
Msg 102, Level 15, State 1, Line 18
Incorrect syntax near 'BEGIN'.
Msg 102, Level 15, State 1, Line 23
Incorrect syntax near '#errnum'.
How do I complete this Transaction successfully? OR
What is the correct way to write this Transaction?
This is my work as at now:
CREATE PROCEDURE ItemFlow (#Name VARCHAR(50),
#aPrice MONEY,
#bPrice MONEY)
AS
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
GO
BEGIN TRAN
IF EXISTS (SELECT 1
FROM Cashier
WHERE Item = '#Item')
UPDATE Cashier
SET bPrice = '#bPrice',
aprice = '#aprice'
WHERE Item = '#Item'
ELSE
INSERT INTO Cashier
(Item, aPrice, bPrice)
VALUES ('#Item', '#aPrice', '#bPrice')
END
BEGIN TRY
EXECUTE ItemFlow
END TRY
BEGIN CATCH
#errnum = ERROR_NUMBER(),
#severity = ERROR_SEVERITY(),
#errstate = ERROR_STATE(),
#proc = ERROR_PROCEDURE(),
#line = ERROR_LINE(),
#message = ERROR_MESSAGE()
END CATCH
One problem is the GO that terminates the create procedure statement. I always use begin/end with stored procedures:
CREATE PROCEDURE ItemFlow (#Name VARCHAR(50),
#aPrice MONEY,
#bPrice MONEY)
AS
BEGIN
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRAN
IF EXISTS (SELECT 1
FROM Cashier
WHERE Item = '#Item')
UPDATE Cashier
SET bPrice = '#bPrice',
aprice = '#aprice'
WHERE Item = '#Item'
ELSE
INSERT INTO Cashier
(Item, aPrice, bPrice)
VALUES ('#Item', '#aPrice', '#bPrice')
COMMIT TRAN;
END; -- ItemFlow
Then, you need arguments when you call the stored procedure, or default values defined for them:
BEGIN TRY
EXECUTE ItemFlow #arg1, #arg2, #arg3
END TRY
BEGIN CATCH
#errnum = ERROR_NUMBER(),
#severity = ERROR_SEVERITY(),
#errstate = ERROR_STATE(),
#proc = ERROR_PROCEDURE(),
#line = ERROR_LINE(),
#message = ERROR_MESSAGE()
END CATCH;
Before implementing any of the suggested changes, please answer: 1) why you are calling the proc itself within the proc? and 2) why are you setting the ERROR_ functions into variables? Are you going to use them? If not, no need to declare variables.
Also:
Obviously the GO can't be a part of this. It is just a batch separator for SSMS.
You have an input param called #Name that is not used, and a variable called #Item that is not declared. I suspect they are the same thing so I have used #Item as the input param instead of #Name.
You use the input params as string literals in the three queries, which makes no sense. You need to remove the single-quotes from around them so that they can act as variables.
And, please do NOT separate the TRY / CATCH logic from the transaction!
Assuming that there was no real intention in calling an infinite loop (the proc calling itself with no condition to ever stop), your code should look as follows:
CREATE PROCEDURE ItemFlow
(
#Item VARCHAR(50),
#aPrice MONEY,
#bPrice MONEY
)
AS
SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRY
BEGIN TRAN
IF EXISTS (SELECT 1
FROM Cashier
WHERE Item = #Item)
BEGIN
UPDATE Cashier
SET bPrice = #bPrice,
aprice = #aPrice
WHERE Item = #Item;
END;
ELSE
BEGIN
INSERT INTO Cashier
(Item, aPrice, bPrice)
VALUES (#Item, #aPrice, #bPrice);
END;
COMMIT TRAN;
END TRY
BEGIN CATCH
IF (##TRANCOUNT > 0)
BEGIN
ROLLBACK TRAN;
END;
THROW;
END CATCH;
THROW was introduced in SQL Server 2012. If you are using anything from 2005 - 2008 R2, replace the THROW with:
DECLARE #ErrMessage NVARCHAR(4000);
SET #ErrMessage = ERROR_MESSAGE();
RAISERROR(#ErrMessage, 16, 1);
RETURN;
Your code has a few problems:
Error near BEGIN
GO is a batch separator. It's not valid T-SQL and only understood by SSMS. You are effectively submitting two queries:
CREATE PROCEDURE ItemFlow (#Name VARCHAR(50),
#aPrice MONEY,
#bPrice MONEY)
AS
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
and:
BEGIN TRAN
...
As you can see, the stored procedure body is empty. Remove GO to get rid of this.
Error near #errnum
Your variables are not declared. Also, you must use SELECT or SET to assign a value to these variables:
DECLARE #errnum int,
#serverity int,
(etc.)
SELECT #errnum = ERROR_NUMBER(),
#severity = ERROR_SEVERITY(),
#errstate = ERROR_STATE(),
#proc = ERROR_PROCEDURE(),
#line = ERROR_LINE(),
#message = ERROR_MESSAGE()
One last thing:
You have a BEGIN TRAN but not a COMMIT TRAN. Your transaction is still open by the end of the sproc's execution.

Set output parameter when commit transaction fails

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