Вот простое решение. Нет расы. Нет блокировки от ограничительного уровня изоляции транзакции. Однако, вероятно, не будет работать на диалектах SQL, кроме T-SQL.
Я предполагаю, что это какая-то внешняя сила в работе, чтобы ваша таблица каталожных номеров заполнялась неназначенными каталожными номерами.
Этот метод должен работать для вас: просто сделайте то же самое «блокированное обновление», которое извлекает значение, что-то вроде:
update top 1 CatalogNumber
set in_use = 1 ,
@newCatalogNumber = catalog_number
from CatalogNumber
where in_use = 0
В любом случае, следующая хранимая процедура просто набирает номер при каждом выполнении и возвращает предыдущий. Если вы хотите более интересное значение, добавьте вычисляемый столбец, который применяет преобразование выбора к инкрементному значению, чтобы получить желаемое значение.
drop table dbo.PrimaryKeyGenerator
go
create table dbo.PrimaryKeyGenerator
(
id varchar(100) not null ,
current_value int not null default(1) ,
constraint PrimaryKeyGenerator_PK primary key clustered ( id ) ,
)
go
drop procedure dbo.GetNewPrimaryKey
go
create procedure dbo.GetNewPrimaryKey
@name varchar(100)
as
set nocount on
set ansi_nulls on
set concat_null_yields_null on
set xact_abort on
declare
@uniqueValue int
--
-- put the supplied key in canonical form
--
set @name = ltrim(rtrim(lower(@name)))
--
-- if the name isn't already defined in the table, define it.
--
insert dbo.PrimaryKeyGenerator ( id )
select id = @name
where not exists ( select *
from dbo.PrimaryKeyGenerator pkg
where pkg.id = @name
)
--
-- now, an interlocked update to get the current value and increment the table
--
update PrimaryKeyGenerator
set @uniqueValue = current_value ,
current_value = current_value + 1
where id = @name
--
-- return the new unique value to the caller
--
return @uniqueValue
go
Чтобы использовать это:
объявить @pk int
exec @pk = dbo.GetNewPrimaryKey 'foobar'
выберите @ рк
Тривиально изменить его так, чтобы он возвращал набор результатов или возвращал значение через параметр OUTPUT.