Связь между транзакциями во вложенных хранимых процедурах? - PullRequest
1 голос
/ 03 февраля 2012

Я помещаю транзакции во все мои "установленные" процедуры.Нет проблем.Все работает.

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

Так что счастливое омовение будет.

  • Я в ProcA и начинаю транзакцию.
  • Вызывает ProcB и начинает транзакцию.
  • ProcB успешен и фиксирует.
  • ProcA успешен и фиксирует.

Однако,что произойдет, если ProcB завершится неудачно, откатится и сбросит ошибку.Это также должно привести к корректному откату ProcA?

Что если ProcB завершится успешно, завершится, затем ProcA завершится неудачно и произойдет откат ... произойдет ли откат того, что произошло в ProcB?или это зафиксировано?

Мне нужно, чтобы эти два работали вместе, либо оба преуспели, либо потерпели неудачу, и оба откатились.Каков наилучший способ обеспечить это?

Я работаю с Microsoft SQL Server 2008 R2 (SP1)

Примечание. Если ProcB требует транзакцию, поскольку ее можно вызвать без переноса в ProcA,Технически, ProcA не всегда вызывает ProcB (зависит от ввода).

Ответы [ 3 ]

5 голосов
/ 03 февраля 2012

Вот простая демонстрация, показывающая, что происходит с вложенными переводами:

CREATE TABLE TranTest (Field1 INTEGER)

BEGIN TRANSACTION
SELECT @@TRANCOUNT -- 1 open transaction
INSERT TranTest VALUES (1)

    BEGIN TRANSACTION
    SELECT @@TRANCOUNT -- 2 open transactions
    INSERT TranTest VALUES (2)
    ROLLBACK TRANSACTION -- this rolls back ALL transaction

SELECT @@TRANCOUNT -- 0 open transactions (you may have expected 1?)
SELECT * FROM TranTest -- No rows

Вместо указанного выше ROLLBACK, если вы выполняли транзакцию COMMIT, этот факт не делает ничего, кроме декремента @@ TRANCOUNT. Таким образом, вам нужно будет либо COMMIT внешней транзакции (которая будет COMMIT обеих строк в таблице), либо выполнить ROLLBACK, что не приведет к фиксации строк в таблице.

Вот ссылка MSDN на вложенные транзакции: http://msdn.microsoft.com/en-us/library/ms189336.aspx

1 голос
/ 03 февраля 2012

Я параноидально отношусь к транзакциям (эта транзакция оставалась открытой в Производстве однажды, которую никто не заметил в течение получаса ...), поэтому я бы деформировал внутреннюю транзакцию потенциально следующим образом:

CREATE PROCEDURE etcetc

...

DECLARE @IsTransaction bit = 0

IF @@trancount > 0
 BEGIN
    BEGIN TRANSACTION
    SET @IsTransaction = 1
 END

...

IF @IsTransaction = 1
 BEGIN
    COMMIT
    --  or ROLLBACk, as necessary
 END

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

(И заметил ли кто-либо еще, как BOL не 'на самом деле сказать, что происходит, когда вы запускаете ROLLBACK для именованной транзакции, которая не является самой внешней транзакцией? Они прописывают каждую другую перестановку ...)

1 голос
/ 03 февраля 2012

Просто используйте XACT_ABORT ON, и все готово. Запустите следующий скрипт и убедитесь сами:

CREATE DATABASE ak_test;
GO
USE ak_test;
GO
CREATE TABLE dbo.a(i INT CONSTRAINT a_CannotInsertNegative CHECK(i>=0));
GO
CREATE TABLE dbo.b(i INT CONSTRAINT b_CannotInsertNegative CHECK(i>=0));
GO

CREATE PROCEDURE dbo.innerProc @i INT
AS
SET XACT_ABORT ON ;
BEGIN TRAN
INSERT b(i)VALUES(@i);
COMMIT;
GO

CREATE PROCEDURE dbo.outerProc @i1 INT, @i2 INT, @i3 INT
AS
SET XACT_ABORT ON ;
BEGIN TRAN
INSERT a(i)VALUES(@i1);
EXEC innerProc @i=@i2;
INSERT a(i)VALUES(@i3);
COMMIT;
GO

-- succeeds
EXEC dbo.outerProc 1, 2, 3;
SELECT * FROM dbo.a;
SELECT * FROM dbo.b;
GO
-- inner proc fails
EXEC dbo.outerProc 2, -3, 4;
GO
SELECT * FROM dbo.a;
SELECT * FROM dbo.b;
GO
-- second insert in outer proc fails
EXEC dbo.outerProc 3, 4, -5;
GO
SELECT * FROM dbo.a;
SELECT * FROM dbo.b;
...