Число транзакций после EXECUTE указывает на несовпадающее количество операторов BEGIN и COMMIT. Предыдущий счет = 1, текущий счет = 0

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

Но когда я называю "Вторая хранимая процедура" как:

Exec USPStoredProcName

Это дает мне ошибку следующим образом:

Число транзакций после EXECUTE указывает на несовпадающее количество операторов BEGIN и COMMIT. Предыдущий счетчик = 1, текущий счетчик = 0.

Я прочитал ответы на другие подобные вопросы и не могу найти, где именно запутывается количество коммитов.

18 ответов

Решение

Если у вас есть блок TRY/CATCH, вероятной причиной является то, что вы перехватываете исключение прерывания транзакции и продолжаете. В блоке CATCH вы всегда должны проверять XACT_STATE() и обрабатывать соответствующие прерванные и неподтвержденные (обреченные) транзакции. Если вызывающий абонент начинает транзакцию, и вызываемый пользователь, например, заходит в тупик (который прервал транзакцию), как вызываемый абонент собирается сообщить вызывающему абоненту, что транзакция была прервана, и он не должен продолжать "обычный бизнес"? Единственно возможный способ - повторно вызвать исключение, заставив вызывающего обработать ситуацию. Если вы молча проглатываете прерванную транзакцию, и вызывающий абонент продолжает считать, что он все еще находится в исходной транзакции, это может гарантировать только беспредел (а ошибка, которую вы получаете, заключается в том, как механизм пытается защитить себя).

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

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) ;
    end catch
end
go

У меня тоже была эта пробема. Для меня причина была в том, что я делал

return
commit

вместо

commit
return   

в одной хранимой процедуре.

Обычно это происходит, когда транзакция запущена и либо не зафиксирована, либо не является откатом.

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

SET XACT_ABORT ON
SET NoCount ON
Begin Try 
     BEGIN TRANSACTION 
        //Insert ,update queries    
     COMMIT
End Try 
Begin Catch 
     ROLLBACK
End Catch

Источник

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

Я обнаружил, что мне нужно использовать две хранимые процедуры: "рабочую" и оберточную с try/catch, с логикой, аналогичной описанной Remus Rusanu. Работающий перехватчик используется для обработки "обычных" сбоев, а перехватчик - для обработки ошибок сбоя компиляции.

https://msdn.microsoft.com/en-us/library/ms175976.aspx

Ошибки, на которые не влияет TRY…CATCH Construct

Следующие типы ошибок не обрабатываются блоком CATCH, когда они происходят на том же уровне выполнения, что и конструкция TRY… CATCH:

  • Ошибки компиляции, такие как синтаксические ошибки, которые мешают запуску пакета.
  • Ошибки, возникающие при перекомпиляции на уровне операторов, такие как ошибки разрешения имен объектов, возникающие после компиляции из-за отложенного разрешения имен.

Надеюсь, это поможет кому-то сэкономить несколько часов на отладку...

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

При использовании в сочетании с TRY/CATCH это может привести к описанной вами ошибке. Подробнее здесь.

В моем случае ошибка была вызвана RETURN внутри BEGIN TRANSACTION. Так у меня было что-то вроде этого:

Begin Transaction
 If (@something = 'foo')
 Begin
     --- do some stuff
     Return
 End
commit

и это должно быть:

Begin Transaction
 If (@something = 'foo')
 Begin
     --- do some stuff
     Rollback Transaction ----- THIS WAS MISSING
     Return
 End
commit

Избегать использования

RETURN

заявление, когда вы используете

BEGIN TRY
    ... 
END TRY

BEGIN CATCH
    ...
END CATCH

а также

BEGIN, COMMIT & ROLLBACK

операторы в хранимых процедурах SQL

У меня было то же сообщение об ошибке, моя ошибка была в том, что у меня была точка с запятой в конце строки COMMIT TRANSACTION

Для меня после обширной отладки исправление было простым пропущенным броском; Заявление в подвохе после отката. Без этого уродливое сообщение об ошибке, что вы в конечном итоге.

begin catch
    if @@trancount > 0 rollback transaction;
    throw; --allows capture of useful info when an exception happens within the transaction
end catch

Это также может зависеть от того, как вы вызываете SP из своего кода C#. Если SP возвращает какое-либо значение типа таблицы, тогда вызывайте SP с помощью ExecuteStoreQuery, а если SP не возвращает никакого значения, вызывайте SP с помощью ExecuteStoreCommand.

Точной причиной этого сообщения является правило, которое подразумевает SQL Server: количество транзакций должно быть одинаковым в начале и в конце выполнения процедуры. Другими словами, процедура;

  • не следует фиксировать/откатывать транзакцию, которую она не запускала. В этом случае предыдущий счетчик, отображаемый в сообщении об исключении, будет больше нуля, а текущий счетчик равен нулю. Лучший способ предотвратить это — зафиксировать счетчик транзакций (@@TRANCOUNT) в самом начале выполнения и использовать операторы транзакций, только если он равен нулю. Приведенная ниже примерная процедура представляет собой простейшую «защитную» структуру от ошибок такого типа. Если эта процедура вызывается в рамках существующей транзакции, она не начнет новую транзакцию и не попытается зафиксировать или откатить "унаследованную" транзакцию. Вместо этого он просто повторно выдает ту же ошибку в контекст вызывающей стороны. Это также хорошая практика, чтобы сохранить настоящую исходную процедуру ошибки.
  • должен решить судьбу (зафиксировать или откатить) начатую транзакцию до завершения ее выполнения. В этом случае текущий счет будет больше, чем предыдущий счет.

Я настоятельно рекомендую внимательно прочитать Erland Sommarskog Error and Transaction Handling in SQL Server.


      create or alter proc sp_err266
as
begin
    set nocount on
    set xact_abort on

    declare @trancount int = @@trancount

    if @trancount = 0
        begin tran

    begin try

        raiserror('Raise an unexpected error...', 16, 1);
        
        if XACT_STATE() = 1 and @trancount = 0
            commit;

    end try
    begin catch
        if XACT_STATE() <> 0 and @trancount = 0
            rollback;
        else
            throw;
    end catch
end

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

В моем случае у меня случайно был запрос BEGIN TRAN в запросе

Я столкнулся с этой ошибкой один раз после пропуска этого оператора из моей транзакции.

COMMIT TRANSACTION [MyTransactionName]

На мой взгляд, принятый ответ в большинстве случаев является излишним.

Причиной ошибки часто является несоответствие BEGIN и COMMIT, как четко указано в ошибке. Это означает использование:

Begin
  Begin
    -- your query here
  End
commit

вместо

Begin Transaction
  Begin
    -- your query here
  End
commit

Пропуск транзакции после начала вызывает эту ошибку!

Для меня проблема заключалась в том, что я забыл добавить output ключевое слово после некоторых выходных параметров вызова SP в транзакции.

Если у вас есть структура кода что-то вроде:

SELECT 151
RETURN -151

Тогда используйте:

SELECT 151
ROLLBACK
RETURN -151

Во вложенных процедурах ROLLBACK следует использовать с осторожностью, подробное объяснение здесь /questions/42410005/sql-server-vlozhennyie-tranzaktsii-v-hranimoj-protsedure/63346907#63346907

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

      ------------------------------------------------------------
BEGIN TRANSACTION
                           -- BEGING TRANSACTION
call of stored procedure   -- ROLLBACK TRANASCTION
                           -- ROLLBACK TRANSACTION
ROLLBACK TRANSACTION
-----------------------------------------------------------

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

Другие вопросы по тегам