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
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!
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.
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.
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.
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.
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?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With