Verwenden Sie keine Transaktion für gespeicherte Prozeduren

18

Ich habe eine gespeicherte Prozedur, die einige Befehle ausführt. Ich möchte nicht, dass diese Befehle in die Transaktion der gespeicherten Prozedur eingeschlossen werden. Wenn der 4. Befehl fehlschlägt, soll der 1., 2. und 3. Befehl erhalten bleiben und nicht zurückgesetzt werden.

Ist es möglich, die gespeicherte Prozedur so zu schreiben, dass nicht alles als eine große Transaktion ausgeführt wird?

Matthew Steeples
quelle

Antworten:

16

Alle Transaktionen werden nicht in einer einzigen Transaktion ausgeführt. Schauen Sie sich dieses Beispiel an:

use TestDB;
go

if exists (select 1 from sys.tables where object_id = object_id('dbo.TestTranTable1'))
    drop table dbo.TestTranTable1;
create table dbo.TestTranTable1
(
    id int identity(1, 1) not null,
    some_int int not null
        default 1
);
go

insert into dbo.TestTranTable1
default values;
go 4

select *
from dbo.TestTranTable1;

if exists (select 1 from sys.sql_modules where object_id = object_id('dbo.ChangeValues'))
begin
    drop proc dbo.ChangeValues;
end
go
create proc dbo.ChangeValues
as
    update dbo.TestTranTable1
    set some_int = 11
    where id = 1;

    update dbo.TestTranTable1
    set some_int = 12
    where id = 2;

    update dbo.TestTranTable1
    set some_int = 13
    where id = 3;

    -- this will error out (arithmetic overflow)
    update dbo.TestTranTable1
    set some_int = 2147483648
    where id = 4;
go

exec dbo.ChangeValues;

select *
from dbo.TestTranTable1;

Hier ist die Ausgabe:

Bildbeschreibung hier eingeben

Wenn Sie eine Extended Events-Sitzung zur Überwachung des sql_transactionEreignisses erstellen , sehen Sie hier die Ausgabe der Ausführung dbo.ChangeValues:

Bildbeschreibung hier eingeben

Wie Sie in diesem Screenshot oben sehen können, gibt es für jede der vier Anweisungen separate Transaktionen. Die ersten 3 Commits und die letzten 3 Commits werden aufgrund des Fehlers zurückgesetzt.

Thomas Stringer
quelle
16

Ich denke, dass es hier einige Verwirrung über einen Stapel gegen eine Transaktion geben kann .

Eine Transaktion ist eine Anweisung oder eine Gruppe von Anweisungen, die als Einheit erfolgreich sind oder fehlschlagen. Alle DDL-Anweisungen befinden sich in Transaktionen selbst (dh wenn Sie 100 Zeilen aktualisieren, Zeile 98 jedoch einen Fehler ausgibt, wird keine der Zeilen aktualisiert). Sie können auch eine Reihe von Anweisungen in eine Transaktion einschließen, indem Sie BEGIN TRANSACTIONund dann entweder COMMIToder verwenden ROLLBACK.

Ein Batch ist eine Reihe von Anweisungen, die zusammen ausgeführt werden. Eine gespeicherte Prozedur ist ein Beispiel für einen Stapel. Wenn in einer gespeicherten Prozedur eine Anweisung fehlschlägt und eine Fehlerbehandlung (normalerweise TRY/CATCHBlöcke) vorliegt, werden die nachfolgenden Anweisungen nicht ausgeführt.

Ich vermute, dass Ihr Problem darin besteht, dass der Stapel abgebrochen wird, wenn ein Fehler auftritt, weil entweder der gespeicherte Prozess selbst oder ein äußerer Bereich (wie die Anwendung oder der gespeicherte Prozess, der diese Prozedur aufruft) eine Fehlerüberwachung enthält. In diesem Fall ist die Lösung schwieriger, da Sie die Behandlung von Fehlern in dem Bereich anpassen müssen, in dem sie abgefangen werden.

