T-SQL не смог откатиться - PullRequest
       36

T-SQL не смог откатиться

1 голос
/ 10 августа 2011

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

begin transaction

......--My code here......
......
......--code to create Table1
......
ALTER TABLE [dbo].[Table1]  WITH CHECK ADD  CONSTRAINT [FK_constraint] FOREIGN KEY([field1], [field2])
REFERENCES [dbo].[Table2] ([field3], [field4])
GO
....
......--End of My code here......


rollback transaction
commit transaction

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

Но если оператор alter table не выполняется из-за того, что Table2 не существует, он вызывает дальнейшие ошибки.

Сообщение 1767, Уровень 16, Состояние 0, Строка 2 Внешний ключ 'FK_Constraint ссылается на недопустимую таблицу' dbo.Table2 '.

Сообщение 1750, Уровень 16, Состояние 0, Строка 2 Не удалось создать ограничение. Смотрите предыдущие ошибки.

Сообщение 1767, Уровень 16, Состояние 0, Строка 2 Внешний ключ 'FK_xxxxxx' ссылается на недопустимую таблицу 'Table1'.

Когда я запускаю «транзакцию отката», я получаю это сообщение об ошибке «Запрос ROLLBACK TRANSACTION не имеет соответствующей BEGIN TRANSACTION». что глупо, потому что у меня есть начало транзакции сверху!

Пожалуйста, скажите мне, что пошло не так. Любая помощь приветствуется. Использование SQL-Server 2008.

EDIT:

Я добавил

SELECT @@TRANCOUNT;

до и после "ALTER TABLE [dbo]. [Table1] С ПРОВЕРКОЙ И ДОБАВЛЕНИЕМ"

....
SELECT @@TRANCOUNT;
ALTER TABLE [dbo].[Table1]  WITH CHECK ADD  CONSTRAINT [FK_constraint] FOREIGN KEY([field1], [field2]) REFERENCES [dbo].[Table2] ([field3], [field4])
GO 
SELECT @@TRANCOUNT;
....

Результаты равны 1 и 0 соответственно. Таблица alter автоматически откатывает мою транзакцию при ошибке !? Я не могу этого понять.

Ответы [ 5 ]

1 голос
/ 11 августа 2011

Я думаю, что вы ничего не можете сделать с обработкой Sql Server с помощью обработки серьезности ошибок DDL, некоторые из них обрабатываются автоматически (например, принудительным откатом транзакции) самим Sql Server.

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

Пример:

--  drop table thetransformersmorethanmeetstheeye
--  select * from thetransformersmorethanmeetstheeye



--  first batch begins here         

    begin tran

    create table thetransformersmorethanmeetstheeye(i int); -- non-erring if not yet existing

    -- even there's an error here, @@ERROR will be 0 on next batch
    ALTER TABLE [dbo].[Table1]  WITH CHECK ADD  CONSTRAINT [FK_constraint] FOREIGN KEY([field1], [field2])
    REFERENCES [dbo].[Table2] ([field3], [field4]);             

go  -- first batch ends here



--  second batch begins here

    if @@TRANCOUNT > 0 begin        
        PRINT 'I have a control here if things needed be committed or rolled back';

        -- @@ERROR is always zero here, even there's an error before the GO batch. 
        -- @@ERROR cannot span two batches, it's always gets reset to zero on next batch
        PRINT @@ERROR; 


        -- But you can choose whether to COMMIT or ROLLBACK non-erring things here
        -- COMMIT TRAN;
        -- ROLLBACK TRAN;

    end
    else if @@TRANCOUNT = 0 begin
        PRINT 'Sql Server automatically rollback the transaction. Nothing can do about it';
    end
    else begin
        PRINT 'Anomaly occured, @@TRANCOUNT cannot be -1, report this to Microsoft!';
    end

--  second batch implicitly ends here   
1 голос
/ 10 августа 2011

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

Вы можете легко это подтвердить, проверив @TRANCOUNT в своем коде и вызывая откат только тогда, когда он не равен нулю

