Уровень безопасности транзакций, необходимый для безопасного увеличения идентификаторов - PullRequest
3 голосов
/ 28 апреля 2010

Я пишу небольшую часть программного обеспечения для вставки записей в базу данных, используемую коммерческим приложением. Уникальные первичные ключи (идентификаторы) в соответствующей таблице (таблицах) являются последовательными, но, по-видимому, они не установлены на «автоинкремент». Таким образом, я предполагаю, что мне придется найти самый большой идентификатор, увеличить его и использовать это значение для записи, которую я вставляю.

В псевдокоде для краткости:

id = select max(id) from some_table
id++
insert into some_table values(id, othervalues...)

Теперь, если другой поток запустил ту же транзакцию до того, как первый завершил вставку, вы получите два идентичных идентификатора и ошибку при попытке вставить последний. Вы можете проверить этот сбой и повторить попытку, но более простым решением может быть установка уровня изоляции транзакции. Для этого мне нужен SERIALIZABLE или более низкий уровень?

Кроме того, это, как правило, разумный способ решения проблемы? Есть ли другие способы сделать это?

Ответы [ 4 ]

2 голосов
/ 28 апреля 2010

Один из способов сделать это - объединить первые две строки в оператор вставки:

INSERT INTO Some_Table VALUES ((Выберите Max (id) + 1 из Some_Table), Othervalues ​​...)

или

INSERT INTO Some_Table SELECT st2.id, Другие значения ОТ (выберите max (id) +1 из Some_Table) st2

В противном случае, что вы действительно хотите сделать, это заблокировать это в транзакции и запретить кому-либо еще читать в этой первой строке и заканчиваться дублирующими идентификаторами ... Но блокируя чтение, вы откройте большую банку с червями.

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

1 голос
/ 28 апреля 2010

Надежда Это помогает

0 голосов
/ 28 апреля 2010

Мы решили аналогичную проблему для обычно последовательных (но иногда перезаписываемых и не уникальных) номеров документов с использованием отдельной таблицы. Идея SQL:

Declare @docno Int
Begin Tran
Select @docno=lastval+1 From docnotable With(Updlock) Where doctype='xyz'
Update docnotable Set lastval=@docno Where doctype='xyz'
... do whatever you need ...
Commit Tran
0 голосов
/ 28 апреля 2010

попробуйте это (используя синтаксис SQL Server):

INSERT INTO some_table 
        (id, othervalues...)
    SELECT
        ISNULL(max(id),0)+1, othervalues...
        from some_table WITH (UPDLOCK, HOLDLOCK)
        WHERE ...
...