JNK
quelle
Ich habe keinen Artikel gefunden, in dem steht: "Eine Speicherprozedur ist ein Beispiel für einen Stapel". Ich glaube, dass gespeicherte Prozedur Batch sehr ähnlich ist, aber es ist kein Batch. Der Hauptunterschied besteht darin, dass SP im Gegensatz zu Batches garantiert im Voraus kompiliert und mehrmals ausgeführt werden kann. Die Ähnlichkeiten sind: - Beide führen jeden Befehl gleichzeitig aus. - Wenn ein Befehl fehlgeschlagen ist, werden alle vorherigen Befehle festgeschrieben (es sei denn, es wurde in einer Transaktion ausgeführt). - Wenn ein Befehl fehlgeschlagen ist, werden nicht alle nächsten Befehle ausgeführt.
Ashi
6

Alles in SQL Server ist in einer Transaktion enthalten.

Wenn Sie explizit angeben begin transactionund end transactiondann heißt es Explicit Transaction . Wenn Sie dies nicht tun, handelt es sich um eine implizite Transaktion .

Um zu wechseln, in welchem ​​Modus Sie sich befinden, verwenden Sie

set implicit_transactions on

oder

set implicit_transactions off

select @@OPTIONS & 2

Wenn oben 2 zurückgegeben wird, befinden Sie sich im impliziten Transaktionsmodus. Wenn es 0 zurückgibt, werden Sie automatisch festgeschrieben.

Eine Transaktion ist ALL oder nichts, um die Datenbank in einem konsistenten Zustand zu halten. Beachten Sie die ACID-Eigenschaften.

