запись транзакции в t-sql и обработка ошибок - PullRequest
19 голосов
/ 24 января 2010

Как вы думаете, есть лучший способ написать транзакцию в T-SQL? Есть ли лучший подход, который улучшает обслуживание и производительность приложения, использующего эту транзакцию?

-- Description: Insert email Receiver under specified subject
-- =============================================
ALTER PROCEDURE [Contact].[Receiver_stpInsert]
    @First_Name nvarchar(30),
    @Last_Name nvarchar(30),
    @Email varchar(60),
    @Subject_Id int
AS
BEGIN   
    SET NOCOUNT ON;

    DECLARE @error_num int;


    BEGIN TRANSACTION 

    INSERT INTO Contact.Receiver(First_Name, Last_Name, Email) VALUES(@First_Name, @Last_Name, @Email); 

    SET @error_num = @@ERROR;
    IF (@error_num <> 0)
        BEGIN
            ROLLBACK;
            RETURN;
        END

    DECLARE @rec_record_id int;
    SET @rec_record_id = (SELECT Record_Id FROM Contact.Receiver WHERE Email = @Email);

    SET @error_num = @@ERROR;
    IF (@error_num <> 0)
        BEGIN
            ROLLBACK;
            RETURN;
        END

    INSERT INTO Contact.Receiver_Subject(Receiver_Id, Subject_Id) VALUES(@rec_record_id, @Subject_Id);

    SET @error_num = @@ERROR;
    IF (@error_num <> 0)
        BEGIN
            ROLLBACK;
            RETURN;
        END

    SET @error_num = @@ERROR;
    IF (@error_num <> 0)
        BEGIN
            ROLLBACK;
            RETURN;
        END
    ELSE
        BEGIN   
            Commit;

        END

END

Ответы [ 6 ]

36 голосов
/ 24 января 2010

Если вы используете SQL 2005 или более позднюю версию, вы можете использовать блок TRY ... CATCH , например:

BEGIN TRY
    BEGIN TRANSACTION;

    INSERT INTO Contact.Receiver(First_Name, Last_Name, Email) VALUES (@First_Name, @Last_Name, @Email); 
    ... other inserts etc 
    ...
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
        ROLLBACK TRANSACTION;
END CATCH;

Таким образом, вы не будете повторять одни и те же блоки проверки кода @@ ERROR. Если вы хотите узнать, какая ошибка произошла, в блоке BEGIN CATCH вы можете получить различную информацию:

  • ERROR_NUMBER () возвращает номер ошибки.
  • ERROR_SEVERITY () возвращает серьезность.
  • ERROR_STATE () возвращает номер состояния ошибки.
  • ERROR_PROCEDURE () возвращает имя хранимой процедуры или триггера где произошла ошибка.
  • ERROR_LINE () возвращает номер строки внутри подпрограммы, которая вызвала ошибка.
  • ERROR_MESSAGE () возвращает полный текст сообщения об ошибке. Текст включает значения, предоставленные для любого заменяемые параметры, такие как длины, имена объектов или время.
12 голосов
/ 24 января 2010

Долгое время я выступал за использование TRY / CATCH и вложенных транзакций в хранимых процедурах .

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

Если процедура вызывается в контексте транзакции, то процедура откатывает только свои собственные изменения и оставляет вызывающей стороне решать, следует ли откатить транзакцию встраивания или попробовать альтернативный путь ошибки.

create procedure [usp_my_procedure_name]
as
begin
    set nocount on;
    declare @trancount int;
    set @trancount = @@trancount;
    begin try
        if @trancount = 0
            begin transaction
        else
            save transaction usp_my_procedure_name;

        -- Do the actual work here

lbexit:
        if @trancount = 0   
            commit;
    end try
    begin catch
        declare @error int, @message varchar(4000), @xstate int;
        select @error = ERROR_NUMBER(), @message = ERROR_MESSAGE(), @xstate = XACT_STATE();
        if @xstate = -1
            rollback;
        if @xstate = 1 and @trancount = 0
            rollback
        if @xstate = 1 and @trancount > 0
            rollback transaction usp_my_procedure_name;

        raiserror ('usp_my_procedure_name: %d: %s', 16, 1, @error, @message) ;
        return;
    end catch   
end

Недостатками этого подхода являются:

  • Он не работает с распределенными транзакциями. Поскольку точки сохранения транзакций несовместимы с распределенными транзакциями, вы не можете использовать этот шаблон, когда требуются распределенные транзакции. ИМХО распределенные транзакции являются злом и никогда не должны использоваться в любом случае.
  • Это изменяет исходную ошибку. Эта проблема присуща блокам TRY / CATCH, и вы ничего не можете с этим поделать. Приложение, которое подготовлено для работы с исходными кодами ошибок SQL Server (например, 1202, 1205, 2627 и т. Д.), Должно быть изменено для работы с кодами ошибок в вышеуказанном диапазоне 50000, вызванном кодом Transact-SQL, который использует TRY / CATCH .

Также предостережение об использовании SET XACT_ABORT ON . Этот параметр заставит пакет прервать транзакцию при любой ошибке. Это приводит к тому, что любая обработка транзакции TRY / CATCH практически бесполезна, и я рекомендую избегать этого.

7 голосов
/ 24 января 2010

Если у вас SQL Server 2000 или более ранняя версия, тогда да - проверка значения @@ERROR - это практически все, что вы можете сделать.

В SQL Server 2005 корпорация Microsoft представила конструкцию TRY ... CATCH, которая делает ее намного проще:

BEGIN TRY
  ......
  -- your T-SQL code here
  ......
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

    -- do other steps, if you want
END CATCH
4 голосов
/ 24 января 2010

спросил не так давно. Мой ответ с шаблоном TRY / CATCH

2 голосов
/ 24 января 2010

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

2 голосов
/ 24 января 2010

Если вы используете sql 2005 или выше, вы должны рассмотреть TRY CATCH подход

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