ОБНОВЛЕНИЕ СТРОКИ с ТОПОМ и ГДЕ - PullRequest
3 голосов
/ 10 октября 2019

Я планирую создать своего рода рабочую очередь для пользователей, использующих только таблицу SQL Server. Строка будет назначена только одному пользователю за раз. Я планировал пойти по обычному UPDATE SET .. WHERE .. маршруту, как было предложено в этом вопросе:

https://stackoverflow.com/a/9241466

Вот соответствующая часть, процитированная:

;WITH CTE AS 
( 
    SELECT TOP 100 * 
    FROM T1      
    ORDER BY F2 
) 
UPDATE CTE SET F1='foo'

Чтобы гарантировать, что только один пользователь действительно сможет редактировать строку, я включу дополнительное свойство UserId и проверим наличие NULL в предложении WHERE.

;WITH CTE AS 
( 
    SELECT TOP 5 * 
    FROM T1
    WHERE UserId IS NULL      
    ORDER BY F2 
) 
UPDATE CTE SET UserId = @userid

После выполнения оператора я SELECT затроную соответствующие строки из базы данных.

Однако я могу захотеть включить некоторые дополнительные свойства в мое предложение WHERE, а такженекоторые дополнительные заказы.

UPDATE заблокирует строку, если включен WHERE, но так ли это, если WHERE включен в CTE?

Мой вопрос, по-другому, будет ли UPDATE по-прежнему блокировать строки или есть вероятность, что два одновременно выполняющихся оператора перезапишут UserId из-за проблемы гонки?

1 Ответ

1 голос
/ 10 октября 2019

Я создал следующие образцы данных:

DROP TABLE IF EXISTS [dbo].[StackOverflow];

CREATE TABLE [dbo].[StackOverflow]
(
    [ID] INT IDENTITY(1, 1) PRIMARY KEY
   ,[UserID] VARCHAR(12) 
);

INSERT INTO [dbo].[StackOverflow]
VALUES (DEFAULT)
      ,(DEFAULT)
      ,(DEFAULT)
      ,(DEFAULT)
      ,(DEFAULT);

SELECT [ID]  
      ,[UserID]
FROM [dbo].[StackOverflow];

Затем в двух отдельных окнах запросов я запустил следующий код:

BEGIN TRANSACTION;

WITH CTE AS 
( 
    SELECT TOP 2 * 
    FROM [dbo].[StackOverflow]
    WHERE [UserID] IS NULL      
    ORDER BY [ID] 
) 
UPDATE CTE 
SET UserId = 1; -- in the second one change the `userid = 2`


--COMMIT TRANSACTION;

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

SELECT [request_session_id]
      ,[resource_type]
      ,[resource_description]
      ,[resource_associated_entity_id]
      ,[request_mode]
      ,[request_type]
      ,[request_status]
FROM [sys].[dm_tran_locks]
WHERE [resource_database_id] = DB_ID()
ORDER BY [request_session_id]
        ,[resource_type];

enter image description here

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

enter image description here

Теперь, ву нас есть таблица:

enter image description here

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

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