Генерация TSQL ID - PullRequest
       3

Генерация TSQL ID

0 голосов
/ 10 мая 2010

У меня вопрос по поводу блокировки в TSQL. Предположим, у меня есть следующая таблица:

A(int id, varchar name)

где id - это первичный ключ, но НЕ столбец идентификаторов.

Я хочу использовать следующий псевдокод для вставки значения в эту таблицу:

lock (A)
  uniqueID = GenerateUniqueID()  
  insert into A values (uniqueID, somename)
unlock(A)

Как это можно сделать с точки зрения T-SQL? Вычисление следующего идентификатора должно быть выполнено с заблокированной таблицей A, чтобы другие сеансы не выполняли ту же самую операцию в то же время и получали тот же идентификатор.

Ответы [ 6 ]

1 голос
/ 10 мая 2010

Если у вас есть пользовательская логика, которую вы хотите применить при создании идентификаторов, оберните ее в пользовательскую функцию, а затем используйте пользовательскую функцию в качестве значения по умолчанию для столбца. Это должно уменьшить проблему параллелизма, аналогично предоставленным генераторам идентификаторов, откладывая генерацию до точки вставки и добавляя поддержку поведения блокировки вставки.

create table ids  (id int, somval varchar(20))
Go
Create function GenerateUniqueID()
returns int as 
Begin
    declare @ret int
    select @ret =  max(isnull(id,1)) * 2 from ids
    if @ret is null set @ret = 2  
    return @ret
End
go
alter table ids add Constraint DF_IDS Default(dbo.GenerateUniqueID())  for Id
0 голосов
/ 18 февраля 2014

Просто чтобы обновить старый пост. В SQL Server 2012 теперь возможно использовать функцию под названием Sequence. Последовательности создаются почти так же, как функция, и можно указать диапазон, направление (asc, desc) и точку прокрутки. После чего можно вызвать метод NEXT VALUE FOR, чтобы сгенерировать следующее значение в диапазоне.

См. Следующую документацию от Microsoft.

http://technet.microsoft.com/en-us/library/ff878091.aspx

0 голосов
/ 10 мая 2010

@ Маркус, вы должны использовать IDENTITY или NEWID (), как указано в других ответах. если вы абсолютно не можете, вот вариант для вас ...

DECLARE @NewID INT

BEGIN TRAN

SELECT  @NewID = MAX(ID) + 1
FROM    TableA (tablockx)

INSERT  TableA
        (ID, OtherFields)
VALUES  (@NewID, OtherFields)

COMMIT TRAN
0 голосов
/ 10 мая 2010

Если вы используете SQL2005 +, вы можете использовать предложение OUTPUT, чтобы выполнять то, что вы запрашиваете, без какой-либо блокировки (таблица Test1 имитирует таблицу, в которую вы вставляете, а поскольку OUTPUT требует временную таблицу и не переменная для хранения результатов, #Result сделает это):

create table test1( test INT)

create table #result (LastValue INT)


insert into test1
output INSERTED.test into #result(test)
select GenerateUniqueID()

select LastValue from #result
0 голосов
/ 10 мая 2010

На самом деле есть только три способа сделать это.

  1. Измените столбец ID на столбец IDENTITY, в котором он автоматически увеличивается на некоторое значение при каждой вставке.

  2. Измените столбец ID на GUID с ограничением по умолчанию NEWID () или NEWSEQUENTIALID (). Затем вы можете вставить свое собственное значение или позволить таблице генерировать одно значение для вас при каждой вставке.

  3. На каждой вставке начинайте транзакцию. Затем получите следующий доступный идентификатор, используя что-то вроде select max (id) +1. Сделайте это в одном SQL-выражении, если это возможно, чтобы ограничить возможность столкновения.

В целом большинство людей предпочитают вариант 1. Он быстрый, простой в реализации, и большинство людей его понимают.

Я склоняюсь к варианту 2 с приложениями, над которыми я работаю, просто потому, что мы склонны масштабировать (и увеличивать) наши базы данных. Это означает, что у нас обычно есть приложения с несколькими мастерами. Имейте в виду, что использование GUID в качестве первичных ключей может означать, что ваши индексы регулярно удаляются.

Я бы держался подальше от варианта 3, если у вас просто нет выбора. В этом случае я бы посмотрел, как в любом случае структурирована модель данных, потому что обязательно что-то не так.

0 голосов
/ 10 мая 2010
  1. Вы используете функцию NEWID () и вам не нужен какой-либо механизм блокировки

  2. Вы указываете столбец как IDENTITY, и вам не нужен какой-либо механизм блокировки

  3. Если вы генерируете эти идентификаторы вручную, и есть вероятность, что параллельные вызовы могут генерировать те же идентификаторы, то что-то вроде этого:


SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

@NextID = GenerateUniqueID()

WHILE EXISTS (SELECT ID FROM A WHERE ID = @NextID)
BEGIN
  @NextID = GenerateUniqueID()
END

INSERT INTO A (ID, Text) VALUES (@NextID , 'content')

COMMIT TRANSACTION
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...