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


95

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

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

Exec USPStoredProcName

Я получаю следующую ошибку:

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

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


Есть ли в вашей процедуре какие-либо блоки TRY / CATCH?
Ремус Русану

Да, у меня есть блок TRY / CATCH
Vignesh Kumar A

Ответы:


110

Если у вас есть блок 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

3
Спасибо за вашу помощь. Используя Raiserror, я обнаружил проблему. Она заключается в попытке вставить значение NULL в поле NOT NULL,
Виньеш Кумар А

Но проверка ограничения не прервет транзакцию. Вы явно откатываете в улове или используете xact_abort on?
Ремус Русану

Я откровенно откатываюсь назад
Винеш Кумар А

2
Я пробовал этот шаблон, но он все равно не работает - когда у меня есть внешняя транзакция, этот шаблон создает точку сохранения, а в случае критической ошибки (невыполнимая транзакция) откатывает внешнюю транзакцию - это все равно вызывает @@ trancount = 1 перед вводом процедура и @@ trancount = 0 при выходе из нее
воробей

3
Я думаю , что этот бит в улове не так: if @xstate = -1 rollback; Глядя на этом примере MSDN , мы должны не откатить полную транзакцию , если не была не внешняя транзакция (то есть, если мы не делали begin tran). Я думаю, что процедура должна rollbackвыполняться только в том случае, если мы начали транзакцию, что решит проблему @ sparrow.
Ник

62

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

return
commit

вместо того

commit
return   

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


4
@seguso - это было очень полезно. Спасибо, что поделились. Иногда что-то так просто попадает под пыль. Бывает с лучшими из них.
Лео Гурдиан

Это было проблемой для меня, но она была менее очевидной, потому что мы обертывали несколько вызовов sproc в одну большую транзакцию через наш уровень доступа к данным - поэтому, просто глядя на sproc, вы не могли сказать, что транзакция вообще была. Если у вас есть эта проблема, убедитесь, что за пределами самого sproc нет ничего, что создает транзакцию. Если есть, возможно, вы вообще не сможете использовать операторы return в sproc.
EF0

Это был я, у меня была транзакция, и я возвращался перед транзакцией фиксации в операторе if / else
Кевин

19

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

В случае, если ошибка возникает в вашей хранимой процедуре, это может заблокировать таблицы базы данных, потому что транзакция не завершена из-за некоторых ошибок времени выполнения при отсутствии обработки исключений. Вы можете использовать обработку исключений, как показано ниже. УСТАНОВИТЬ XACT_ABORT

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

Источник


Если бы это было так, процитированный вопрос / ответ, вероятно, должен был бы означать, что этот вопрос должен быть помечен как повторяющийся и закрыт
Марк Шультайс,

10

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

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


5

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

Я обнаружил, что мне пришлось использовать 2 хранимые процедуры, одну «рабочую» и одну-оболочку с попыткой / уловом, обе с логикой, аналогичной описанной Ремусом Русану. Уловка рабочего используется для обработки «обычных» сбоев, а ловушка-оболочка для обработки ошибок сбоя компиляции.

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

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

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

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

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


1
Спасибо, Джастин. Хорошее наблюдение. В моем случае я делал агрегат внутри обновления, который не вызывает ошибок компиляции во время сохранения SP, но действительно имел недопустимый синтаксис - «Агрегат может не отображаться в установленном списке оператора UPDATE»
куклей

4

В моем случае ошибка была вызвана 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

2

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

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

2

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


Вот так просто. Кроме того, в моем случае требовался оператор ROLLBACK на случай, если SP не будет полностью выполнен. Просто для закрытия / завершения сделки.
J Cordero

1

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

COMMIT TRANSACTION [MyTransactionName]

1

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

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

Begin
  Begin
    -- your query here
  End
commit

вместо того

Begin Transaction
  Begin
    -- your query here
  End
commit

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


1

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

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


1

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


1

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

RETURN

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

BEGIN TRY
    ... 
END TRY

BEGIN CATCH
    ...
END CATCH

а также

BEGIN, COMMIT & ROLLBACK

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


0

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

SELECT 151
RETURN -151

Затем используйте:

SELECT 151
ROLLBACK
RETURN -151
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.