Хобрук: Ваш путь к мастерству в программировании

Как добавить транзакцию для 2 триггеров

У меня есть 2 хранимые процедуры, которые будут обновлять 2 таблицы и активировать 2 триггера.

CREATE PROCEDURE UP_CheckOut
    @idBill INT, 
    @finalPrice INT
AS 
    UPDATE DBO.Bill 
    SET Status = N'Finished', 
        FinalPrice = @finalPrice 
    WHERE ID = @idBill 
      AND Status = N'Not Finished'
GO

CREATE PROCEDURE UP_ModifyBonusPoint_AfterCheckOut
     @used INT, 
     @get INT, 
     @phoneNum VARCHAR(100)
AS 
     UPDATE DBO.CustomerAccount 
     SET BonusPoint = BonusPoint - @used + @get, 
         LastBuyDate = GETDATE() 
     WHERE PhoneNum = @phoneNum
GO

CREATE TRIGGER UT_BillCheckOut
ON dbo.Bill 
FOR UPDATE
AS 
BEGIN
    DECLARE @check INT = 0

    SELECT @check = COUNT(*) 
    FROM Inserted 
    WHERE Status = N'Finished'

    IF @check > 0 
        EXEC UP_ModifyBonusPoint_AfterCheckOut -- I still stuck here because I don't know how to do
END
GO

CREATE TRIGGER UT_CustomerCheckOut
ON dbo.CustomerAccount
FOR UPDATE
AS 
BEGIN
    DECLARE @check INT = 0

    SELECT @check = COUNT(*) 
    FROM Inserted

    IF @check = 0 
        ROLLBACK UP_CheckOut -- I still stuck here because I don't know how to do
END
GO

Я хочу, чтобы поток был таким:

  • Проверить счет (изменить его статус) -> активировать его Вставлен
  • в случае успеха обновить таблицу клиентов (добавить точку для покупки) -> активировать ее вставку
  • если не получится, откатить всю работу.

Но я не знаю, чтобы добавить транзакцию для отката. Или есть другой способ решить это в SQL?


  • это только откат этого триггера (UT_CustomerCheckOut), но я хочу, чтобы он откатывал все, даже первый процесс (UP_CheckOut) 29.01.2021
  • Совет. При проверке существования одной или нескольких строк более эффективно использовать exists вместо получения точного count и затем сравнивает его с нулем. 29.01.2021
  • @HABO Если он используется внутри IF, то он обычно оптимизируется в EXISTS, здесь явно не из-за переменной, но вы правы, лучше не полагаться на нее 29.01.2021
  • @HưngPhạm Я рекомендую вам читать когда кто-то отвечает 02.02.2021

Ответы:


1

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

CREATE PROC UP_CheckOut
(
    @idBill INT
    , @finalPrice INT
)
AS
BEGIN
    SET NOCOUNT, XACT_ABORT ON;

    BEGIN TRAN;

    UPDATE DBO.Bill SET
        Status = N'Finished'
        , FinalPrice = @finalPrice
    WHERE ID = @idBill AND Status = N'Not Finished';

    -- If there is still a transaction open, commit it
    -- If there is not it means a trigger rolled it back
    IF @@TRANCOUNT > 0 BEGIN
        COMMIT;
    END;

    RETURN 0;
END;
GO

Примечание. Я был бы склонен сообщить пользователю об ошибке на этом этапе, если транзакция не существует.

29.01.2021
  • Я попробовал это успешно, спасибо 29.01.2021
  • Я не думаю, что это работает так, как вы думаете, потому что SQL Server выдаст ошибку в конце триггера, говоря, что транзакция завершилась в триггере. 29.01.2021
  • @Charlieface хммм, но если бы это было так, разве вы не ожидали бы, что вся транзакция будет отменена без необходимости что-либо делать? 30.01.2021
  • Действительно, именно поэтому большинство людей просто устанавливают XACT_ABORT_ON, а затем THROW в триггере, который решает всю проблему. 30.01.2021

  • 2

    Откат в триггере покажет другую ошибку: The transaction ended in the trigger, что не очень полезно.

    Вместо этого лучше всего написать в триггере оператор THROW, чтобы выдать ошибку.

    Затем во внешней процедуре мы можем написать SET XACT_ABORT ON;, что приведет к автоматическому откату неявной транзакции, созданной из триггера, в случае ошибки.

    30.01.2021
  • Или try/catch во внешней процедуре, если нужно выполнить какую-либо обработку в дополнение к откату. 31.01.2021
  • Новые материалы

    Основы принципов S.O.L.I.D, Javascript, Git и NoSQL
    каковы принципы S.O.L.I.D? Принципы SOLID призваны помочь разработчикам создавать надежные, удобные в сопровождении приложения. мы видим пять ключевых принципов. Принципы SOLID были разработаны..

    Как настроить Selenium в проекте Angular
    Угловой | Селен Как настроить Selenium в проекте Angular Держите свое приложение Angular и тесты Selenium в одной рабочей области и запускайте их с помощью Mocha. В этой статье мы..

    Аргументы прогрессивного улучшения почти всегда упускают суть
    В наши дни в кругах веб-разработчиков много болтают о Progressive Enhancement — PE, но на самом деле почти все аргументы с обеих сторон упускают самую фундаментальную причину, по которой PE..

    Введение в Джанго Фреймворк
    Схема «работать умно, а не усердно» В этой и последующих статьях я познакомлю вас с тем, что такое фреймворк Django и как создать свое первое приложение с помощью простых и понятных шагов, а..

    Настольный ПК как «одно кольцо, чтобы править всеми» домашних компьютеров
    Вид после 9 месяцев использования С настольных компьютеров все началось, но в какой-то момент они стали «серверами», и мы все перешли на ноутбуки. В прошлом году я столкнулся с идеей настольных..

    Расширенные методы безопасности для VueJS: реализация аутентификации без пароля
    Руководство, которое поможет вам создавать безопасные приложения в долгосрочной перспективе Безопасность приложений часто упускается из виду в процессе разработки, потому что основная..

    стройный-i18следующий
    Представляем стройную оболочку для i18next. Эта библиотека, основанная на i18next, заключает экземпляр i18next в хранилище svelte и отслеживает события i18next, такие как languageChanged,..