1 голос
/ 10 августа 2011

Это возможно только в том случае, если в этом SPID нет открытой транзакции.

Вот и все. И единственный путь, по которому нет открытых транзакций, это либо:

  • Вы никогда не начинали новую транзакцию после того, как старая зафиксирована или откатана
  • У вас есть еще один коммит или откат, который вы не заметили
  • Что-то разорвало ваше соединение или вызвало откат из-за пределов вашего spid (как команда kill из другого сеанса)

Вы не предоставляете много кода. Есть ли в вашем запросе перехват ошибок или любая другая условная логика, которая не отображается?

0 голосов
/ 10 августа 2011

Ошибка в операторе ALTER TABLE - это ошибка компиляции, а не ошибка времени выполнения, поэтому весь пакет, в котором выполняется этот оператор, никогда не выполняется.Я предполагаю, что между BEGIN TRANSACTION и ALTER TABLE нет GO - следовательно, BEGIN TRANSACTION никогда не выполнялся, и то, что SQL Server говорит вам, совершенно верно.

Попробуйте добавить GOсразу после BEGIN TRANSACTION.

0 голосов
/ 10 августа 2011

Учитывая это:

create table z
(
i int identity(1,1) not null,
zzz int not null
);

При попытке выполнить следующее ..

begin try

    begin transaction

    alter table z drop column aaa;

    commit tran;

end try
begin catch 

    print 'hello';
    SELECT
        ERROR_NUMBER() as ErrorNumber,
        ERROR_MESSAGE() as ErrorMessage;

    IF (XACT_STATE()) = -1
    BEGIN
        PRINT
            N'The transaction is in an uncommittable state. ' +
            'Rolling back transaction.'
        ROLLBACK TRANSACTION;
    END;

end catch

print 'reached';

.. ошибка может быть поймана:

ErrorNumber ErrorMessage
4924    ALTER TABLE DROP COLUMN failed because column 'aaa' does not exist in table 'z'.

Но попробуйте изменить alter table z drop column aaa; на alter table z add zzz int;, Sql Server может перехватить ошибку ..

Имена столбцов в каждой таблице должны быть уникальными. Название столбца 'zzz' в таблице 'z' указано более одного раза.

.. но вам не удастся вернуть управление, блок CATCH не сработает. Кажется, нет жестких и быстрых правил, какие ошибки можно отследить, а какие нет.

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

This is fine, the control is yielded back to you, you can perform other things when you CATCH the error

Вот ошибка, которую ваш код не может отловить, что похоже на вашу проблему.

The error was caught by Sql Server but won't yield back the control to you. Consequently, you cannot perform other things, your CATCH section will be rendered useless

Обратите внимание, что там нет сетки (через SELECT ERROR_NUMBER() as ErrorNumber, ERROR_MESSAGE() as ErrorMessage;). Это означает, что Sql Server не вернул вам элемент управления после обнаружения исключения.

Возможно, здесь вы найдете другие подробности, которые могут помочь: http://msdn.microsoft.com/en-us/library/ms179296.aspx

См. Данное руководство по обработке ошибок @@ ERROR и / или TRY - CATCH

<Ч />

Кстати, в Postgresql все виды ошибок DDL распознаются вашим кодом.

do $$


begin

    -- alter table z drop column aaa;
    alter table z add zzz int;


exception when others then 

    raise notice 'The transaction is in an uncommittable state. '
                     'Transaction was rolled back';

    raise notice 'Yo this is good! --> % %', SQLERRM, SQLSTATE;
end;


$$ language 'plpgsql';

Вот сообщение об ошибке от разработчика для alter table z drop column aaa; в Postgresql:

enter image description here

Вот сообщение об ошибке от разработчика для alter table z add zzz int; в Postgresql; который, кстати, в Sql Server, когда он имеет ошибку в операторе такого типа, не вернет вам управление, поэтому ваши CATCH разделы иногда полезны, иногда бесполезны.

in Sql Server, control execution won't be yielded back to you when there's an exception on adding of column

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