Транзакции SQL Server, происходящие в одно и то же время, создают неверные результаты выбора - PullRequest
1 голос
/ 31 октября 2019

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

У меня есть таблица Элементы , которые содержат OnHandQty и OutQty.

Всякий раз, когда происходит проверка, я сохраняюзапись в моей таблице ItemHistory и обновите количественные значения в моей таблице Items , чтобы отразить это изменение.

Это прекрасно работает до тех пор, пока множество транзакций не будет отправлено всервер одновременно.

Мой запрос на оформление заказа выглядит следующим образом:

BEGIN TRY
BEGIN TRANSACTION 

    DECLARE @OnHand [int]
    DECLARE @Out [int]

    SELECT @OnHand = QtyOnHand, @Out = QtyOut
    FROM [Item] 

    IF (((-@qty) + @OnHand) >= 0)
        BEGIN
            INSERT INTO [dbo].[ItemHistory] (...)
            OUTPUT Inserted.ItemID
            VALUES(...)

            UPDATE [Item]
            SET 
            QtyOnHand = @OnHand - @qty,
            QtyOut = @Out + @qty
        END
    ELSE
        BEGIN
            ROLLBACK TRAN
        END
COMMIT
END TRY
BEGIN CATCH
   IF @@TRANCOUNT > 0
      ROLLBACK TRAN
END CATCH

Проблема возникает здесь:

SELECT @OnHand = QtyOnHand, @Out = QtyOut
FROM [Item] 

Сервер иногда начинает две транзакции в одно и то же время. Это приводит к тому, что переменные @ OnHand и @ Out будут одинаковыми в двух отдельных транзакциях. Это не хорошо, так как они должны представлять текущие итоги. Что технически они делают, так как сервер не успел обновить таблицу Item на данный момент.

Мне нужно этот выбор, чтобы получить абсолютные текущие величины, чтобы это работало.

Есть ли способ предотвратить совершение транзакций в одно и то же время? Или есть лучший способ справиться с этим процессом?

1 Ответ

2 голосов
/ 31 октября 2019

Два возможных способа решения проблем параллелизма в SQL Server:

1) Взять исключительную блокировку таблицы with (tablockx) для таблицы, например,

SELECT @OnHand = QtyOnHand, @Out = QtyOut FROM [Item] WITH (tablockx)

2) Использовать sp_getapplock для создания однопользовательского блока кода.

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

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