Обеспечение уникальных номеров из базы данных сервера SQL - PullRequest
1 голос
/ 09 октября 2009

У меня есть приложение, которое использует номера инцидентов (среди других типов номеров). Эти числа хранятся в таблице с именем Number_Setup, которая содержит текущее значение счетчика.

Когда приложение генерирует новый инцидент, оно отправляет таблицу number_setup и получает требуемую строку счетчика чисел (счетчики могут быть сброшены ежедневно, еженедельно и т. Д. И сохраняются как целые числа). Затем он увеличивает счетчик и обновляет строку новым значением.

Приложение является многопользовательским (приблизительно 100 пользователей одновременно, а также задания sql, которые запускают и получают 100 записей об инцидентах и ​​запрашивают номера инцидентов для каждого). В таблице инцидентов есть несколько повторяющихся номеров инцидентов, где они не должны повторяться.

Сохраненный процесс используется для получения следующего счетчика.

SELECT @Counter = counter, @ShareId=share_id, @Id=id
FROM Number_Setup
WHERE LinkTo_ID=@LinkToId
AND Counter_Type='I'

IF isnull(@ShareId,0) > 0
BEGIN 
    -- use parent counter
    SELECT @Counter = counter, @ID=id
    FROM Number_Setup
    WHERE Id=@ShareID
END

SELECT @NewCounter = @Counter + 1

UPDATE Number_Setup SET Counter = @NewCounter
WHERE id=@Id

Я сейчас окружил этот блок транзакцией, но я не совсем уверен, что он "на 100% решит проблему", так как я думаю, что все еще есть общие блокировки, поэтому счетчик может быть прочитан в любом случае.

Возможно, я смогу проверить, что счетчик не был обновлен, в операторе обновления

UPDATE Number_Setup SET Counter = @NewCounter
WHERE Counter = @Counter
IF @@ERROR = 0 AND @@ROWCOUNT > 0 
    COMMIT TRANSACTION
ELSE
    ROLLBACK TRANSACTION

Я уверен, что это обычная проблема с номерами счетов в финансовых приложениях и т. Д.
Я также не могу поместить логику в код и использовать блокировку на этом уровне. Я также заблокировал в HOLDLOCK, но я не уверен в его применении. Должен ли он быть помещен в два оператора SELECT?

Как я могу убедиться, что дубликаты не созданы?

Ответы [ 4 ]

4 голосов
/ 09 октября 2009

Хитрость заключается в обновлении счетчика и считывании за одну атомарную операцию:

UPDATE Number_Setup SET Counter = Counter+1
OUTPUT INSERTED.Counter 
WHERE id=@Id;

Это, однако, не присваивает новый счетчик @NewCounter, а вместо этого возвращает его как результирующий набор клиенту. Если вам нужно присвоить его, используйте переменную промежуточной таблицы для вывода нового счетчика INTO:

declare @NewCounter int;
declare @tabCounter table (NewCounter int);
UPDATE Number_Setup SET Counter = Counter+1
OUTPUT INSERTED.Counter INTO @tabCounter (NewCounter)
WHERE id=@Id
SELECT @NewCounter = NewCounter FROM @tabCounter;

Это решает проблему создания атомарного приращения счетчика. У вас все еще есть другие условия гонки в вашей процедуре, потому что LinkTo_Id и share_id могут все еще обновляться после первого выбора, так что вы можете увеличить счетчик неправильного элемента link-to, но это не может быть решено только из этого примера кода, так как это зависит также в коде, который действительно обновляет shared_id и / или LinkTo_Id.

Кстати, вы должны войти в привычку называть свои поля последовательным регистром. Если им присвоено последовательное имя, то необходимо использовать регистр точного соответствия в коде T-SQL. Теперь ваши сценарии работают нормально только потому, что у вас есть сервер сортировки без учета регистра, если вы развертываете на сервере сортировки с учетом регистра, а ваши сценарии не совпадают с точным регистром имен полей / таблиц, ошибки будут следовать в изобилии.

0 голосов
/ 09 октября 2009

Если у вас есть возможность изменить свою работу, получая множество записей, я бы изменил мышление так, чтобы ваш счетчик был столбцом идентификации. Затем, когда вы получите следующую запись, вы можете просто вставить и получить @@ идентификатор таблицы. Это гарантирует, что вы получите наибольшее число. Вы также должны выполнить команду dbccReseed для сброса счетчика, а не просто обновлять таблицу, когда хотите сбросить идентификатор. Единственная проблема заключается в том, что для получения группы идентификаторов вам нужно будет выполнить около 100 вставок в рамках задания sql. Это может быть слишком много, но использование столбца идентификаторов - это гарантированный способ получения уникальных номеров.

0 голосов
/ 09 октября 2009

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

вместо чтения и обновления из столбца 'Counter' в таблице Number_Setup, почему бы вам просто не использовать автоинкрементный первичный ключ для своего счетчика? У вас никогда не будет повторяющегося значения для первичного ключа.

0 голосов
/ 09 октября 2009

пытались ли вы использовать GUID вместо автоинкрементов в качестве уникального идентификатора?

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