Zagnieżdżone procedury przechowywane zawierające TRY CATCH ROLLBACK pattern?
Jestem zainteresowany skutkami ubocznymi i potencjalnymi problemami następującego wzoru:
CREATE PROCEDURE [Name]
AS
BEGIN
BEGIN TRANSACTION
BEGIN TRY
[...Perform work, call nested procedures...]
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
RAISERROR [rethrow caught error using @ErrorNumber, @ErrorMessage, etc]
END CATCH
END
Według mojego najlepszego zrozumienia, ten wzór jest dobry, gdy jest używany z jedną procedurą - procedura albo zakończy wszystkie swoje oświadczenia bez błędu, albo wycofa wszystkie działania i zgłosi błąd.
Jednakże, gdy jedna procedura składowana wywołuje inną procedurę składowaną, aby wykonać jakąś podgrupę pracy (ze zrozumieniem, że mniejsza procedura jest czasami wywołane samodzielnie) widzę problem związany z wycofaniem-pojawia się komunikat informacyjny (poziom 16) stwierdzający The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.
. Zakładam, że jest tak dlatego, że wycofanie w podprocedurze zawsze wycofuje najbardziej zewnętrzną transakcję, a nie tylko transakcję rozpoczętą w podprocedurze.
Chcę, aby całość została wycofana i przerwana, jeśli wystąpi jakikolwiek błąd (a błąd zgłoszony Klientowi jako błąd SQL), po prostu nie jestem pewien wszystkich efektów ubocznych, które pochodzą z warstwy zewnętrzne próbują wycofać transakcję, która została już wycofana. Być może sprawdzenie @@TRANCOUNT
przed wykonaniem rollback przy każdej warstwie try CATCH?
W końcu jest client end (Linq2SQL), który ma własną warstwę transakcyjną:
try
{
var context = new MyDataContext();
using (var transaction = new TransactionScope())
{
// Some Linq stuff
context.SubmitChanges();
context.MyStoredProcedure();
transactionComplete();
}
}
catch
{
// An error occured!
}
W przypadku, gdy procedura składowana, "MySubProcedure", o nazwie wewnątrz MyStoredProcedure spowoduje błąd, Czy Mogę być pewien, że wszystko, co wcześniej zrobiono w MyStoredProcedure, zostanie wycofane, wszystkie operacje Linq wykonane przez SubmitChanges zostaną wycofane, a na koniec, że błąd zostanie zarejestrowany? Lub co muszę zmienić w moim wzorze, aby upewnić się, że cała operacja jest atomowa, jednocześnie pozwalając na indywidualne użycie części dziecka (tzn. procedury podrzędne powinny nadal mieć tę samą ochronę atomową)
5 answers
To jest nasz szablon (usunięto błąd logowania)
To jest przeznaczone do obsługi
- Nie ma czegoś takiego jak zagnieżdżona transakcja w SQL serverze[18]} artykuł Paula Randala "No such thing as a nested transaction in SQL Server"
- błąd 266
- Trigger Rollbacks
Objaśnienia:
-
Wszystkie TXN begin I commit/rollbacks muszą być sparowane tak, aby
@@TRANCOUNT
było takie samo przy wejściu i wyjściu -
Niedopasowanie
@@TRANCOUNT
powoduje błąd 266 ponieważBEGIN TRAN
przyrosty@@TRANCOUNT
COMMIT
dekrety@@TRANCOUNT
-
ROLLBACK
zwraca@@TRANCOUNT
do zera
-
Nie można zmniejszyć
@@TRANCOUNT
dla bieżącego zakresu
To jest to, co myślisz, że jest "wewnętrzna transakcja" -
SET XACT_ABORT ON
tłumi błąd 266 spowodowany niedopasowaniem@@TRANCOUNT
A także zajmuje się kwestiami takimi jak "SQL Server Transaction Timeout" na dba.se -
Pozwala to na TXNs po stronie klienta (jak LINQ) Jedna procedura składowana może być częścią transakcji rozproszonej lub XA, lub po prostu zainicjowana w kodzie klienta (np.. NET TransactionScope)
Użycie:
- każdy przechowywany proc musi być zgodny z tym samym szablonem
Podsumowanie
- więc nie twórz więcej TXNs niż potrzebujesz
Na kod
CREATE PROCEDURE [Name]
AS
SET XACT_ABORT, NOCOUNT ON
DECLARE @starttrancount int
BEGIN TRY
SELECT @starttrancount = @@TRANCOUNT
IF @starttrancount = 0
BEGIN TRANSACTION
[...Perform work, call nested procedures...]
IF @starttrancount = 0
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0 AND @starttrancount = 0
ROLLBACK TRANSACTION;
THROW;
--before SQL Server 2012 use
--RAISERROR [rethrow caught error using @ErrorNumber, @ErrorMessage, etc]
END CATCH
GO
Uwagi:
-
Kontrola wycofywania jest w rzeczywistości zbędna z powodu
{21]}SET XACT_ABORT ON
. Jednak to sprawia, że czuję się lepiej, wygląda dziwnie Bez i pozwala na sytuacje, w których nie chcesz go na -
Remus Rusanu mA podobną powłokę , która wykorzystuje punkty zapisu. Wolę atomowe wywołanie DB i nie używam częściowych aktualizacji, takich jak ich Artykuł
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-25 10:54:21
Nie jestem facetem Linq (i Erland też nie), ale napisał absolutną Biblię na temat obsługi błędów. Poza komplikacjami, które Linq może dodać do twojego problemu, na wszystkie inne pytania należy odpowiedzieć tutaj:
Http://www.sommarskog.se/error_handling/Part1.html
(Stary link: http://www.sommarskog.se/error_handling_2005.html )
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-18 18:06:58
Aby rozwiązać problem zwracania numeru błędu i numeru linii, o którym wspomniał @AlexKuznetsov, można podnieść błąd jako taki:
DECLARE @ErrorMessage NVARCHAR(4000)
DECLARE @ErrorSeverity INT
DECLARE @ErrorState INT
DECLARE @ErrorLine INT
DECLARE @ErrorNumber INT
SELECT @ErrorMessage = ERROR_MESSAGE(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE(),
@ErrorNumber = ERROR_NUMBER(),
@ErrorLine = ERROR_LINE()
RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorNumber, @ErrorLine)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-29 05:57:44
-- @ Amanda metoda powyżej nie zwraca poprawnego numeru błędu
DECLARE
@ErrorMessage nvarchar(4000),
@ErrorSeverity int,
@ErrorState int,
@ErrorLine int,
@ErrorNumber int
BEGIN TRY
SELECT 1/0; -- CATCH me
END TRY
BEGIN CATCH
DECLARE @err int = @@ERROR
PRINT @err -- 8134, divide by zero
PRINT ERROR_NUMBER() -- 8134
SELECT
@ErrorMessage = ERROR_MESSAGE(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE(),
@ErrorNumber = ERROR_NUMBER(),
@ErrorLine = ERROR_LINE()
-- error number = 50000 :(
RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorNumber, @ErrorLine)
END CATCH
-- error number = 8134
SELECT 1/0
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-03 19:08:22
W przypadku, gdy nie jest wymagana specjalna obsługa błędów w CATCH, z wyjątkiem rethrow i przechowywanego łańcucha wywołań procs, może być odpowiednie użycie takiego prostego szablonu:
create procedure someNestedSP
as
SET XACT_ABORT ON
begin transaction
-- do some work or call some other similar SP
commit transaction
W przypadku błędu cofnęłoby to również transakcję roota ze wszystkimi "zagnieżdżonymi", ale kod jest krótszy i prostszy niż rozwiązanie @gbn. Nadal XACT_ABORT
zajmuje się większością poruszanych tam kwestii.
Mogą być dodatkowe koszty na zagnieżdżanie transakcji, ale może nie być zbyt wysokie, I Zgadnij.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-23 09:46:41