CREATE TABLE [dbo].[Products](
    [ProductID] [int] NOT NULL,
    [ProductName] [varchar](25) NULL,
    [DatabaseName] [sysname] NOT NULL,
 CONSTRAINT [pk_Product_ID_ServerName] PRIMARY KEY CLUSTERED 
(
    [ProductID] ASC,
    [DatabaseName] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO


-- insert some data 
INSERT INTO [dbo].[Products]([ProductID], [ProductName], [DatabaseName])
SELECT 1, N'repl1_product1', N'repl1' UNION ALL
SELECT 1, N'repl2_product1_02', N'repl2' UNION ALL
SELECT 1, N'repl3_product1_03', N'repl3' UNION ALL
SELECT 2, N'repl1_product1_01', N'repl1' UNION ALL
SELECT 2, N'repl2_product1', N'repl2' UNION ALL
SELECT 2, N'repl3_product1_03', N'repl3' UNION ALL
SELECT 3, N'repl1_product1_01', N'repl1' UNION ALL
SELECT 3, N'repl2_product1_02', N'repl2' UNION ALL
SELECT 3, N'repl3_product1', N'repl3' UNION ALL
SELECT 4, N'repl1_product1_01', N'repl1' UNION ALL
SELECT 4, N'repl2_product1_02', N'repl2' UNION ALL
SELECT 5, N'repl1_product1_01', N'repl1' UNION ALL
SELECT 5, N'repl2_product1_02', N'repl2'

- SP jetzt erstellen - Beachten Sie, dass die ersten 3 erfolgreich sind und die 4. fehlschlägt, wenn die Zeichenfolge abgeschnitten wird ...

IF OBJECT_ID ('usp_UpdateProducts', 'P') IS NOT NULL
    DROP PROCEDURE usp_UpdateProducts;
GO
create procedure usp_UpdateProducts
as 
begin try
update Products 
set ProductName = 'repl1_product1'
where DatabaseName = 'repl1'and ProductID = 1;
update Products
set ProductName = 'repl2_product1'
where DatabaseName = 'repl2' and ProductID = 2;
update Products
set ProductName = 'repl3_product1'
where DatabaseName = 'repl3' and ProductID = 3;
update Products
set ProductName = 'repl3_product1_03&&&&&&&&&&39399338492w9924389234923482' -- this will fail ...
where DatabaseName = 'repl3' and ProductID = 4;
SELECT 1/0;
end try
begin catch
SELECT 
        ERROR_NUMBER() AS ErrorNumber,
        ERROR_SEVERITY() AS ErrorSeverity,
        ERROR_STATE() as ErrorState,
        ERROR_PROCEDURE() as ErrorProcedure,
        ERROR_LINE() as ErrorLine,
        ERROR_MESSAGE() as ErrorMessage;
end catch
go

Siehe auch: Ist es eine schlechte Praxis, immer eine Transaktion zu erstellen?

Kin Shah
quelle
3

So funktionieren gespeicherte Prozeduren standardmäßig. Die gespeicherte Prozedur wird nicht automatisch in eine Transaktion eingeschlossen.

Wenn die gespeicherte Prozedur angehalten werden soll, wenn der erste Fehler auftritt, müssen Sie einige TRY / CATCH-Anmeldedaten eingeben, um sie beispielsweise bei einem Problem mit Befehl 2 zurückzugeben.

mrdenny
quelle
2

Sie benötigen für jeden Befehl einzelne Transaktionen. Sie können dies auch mit gespeicherten Transaktionen durchführen:

Siehe SAVE TRANSACTION (Transact-SQL)in der Produktdokumentation.

Ich möchte, dass einzelne Transaktionen das Standardverhalten für gespeicherte Prozeduren sind, da alle Anweisungen in implizite Transaktionen eingeschlossen sind. Niemand sollte sich jedoch auf implizite Transaktionen verlassen, um das Schicksal seines Codes zu kontrollieren. Es ist weitaus besser, die Art und Weise, wie Transaktionen im Produktionscode verarbeitet werden, explizit zu steuern.

Adam Haines
quelle
-2

Trennen Sie jedes der Teile mit einem BEGIN TRAN und prüfen Sie, ob die Transaktion erfolgreich war. Wenn es ein Commit war, führen Sie andernfalls ein Rollback durch, da sie alle auf derselben Ebene ausgeführt werden. Sie können jeden Abschnitt separat festschreiben, ohne alle zurücksetzen zu müssen, wenn einer fehlschlägt.

Weitere Informationen finden Sie unter: http://msdn.microsoft.com/en-us/library/ms188929.aspx

Toni Kostelac
quelle
1
Werden dadurch Untertransaktionen in meiner gespeicherten Prozedur erstellt? Im Idealfall möchte ich das nach Möglichkeit vermeiden
Matthew Steeples
1
Wenn der SP innerhalb einer Transaktion aufgerufen wird, sind die oben gespeicherten Transaktionen die Antwort. Wenn der sp nicht mit aufgerufen wird, ist @mrdenny korrekt. SQL Server unterstützt keine verschachtelten Transaktionen.
StrayCatDBA
@StrayCatDBA nur zur Verdeutlichung. Es gibt geschachtelte Transaktionen in SQL Server, aber sie sind böse. Mit SQL Server können Sie Transaktionen in anderen Transaktionen starten, die als geschachtelte Transaktionen bezeichnet werden. Siehe sqlskills.com/blogs/paul/… , msdn.microsoft.com/en-us/library/ms189336(v=sql.105).aspx und sqlblog.com/blogs/kalen_delaney/archive/2007/08/13 /…
Kin Shah
2
Um klar zu sein (und für die Faulen, die nicht auf die Links klicken möchten), starten Sie eigentlich keine weitere Transaktion. Aka der Titel auf Pauls Post: "Mythos: Verschachtelte Transaktionen sind real." Sie sind keine wirklichen Transaktionen. COMMIT in einer verschachtelten Transaktion bewirkt nichts anderes als das Dekrementieren von @@ TRANCOUNT. Es ist wahr, dass Sie keine Fehlermeldung erhalten, wenn Sie BEGIN TRAN / COMMIT verschachteln. Dies unterscheidet sich jedoch von echten verschachtelten Transaktionen.
StrayCatDBA