После прочтения большого количества статей и ответов на эту тему я все еще задаюсь вопросом, как работает механизм базы данных SQL Server в следующем примере:
Давайте предположим, что у нас есть таблица с именем t3:
create table t3 (a int , b int);
create index test on t3 (a);
и следующий запрос:
INSERT INTO T3
SELECT -86,-86
WHERE NOT EXISTS (SELECT 1 FROM t3 where t3.a=-86);
Запрос вставляет строку в таблицу t3 после проверки того, что строка еще не существует, на основе столбца "a".
Во многих статьях и ответах указывается, что при использовании вышеуказанного запроса ни одна строка не будет вставлена дважды.
Для выполнения вышеуказанного запроса я предполагаю, что ядро базы данных работает следующим образом:
- Сначала выполняется подзапрос.
- Механизм базы данных устанавливает общую (ые) блокировку для диапазона.
- Данные читаются.
- Общая блокировка снята. По данным MSDN общий доступ
Блокировка снимается, как только данные
был прочитан.
- Если строка не существует, она вставляет новую строку в таблицу.
- Новая линия заблокирована эксклюзивным замком (х)
Теперь рассмотрим следующий сценарий:
- Вышеприведенный запрос выполняется процессором A (SPID 1).
- Тот же запрос выполняется
процессор B (SPID 2).
- [SPID 1] Механизм базы данных устанавливает общую (ые) блокировку (и)
- [SPID 1] Подзапрос читает
данные. Теперь строки возвращаются.
- [SPID 1] Общая блокировка
освобожден.
- [SPID 2] Механизм базы данных устанавливает
общий (ые) замок
- [SPID 2] Подзапрос читает
данные. Строки не возвращаются.
- [SPID 2] Общая блокировка
освобожден.
- Оба процесса продолжают вставку строки (и мы получаем повторяющуюся запись).
Я что-то упустил? Является ли вышеуказанный способ правильным способом избежать повторяющихся записей?
Безопасный способ избежать дублирования записей - использовать приведенный ниже код, но мне просто интересно, правильный ли вышеуказанный метод.
begin tran
if (SELECT 1 FROM t3 with (updlock) where t3.a=-86)
begin
INSERT INTO T3
SELECT -86,-86
end
commit