1

If I have the following:

Begin transaction
 numberOfRecords = select count from table where foreignKey = "some value" 

 Insert into table (set SequenceNumber = numberOfRecords + 1)
End Transaction

and multiple users are executing the above code, would each insert have a unique increasing number?

In other words, does the begin transaction queue up other transactions even reads so that each insert will have the correct sequence number? or do I require Insert into..Select statement to achieve what I want?

Thanks.

3
  • I'm a bit confused what you're trying to achieve here. Are you trying to create an auto incrementing ID column? could you not just use an identity column? Commented Jul 15, 2012 at 8:20
  • Count is a peculiar name for a column. If you were planning on using the count of rows in the table then you don't expect any rows to ever be deleted. Commented Jul 15, 2012 at 11:43
  • sorry it was suppose to be pseudo code but got a bit too close to sql. Commented Jul 15, 2012 at 19:15

5 Answers 5

2

No, a transaction with the default SQL Server isolation level (READ COMMITTED) is not sufficient. Putting it into one INSERT...SELECT statement won't fix it either. You basically have two options to fix this:

Option 1: Set your isolation level to SERIALIZABLE: SET TRANSACTION ISOLATION LEVEL SERIALIZABLE. This will ensure that transactions from two different users occur as if they occurred in sequence. This, however, might create deadlocks if many such transactions occur in parallel.

Option 2: Exclusively lock the table at the beginning of the transaction (SELECT * FROM table WITH (TABLOCKX, HOLDLOCK) WHERE 1=0). Note that this might impact performance if table is used frequently.

Both approaches have been analyzed in detail in my answer to the following SO question:

Sign up to request clarification or add additional context in comments.

2 Comments

What about optimistic locking, is there a way I can throw any concurrent transactions out due to optimistic locking and then retry again?
@JD: Optimistic locking usually uses some kind of version number on row level. I'm not sure how it could be applied here. Can you elaborate?
1

No, transactions does not queue up commands, it is not like a lock.

Usually you want to use an identity column, but in some cases when you want to generate SequenceNumber without gaps you need to use the above code with a unique constraint on the SequenceNumber column and be prepared to retry if the commit transaction throws an exception.

4 Comments

Thanks for the reply. What if I was doing just inserts in the transaction (not selects), also would Insert ...Select as one statement do what I want?
Sorry the psuedo code above missed out numberOfRecords = select count from table where foreignKey = "somevalue", in this case would identity column work?
@JD: No. In terms of concurrency, SELECT + INSERT and INSERT...SELECT behave similarly. See this question for more details.
As @Heinzi writes, the behaviour is dependent of the transaction isolation level, but the answer is correct for the default level (READ COMMITED)
1

I once used a SQL DB as a logger for a massive data export, to get a sequential "identity" I created a "on insert" trigger that dealt with issuing the next number. Worked well for me, however it was only a single user DB so not sure if there's any issues with multiple users and what I did.

Now that I've re-read the question, this may not be what your looking for but I think you could also do a trigger for a select?

USE [ExportLog]
GO

/****** Object:  Trigger [dbo].[Migration_Update_Event_Date]    Script Date: 02/10/2011                17:06:11 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TRIGGER [dbo].[Migration_Update_LogState]
ON [dbo].[MigrationLog] FOR INSERT NOT FOR REPLICATION
AS
UPDATE [MIGRATION-DATA].dbo.MIGRATIONSTATE
SET LASTPROCESSID = ID
WHERE MACHINENAME IN (SELECT MACHINENAME FROM INSERTED)

GO

1 Comment

I think the basic issue is the same here. It is just two lines stuffed into the same statement. Not sure they are atomic.
1

if you want to insert the record in unique ,then first you create on sequence then record should be insert thorugh sequance .like

create sequnce seq_num ;

now use seq_num to insert the rcords .

insert into <table name>(col1) values(seq_num.nextval);

2 Comments

Isn't this Oracle only? Didn't think sql server supported sequences
@Charleh - SQL Server 2012 does.
1

You need to set the transaction isolation level to a level that provides the right isolation level. In your code, two processes could execute the first line, then the second line. Both will insert the same value.

Set the isolation level to serializable and perform the select statement WITH (UPDLOCK). This will reduce concurrency in your system but it will be safe.

Other strategies are possible, but they are more time-consuming to implement (and test!).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.