TSQL Try / Catch внутри транзакции или наоборот?

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

Это краткое изложение сценария.

BEGIN TRANSACTION SCHEDULEDELETE
    BEGIN TRY
        DELETE   -- delete commands full SQL cut out
        DELETE   -- delete commands full SQL cut out
        DELETE   -- delete commands full SQL cut out
        PRINT 'X rows deleted. Please commit or rollback.' --calculation cut out.
    END TRY
    BEGIN CATCH 
        SELECT
            ERROR_NUMBER() AS ErrorNumber,
            ERROR_SEVERITY() AS ErrorSeverity,
            ERROR_STATE() AS ErrorState,
            ERROR_PROCEDURE() AS ErrorProcedure,
            ERROR_LINE() AS ErrorLine,
            ERROR_MESSAGE() AS ErrorMessage

            ROLLBACK TRANSACTION SCHEDULEDELETE
            PRINT 'Error detected, all changes reversed.'
    END CATCH

--COMMIT TRANSACTION SCHEDULEDELETE --Run this if count correct.

--ROLLBACK TRANSACTION SCHEDULEDELETE --Run this if there is any doubt whatsoever.

Это мой первый раз, когда я пишу транзакцию, правильно/лучше всего иметь блок TRY / CATCH внутри транзакции или транзакция должна быть внутри блока TRY?

важным фактором в этом сценарии является то, что пользователь должен вручную зафиксируйте транзакцию.

3 ответов


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

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

я добавлена небольшая проверка для любой открытой транзакции с помощью @@TRANCOUNT функция перед фактическим откатом транзакции. В этом сценарии нет особого смысла. Это более полезно, когда вы делаете некоторые проверки проверки в TRY блок перед открытием транзакции, такой как проверка значений param и других вещей и повышение ошибки в TRY блок, если какая-либо из проверок не удастся. В этом случае элемент управления перейдет на CATCH блок, даже не открывая транзакцию. Там вы можете проверить любую открытую транзакцию и откат, если они есть. В вашем случае вам действительно не нужно проверять наличие открытой транзакции, так как вы не введете CATCH блок, если что-то пойдет не так в вашей сделке.

не задать после выполнения DELETE операция, должна ли она быть зафиксирована или откат; выполните все эти проверки перед открытием транзакции. Как только транзакция будет открыта, зафиксируйте ее сразу и в случае из любых ошибок выполните обработку ошибок (вы делаете хорошую работу, получая подробную информацию, используя почти все функции ошибок).

BEGIN TRY

  BEGIN TRANSACTION SCHEDULEDELETE
    DELETE   -- delete commands full SQL cut out
    DELETE   -- delete commands full SQL cut out
    DELETE   -- delete commands full SQL cut out
 COMMIT TRANSACTION SCHEDULEDELETE
    PRINT 'X rows deleted. Operation Successful Tara.' --calculation cut out.
END TRY

BEGIN CATCH 
  IF (@@TRANCOUNT > 0)
   BEGIN
      ROLLBACK TRANSACTION SCHEDULEDELETE
      PRINT 'Error detected, all changes reversed'
   END 
    SELECT
        ERROR_NUMBER() AS ErrorNumber,
        ERROR_SEVERITY() AS ErrorSeverity,
        ERROR_STATE() AS ErrorState,
        ERROR_PROCEDURE() AS ErrorProcedure,
        ERROR_LINE() AS ErrorLine,
        ERROR_MESSAGE() AS ErrorMessage
END CATCH

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

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

лучше сначала выпустить a выберите запрос, чтобы определить количество строк, предоставьте это конечному пользователю и после подтверждения выполните фактическое удаление.


в дополнение к хорошим советам М. Али и Дина выше, немного помогите тем, кто хочет использовать новую (er) парадигму try CATCH THROW в SQL SERVER:

(Я не мог легко найти полный синтаксис, поэтому добавлять его здесь)

суть : здесь

пример кода хранимой процедуры здесь (из моего gist):

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE PROC [dbo].[pr_ins_test]
@CompanyID INT
AS

SET NOCOUNT ON

BEGIN

    DECLARE @PreviousConfigID INT

    BEGIN TRY
        BEGIN TRANSACTION MYTRAN; -- Give the transaction a name
        SELECT 1/0  -- Generates divide by zero error causing control to jump into catch

        PRINT '>> COMMITING'
        COMMIT TRANSACTION MYTRAN;
    END TRY
    BEGIN CATCH
        IF @@TRANCOUNT > 0
        BEGIN 
            PRINT '>> ROLLING BACK'
            ROLLBACK TRANSACTION MYTRAN; -- The semi-colon is required (at least in SQL 2012)


        END
        THROW
    END CATCH
END