Этот вид процедуры гарантированно потерпит неудачу при параллельности. То, чего вы хотите достичь, - это использовать таблицу в качестве очереди, которая, как известно, подвержена ошибкам, и есть хорошо известные проверенные и опробованные способы использования Использование таблиц в качестве очередей . Прямо сейчас вы находитесь на пути, который никуда не ведет быстро. На ваш предыдущий вопрос вы должны были выбрать ответ Кейда.
Чтобы исключить элементы из таблицы (заблокировать их для обработки при наличии одновременных потоков, пытающихся сделать то же самое), необходимо выполнить несколько условий, все они описаны в статье, на которую я ссылался, и фактический запрос - только одно из них. , Самое важное условие - это наличие правильного кластеризованного индекса. Поле isLocked
должно быть крайним левым полем в кластеризованном ключе, за которым должны следовать критерии упорядочения.
CREATE CLUSTERED INDEX cdxCandidates ON Candidates(isLocked, ...);
Этот кластеризованный индекс необходим для эффективных операций удаления из очереди. Тогда вы должны использовать предложение OUTPUT, это единственный способ вытащить это правильно:
WITH cte AS (
SELECT TOP (@x) isLocked, ...
FROM Candidates WITH (READPAST)
WHERE isLocked = 0
ORDER BY ...)
UPDATE cte
SET isLocked = 1
OUTPUT INSERTED.*;
Обработчик запросов понимает, что вы выбираете для обновления, и будет получать строки в таблице таким образом, чтобы одновременный поток не мог получить одинаковых строк.
Между прочим, эта «схема» является именно схемой (условие кластеризованного индекса и обновление с помощью OUTPUT поверх READPAST SELECT) используется в QUEUE компонента Service Broker, которые были специально разработаны для операций с высоким числом одновременных операций постановки / удаления. Были рассмотрены и другие альтернативы. Это единственный вариант, который имеет достойные шансы получить какую-либо производительность при сохранении корректности