Кількість транзакцій після EXECUTE вказує на невідповідність кількості операторів BEGIN та COMMIT. Попередній рахунок = 1, поточний рахунок = 0


94

У мене є Insertзбережена процедура, яка подаватиме дані Table1та отримуватиме Column1значення, Table1а також викликатиме другу збережену процедуру, яка буде подавати таблицю2.

Але коли я називаю другу збережену процедуру як:

Exec USPStoredProcName

Я отримую таку помилку:

Кількість транзакцій після EXECUTE вказує на невідповідність кількості операторів BEGIN та COMMIT. Попередній рахунок = 1, поточний рахунок = 0.

Я прочитав відповіді в інших таких запитаннях і не можу знайти, де саме кількість рахунків стає заплутаною.


Чи є у вашій процедурі блоки TRY / CATCH?
Remus Rusanu

Так, у мене блок TRY / CATCH
Віньєш Кумар,

Відповіді:


109

Якщо у вас є блок TRY / CATCH, то, швидше за все, ви ловите виняток щодо переривання транзакції та продовжуєте. У блоці CATCH ви завжди повинні перевіряти XACT_STATE()та обробляти відповідні перервані та несумісні (приречені) транзакції. Якщо ваш абонент розпочинає транзакцію, а calee потрапляє, скажімо, у глухий кут (що призвело до припинення транзакції), як викликана особа повідомляє абоненту про те, що транзакція була перервана, і вона не повинна продовжувати роботу як зазвичай? Єдиним можливим способом є повторне виняток, що змушує абонента вирішити ситуацію. Якщо ви мовчки проковтнете перервану транзакцію, а абонент продовжує припускати, що вона все ще знаходиться в початковій транзакції, може забезпечити лише погром (і помилка, яку ви отримуєте, полягає в тому, як двигун намагається захистити себе).

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

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?
Remus Rusanu

Я явно
відкочуюсь

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

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

59

У мене теж була ця проблема. Для мене причиною було те, що я робив

return
commit

замість

commit
return   

за одну збережену процедуру.


4
@seguso - це було дуже корисно. Дякую, що поділились. Іноді щось так просто потрапляє під пил. Трапляється з найкращим із них.
Лео Гардіан,

Це була проблема для мене, але була менш очевидною, оскільки ми обгортали кілька викликів sproc в одну велику транзакцію через наш рівень доступу до даних - так що просто дивлячись на sproc, ви не могли сказати, що транзакція взагалі була. Якщо у вас є ця проблема, переконайтеся, що за межами самого sproc немає чогось, що створює транзакцію. Якщо є, тоді ви взагалі не зможете використовувати оператори return у рамках sproc.
EF0

Це я, я мав транзакцію і повертався до моєї транзакції фіксації у виписці if / else
Кевін

18

Зазвичай це трапляється, коли транзакція запущена, або вона не фіксується, або не відміняється.

Якщо у вашій збереженій процедурі виникає помилка, це може заблокувати таблиці бази даних, оскільки транзакція не завершена через деякі помилки виконання під час відсутності обробки винятків. Ви можете використовувати обробку винятків, як показано нижче. ВСТАНОВИТИ 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

Помилки, на які не впливає конструкція Спробуй… Улов

Наступні типи помилок не обробляються блоком CATCH, коли вони трапляються на тому ж рівні виконання , що і конструкція TRY… CATCH:

  • Компілюйте помилки, такі як синтаксичні помилки , які перешкоджають запуску пакета.
  • Помилки, які виникають під час перекомпіляції на рівні виписки, наприклад, помилки роздільної здатності імен об’єктів, що виникають після компіляції через відкладене роздільне ім’я.

Сподіваємось, це допоможе комусь іншому заощадити кілька годин налагодження ...


1
Дякуй Джастіне. Приємне спостереження. У моєму випадку я робив агрегат всередині оновлення, яке не видає помилок компіляції під час збереження SP, але насправді був недійсним синтаксисом - "Агрегат може не відображатися в списку наборів оператора UPDATE"
kuklei

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

Для мене після великої налагодження виправлення було простим відсутнім кидком; заяву в улові після відкоту. Без нього це потворне повідомлення про помилку - це те, з чим ви закінчуєтесь.

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

2

У мене було таке саме повідомлення про помилку, моя помилка полягала в тому, що я мав крапку з комою в кінці РЕАЛІЗАЦІЇ ОПЕРАЦІЇ


Як це просто. Крім того, у моєму випадку потрібен оператор "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

пропуск Transaction after Begin спричиняє цю помилку!


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.