Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to get the next id number without "identity"

I have to insert some records in a table in a legacy database and, since it's used by other ancient systems, changing the table is not a solution.

The problem is that the target table has a int primary key but no identity specification. So I have to find the next available ID and use that:

select @id=ISNULL(max(recid)+1,1) from subscriber

However, I want to prevent other applications from inserting into the table when I'm doing this so that we don't have any problems. I tried this:

begin transaction
    declare @id as int
    select @id=ISNULL(max(recid)+1,1) from subscriber WITH (HOLDLOCK, TABLOCK)
    select @id
    WAITFOR DELAY '00:00:01'
    insert into subscriber (recid) values (@id)
commit transaction
select * from subscriber

in two different windows in SQL Management Studio and the one transaction is always killed as a deadlock victim.

I also tried SET TRANSACTION ISOLATION LEVEL SERIALIZABLE first with the same result...

Any good suggestions to how I can ensure that I get the next id and use that without risking that someone else (or me!) is getting hosed?

Sorry for not mentioning this earlier, but this is a SQL 2000 server so I can't use things like FOR UPDATE and OUTPUT

UPDATE: This is the solution that worked for me:

BEGIN TRANSACTION
    DECLARE @id int

    SELECT  @id=recid
    FROM    identities WITH (UPDLOCK, ROWLOCK)
    WHERE table_name = 'subscriber'

    waitfor delay '00:00:06'

    INSERT INTO subscriber (recid) values (@id)

    UPDATE identities SET recid=recid+1 
    WHERE table_name = 'subscriber'

COMMIT transaction

select * from subscriber

The WAITFOR is so that I can have multiple connections and start the query several times to provoke concurrency.

Thanks to Quassnoi for the answer and to all you other guys that contributed! Awesome!

like image 625
henriksen Avatar asked Apr 15 '09 11:04

henriksen


People also ask

How do I find the next ID in SQL table?

MySQL has the AUTO_INCREMENT keyword to perform auto-increment. The starting value for AUTO_INCREMENT is 1, which is the default. It will get increment by 1 for each new record. To get the next auto increment id in MySQL, we can use the function last_insert_id() from MySQL or auto_increment with SELECT.

How do I make consecutive numbers in SQL?

To number rows in a result set, you have to use an SQL window function called ROW_NUMBER() . This function assigns a sequential integer number to each result row.

How do you find sequence number in SQL?

The syntax to a view the properties of a sequence in SQL Server (Transact-SQL) is: SELECT * FROM sys. sequences WHERE name = 'sequence_name'; sequence_name.


2 Answers

Create another table:

t_identity (id INT NOT NULL PRIMARY KEY CHECK (id = 1), value INT NOT NULL)

with a single row, lock this row, and increment value by one each time you need an IDENTITY.

To lock, increment, and return the new value in a single statement, use:

UPDATE  t_identity
SET     value = value + 1
OUTPUT  INSERTED.value

If you don't want to update, just lock, then issue:

SELECT  value
FROM    t_identity WITH (UPDLOCK, ROWLOCK)

This will lock the table until the end of the transaction.

If you always first lock t_identity before messing with ancient_table, you will never get a deadlock.

like image 102
Quassnoi Avatar answered Oct 14 '22 13:10

Quassnoi


Add another table with an identity column and use this new table and column to select/generate your identity values for the old table.

Update: Depending on the frequency of INSERTS (and the number of existing rows e) you could seed your new IDENTITY values at e+x where x is sufficiently large. Thhis would avoid conflict with the legacy inserts. A sad solution, an imperfect one for sure, but something to think about?

like image 37
Ed Guiness Avatar answered Oct 14 '22 14:10

Ed Guiness