Using a single column table for generation sequence in SQL Server 2008 is scalable solution? - sql

Currently we are relying on a formula for generating confirmation numbers, but on heavy loads we observed duplicate confirmation numbers are being generated. We are considering switching to a sequence table as a quick fix, but we're worried about how it will perform. How can we calculate the risk in using the following approach?
CREATE TABLE dbo.TRN_CNFRM_SEQUENCE_UC_LOC_1
(
id INT IDENTITY(seed,increment)
)
declare #confirm table (id int);
BEGIN TRAN
INSERT INTO dbo.TRN_CNFRM_SEQUENCE_UC_LOC_1
output inserted.id into #confirm
DEFAULT VALUES
ROLLBACK TRAN
select ID from #confirm

First, creating a memory table means a table will be created in tempdb requiring schema lock. Adding a row to that table requires an additional exclusive page lock. If you're only acquiring a single "confirmation number" at the time you could get around that by altering the code to:
BEGIN TRAN
INSERT INTO dbo.TRN_CNFRM_SEQUENCE_UC_LOC_1
DEFAULT VALUES
ROLLBACK TRAN
SELECT SCOPE_IDENTITY()
Inserting into the table will also require an exclusive page lock. Having a high degree of concurrent work loads can lead to latch contention on the single page this table will allocate.

Related

Make sure only one record inserted in table with thousands of concurrent users

Recently, I needed to write a stored procedure to insert only one record when the first user come and ignore for others. I think the IF NOT EXISTS INSERT will not work for me. Also, some people saying online that MERGE adds race condition. Any quick way to achieve this? This is my code for now.
IF NOT EXISTS (SELECT ......)
INSERT
You might add another table to use as the lock mechanism.
Let's say your table's name is a, and the name of the table which has the locked value is check_a :
create table a (name varchar(10))
create table check_a (name varchar(10))
Insert only one record to the lock table:
insert into check_a values ('lock')
go
Then create a stored procedure which checks if there is a value in the main table. If there is no record, we might lock the only value in the table check_a and insert our value into the table a.
create proc insert_if_first
as
begin
set nocount on
if not exists (select name from a)
begin
declare #name varchar(10)
begin tran
select #name = name from check_a with (updlock)
if not exists (select name from a)
begin
insert into a values ('some value')
end
commit
end
end
go
First selection from the table a to check there is no record is for using system resources as low as we can. If there is a record in the table a, we can skip opening transaction and skip locking the row.
Second check is to make sure that while we are waiting to obtain the lock, no one inserted a row to the table a.
This way, only the first user which can lock check_a will be able to insert a value to the table a.
I'm guessing that you mean you want users to make a stored procedure that makes sure only one user can run the procedure. Then you need to use isolation levels. There are different Isolation levels, so you need to decide which one you need.
READ UNCOMMITTED
READ COMMITTED
REPEATABLE READ
SERIALIZABLE
You can read what they do here:
https://msdn.microsoft.com/en-us/library/ms173763.aspx

Can someone give me a real time example with the below temp table and tablevariable example that I found in stackexchange

Difference between temp table and table variable as stated:
Operations on #table_variables are carried out as system transactions, independent of any outer user transaction, whereas the equivalent #temp table operations would be carried out as part of the user transaction itself. For this reason a ROLLBACKcommand will affect a #temp table but leave the #table_variable untouched.
DECLARE #T TABLE(X INT)
CREATE TABLE #T(X INT)
BEGIN TRAN
INSERT #T
OUTPUT INSERTED.X INTO #T
VALUES(1),(2),(3)
/*Both have 3 rows*/
SELECT * FROM #T
SELECT * FROM #T
ROLLBACK
/*Only table variable now has rows*/
SELECT * FROM #T
SELECT * FROM #T
DROP TABLE #T
Can anyone tell me when will this above mentioned application/scenario will be used in real time? Can anyone give a real time example. Thanks
P.S. - Referred from this link: https://dba.stackexchange.com/questions/16385/whats-the-difference-between-a-temp-table-and-table-variable-in-sql-server/16386#16386
In a real example, just consider you have a transaction and for somehow your transaction rollbacks but you still want to log and see why the transaction is failed and try to keep the log until you execute the transaction without any rollbacks.
In this example, you can capture all your logs information into a table variable.
What is going on is that the developer is demonstrating the use of a temporary table (which for most intents is the same as a regular table) and a variable that is a table. When a rollback occurs any changes made to the temporary table is undone (the table is in the same state as before transaction started) but the variable is changed - its not affected by the rollback.

