Обновление с вложенной выбранной атомарной операцией? - PullRequest
9 голосов
/ 25 января 2010

Мне нужно сначала выбрать (скажем) 10000 строк в базе данных и вернуть их. Может быть больше клиентов, которые делают эту операцию одновременно. Я пришел с этим запросом:

update v set v.batch_Id = :batchId 
    from tblRedir v 
    inner join (
        select top 10000 id 
            from tblRedir
            where batch_Id is null 
            order by Date asc
    ) v2 on v.id=v2.id

Это операция, которая состоит из обновления и вложенного выбора. Оба запроса работают с одной и той же таблицей (tblRedir). Идея состоит в том, что строки сначала помечаются уникальным идентификатором batchId, а затем возвращаются через

select * from tblRedir where batch_id = :batchId

(batchid является уникальным идентификатором (например, метка времени или guid) для каждого этого обновления)

Мой вопрос:

Я думал, что операция обновление с вложенным выбором является атомарной - это означает, что каждый клиент получает свой собственный набор данных, который уникален (ни один другой клиент не получил подмножество своих данных).

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

Эта операция атомарная или нет?


Я работаю с Sql server 2005. Запрос выполняется через NHibernate, как это

session.CreateSQLQuery('update....')

1 Ответ

5 голосов
/ 25 января 2010

SELECT устанавливает общие блокировки на считанные строки, которые затем можно снять в режиме изоляции READ COMMITED.

UPDATE устанавливает блокировку обновления, позднее повышенную до эксклюзивных блокировок. Они не отменяются до конца транзакции.

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

Это можно сделать, установив уровень изоляции транзакции REPEATABLE READ, который сохранит общие блокировки до конца транзакции и предотвратит блокировку UPDATE этих строк.

Кроме того, вы можете переписать ваш запрос следующим образом:

WITH    q AS
        (
        SELECT  TOP 10000 *
        FROM    mytable WITH (ROWLOCK, READPAST)
        WHERE   batch_id IS NULL
        ORDER BY
                date
        )
UPDATE  q
SET     batch_id = @myid

, который просто пропустит заблокированные строки.

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