Допустим, у вас есть следующий код (пожалуйста, не обращайте внимания, что это ужасно):
BEGIN TRAN;
DECLARE @id int
SELECT @id = id + 1 FROM TableA;
UPDATE TableA SET id = @id; --TableA must have only one row, apparently!
COMMIT TRAN;
-- @id is returned to the client or used somewhere else
На мой взгляд, это НЕ управляет параллелизмом должным образом. Тот факт, что у вас есть транзакция, не означает, что кто-то другой не будет читать то же значение, что и вы, прежде чем вы попадете в оператор обновления.
Теперь оставьте код как есть (я понимаю, что его лучше обрабатывать как один оператор или даже лучше, используя столбец автоинкремента / идентификатора), что является верным способом заставить его обрабатывать параллелизм правильно и предотвратить гонки, которые позволяют двум клиентам получить одинаковые значение идентификатора?
Я уверен, что добавление WITH (UPDLOCK, HOLDLOCK)
в SELECT поможет. Уровень изоляции транзакции SERIALIZABLE , по-видимому, также работает, поскольку он запрещает кому-либо читать то, что вы делали, до тех пор, пока не закончится транс ( ОБНОВЛЕНИЕ : это неверно. См. Ответ Мартина). Это правда? Будут ли они оба работать одинаково хорошо? Один предпочтительнее другого?
Представьте, что вы делаете что-то более законное, чем обновление идентификатора - некоторые вычисления основаны на чтении, которое вам нужно обновить. Там может быть много таблиц, некоторые из которых вы будете писать, а другие нет. Какова лучшая практика здесь?
Написав этот вопрос, я думаю, что подсказки блокировки лучше, потому что тогда вы блокируете только те таблицы, которые вам нужны, но я был бы признателен за чей-либо вклад.
PS И нет, я не знаю лучшего ответа и действительно хочу получить лучшее понимание! :)
update
что может быть основано на устаревших данных? В последнем случае вы можете использоватьrowversion
столбец, чтобы проверить, не была ли изменена строка, подлежащая обновлению, с момента ее чтения.