Conditionally inserting records into a table in multithreaded environment based on a count

I am writing a T-SQL stored procedure that conditionally adds a record to a table only if the number of similar records is below a certain threshold, 10 in the example below. The problem is this will be run from a web application, so it will run on multiple threads, and I need to ensure that the table never has more than 10 similar records.
The basic gist of the procedure is:
BEGIN
DECLARE #c INT
SELECT #c = count(*)
FROM foo
WHERE bar = #a_param
IF #c < 10 THEN
INSERT INTO foo
(bar)
VALUES (#a_param)
END IF
END
I think I could solve any potential concurrency problems by replacing the select statement with:
SELECT #c = count(*) WITH (TABLOCKX, HOLDLOCK)
But I am curious if there any methods other than lock hints for managing concurrency problems in T-SQL
One option would be to use the sp_getapplock system stored procedure. You can place your critical section logic in a transaction and use the built in locking of sql server to ensure synchronized access.
Example:
CREATE PROC MyCriticalWork(#MyParam INT)
AS
DECLARE #LockRequestResult INT
SET #LockRequestResult=0
DECLARE #MyTimeoutMiliseconds INT
SET #MyTimeoutMiliseconds=5000--Wait only five seconds max then timeouit
BEGIN TRAN
EXEC #LockRequestResult=SP_GETAPPLOCK 'MyCriticalWork','Exclusive','Transaction',#MyTimeoutMiliseconds
IF(#LockRequestResult>=0)BEGIN
/*
DO YOUR CRITICAL READS AND WRITES HERE
*/
--Release the lock
COMMIT TRAN
END ELSE
ROLLBACK TRAN
Use SERIALIZABLE. By definition it provides you the illusion that your transaction is the only transaction running. Be aware that this might result in blocking and deadlocking. In fact this SQL code is a classic candidate for deadlocking: Two transactions might first read a set of rows, then both will try to modify that set of rows. Locking hints are the classic way of solving that problem. Retry also works.
As stated in the comment. Why are you trying to insert on multiple threads? You cannot write to a table faster on multiple threads.
But you don't need a declare
insert into [Table_1] (ID, fname, lname)
select 3, 'fname', 'lname'
from [Table_1]
where ID = 3
having COUNT(*) <= 10
If you need to take a lock then do so
The data is not 3NF
Should start any design with a proper data model
Why rule out table lock?
That could very well be the best approach
Really, what are the chances?
Even without a lock you would have to have two at a count of 9 submit at exactly the same time. Even then it would stop at 11. Is the 10 an absolute hard number?

Singleton pattern in a stored procedure

How can you implement the Singleton pattern in a SQL Server 2005/2008 stored procedure?
We want the stored procedure to return the next value from a table to a caller, and then update the value, so the next caller gets a different value ...
BUT there will be time when there are lots of callers!
AND we don't want blocking/time-out issues
PS. maybe singleton isn't the answer ... if not, how could you handle this?
By definition SINGLETON IS A LOCKING Pattern.
Talking about databases, there are so many DB professionals that get afraid when you mention the word "Lock", but locks per se are not a problem, they are a fundamental mechanism for the Relational Databases.
You must learn how locks works, what kind of locks exists, and treat them with respect.
Always work with short transactions, lock the minimum rows as you can, work with sets not individual rows.
Locks become a problem when they are massive, and when they last too much, and of course when you build a DEADLOCK.
So, the golden rule, when you must change data into a transaction, first put an exclusive Lock (UPDATE), never a Shared Lock (SELECT), it means sometimes you have to start doing a fake LOCK as in :
BEGIN TRAN
UPDATE table
set col1 = col1
Where Key = #Key
.......
COMMIT TRAN
Prior to SQL Server 2012, when I needed a serial I've done it in two ways:
Create an IDENTITY column, so after inserting you can get the value with the built in function SCOPE_IDENTITY() there is also ##IDENTITY, but if someone create a trigger that inserts into another table with an identity column starts the nightmare.
CREATE TABLE [table]
(
Id int IDENTITY(1,1) NOT NULL,
col2 ....
col3 ....
)
The second option is to add an serial column usually in the parent table or a table made for it plus a procedure (you can use client code) to get the serial:
--IF YOU CREATE A SERIAL HERE YOU'LL SPENT SOME SPACE,
--BUT IT WILL KEEP YOUR BLOCKINGS VERY LOW
CREATE TABLE Parent
(
Id,
ChildSerial int not null,
col2 ...
col3 ...
CONSTRAINT PK_Parent PRIMARY KEY (Id)
)
GO
--NAMED CONSTRAINT Auto names are random (avoid them)
ALTER TABLE Parent
ADD CONSTRAINT Parent_DF_ChildSerial DEFAULT(0) FOR ChildSerial;
GO
CREATE TABLE Child
(
Id int not null
col2..
colN..
--PLUS PRIMARY KEY... INDEXES, etc.
)
CREATE PROC GetChildId
(
#PatentId int
#ChildSerial int output --To use the proc from another proc
)
As
Begin
BEGIN TRAN
--YOU START WITH A LOCK, SO YOU'LL NEVER GET A DEADLOCK
--NOR A FAKE SERIAL (Two clients with the same number)
UPDATE Parent
SET ChildSerial = ChildSerial + 1
WHERE Id = #PatentId
If ##error != 0
Begin
SELECT #ChildSerial = -1
SELECT #ChildSerial
ROLLBACK TRAN
RETURN
End
SELECT #ChildSerial = ChildSerial
FROM Parent
WHERE Id = #PatentId
COMMIT TRAN
SELECT #ChildSerial --To Use the proc easily from a program
End
Go

How to insert data in two tables at the database at the same time?

I work using SQL Server. I have 2 tables, Animal and Cat. When I add a new cat to the database, I want to update both tables. I should add the cat to the Animal table first, so that I can add the animal_Id to the Cat table afterwards.
Is there a way to add the record at two tables at the same time? If there isn't, what is the best way to do it?
I just want an idea.
If you use a transaction, both inserts will be done, at least logically, "at the same time".
That means that no other query, done from outside of the transaction, can see the base "between the inserts". And if there is a failure between both inserts (and no effective commit), the final state will ignore first insert.
In order to get the id of a row just added in your session, use SCOPE_IDENTITY.
You can't use INSERT against two tables in one statement.
SET XACT_ABORT ON
BEGIN TRANSACTION
INSERT INTO [A](...) VALUES(...);
INSERT INTO [B](...) VALUES(...);
COMMIT TRANSACTION
SET XACT_ABORT OFF
The transaction is to make sure it is everything or nothing is committed. The XACT_ABORT ensures that if one fails with an error (therefore COMMIT TRANSACTION will not fire), the transaction will be forced to roll back.
I would suggest to use transaction here. For example (if you know the Id of new row beforehand):
DECLARE #CAT TABLE(id int, name varchar(50));
DECLARE #ANIMAL TABLE(id int);
DECLARE #anmalId INT = 1;
BEGIN TRAN
INSERT INTO #ANIMAL VALUES(#anmalId);
INSERT INTO #CAT VALUES(#anmalId, 'Kitty');
COMMIT TRAN
SELECT * FROM #CAT;
SELECT * FROM #ANIMAL;
You can use ##identity in case of auto increments.
Use triggers. That is the best way
how about using trigger while insertion to one table??