it-swarm.com.de

Zurückschneiden der abgeschnittenen Tabelle

Ich habe eine gespeicherte Prozedur, die Folgendes ausführt:

BEGIN TRANSACTION

    -- Code to delete updated records from production (dbo) table
    DELETE FROM [dbo].[factMyTable]
    WHERE exists (SELECT *
        FROM [RAW].[MyTable]
        WHERE [RAW].[MyTable].[refno] = [dbo].[factMyTable].[refno]
        AND [RAW].[MyTable].[modification_dttm] >= [dbo].[factMyTable].[modification_dttm]
        )

    -- Code to perform the append of incremental records 
    INSERT INTO [dbo].[factMyTable]
    SELECT
         [refno]
        ,[field1]
        ,[field2]
        ,[field3]
        ,[FieldN]
        ,[modification_dttm]
    FROM [RAW].[MyTable]

-- Truncate stage table and get ready for next load
TRUNCATE TABLE [RAW].[MyTable]

COMMIT TRANSACTION

Wie Sie oben sehen können, habe ich einen Befehl zum Abschneiden, der in einem BEGIN/COMMIT-Transaktionsblock enthalten ist. Beim Ausführen dieser gespeicherten Prozedur beim Befehl insert wurde jedoch ein Fehler angezeigt, bei dem ein als NOT NULL festgelegtes Feld einen NULL-Wert erhielt. Als Ergebnis:

  1. Das Einfügen von Datensätzen aus der RAW-Tabelle in die dbo-Tabelle wurde zurückgesetzt. ABER
  2. Das Abschneiden der RAW-Tabelle wurde nicht zurückgesetzt.

Die Idee ist, dass die Kürzung nicht erfolgen sollte, wenn beim Einfügen von Daten ein Fehler auftritt.

Laut dieser Artikel können wir den Befehl zum Abschneiden zurücksetzen, aber möglicherweise ist meine gespeicherte Prozedur nicht richtig skriptiert. Vielleicht gibt es eine direktere Möglichkeit, um sicherzustellen, dass das Abschneiden nur erfolgt, wenn das Einfügen keine Fehler zurückgibt? Wie würde ich vorgehen?

3
pmdci
  1. Das Einfügen von Datensätzen aus der RAW-Tabelle in die dbo-Tabelle wurde zurückgesetzt. ABER
  2. Das Abschneiden der RAW-Tabelle wurde nicht zurückgesetzt.

Nein, es gab überhaupt kein rollback, und hier ist der Repro.

Mit xact_abort off, das ist Ihre default Set-Option. Ich habe 2 Tabellen erstellt. Dann öffne ich die Transaktion und mache 2 inserts. Eine davon (die zweite) schlägt fehl. Ich habe select @@trancount und select aus beiden Tabellen, damit Sie besser sehen können, was passiert:

--set xact_abort on

if object_id('dbo.t1') is not null drop table dbo.t1;
if object_id('dbo.t2') is not null drop table dbo.t2;
go


create table dbo.t1 (col1 int);
insert into dbo.t1 values(1), (null);

create table dbo.t2 (col1 int not null);
go

begin transaction

    insert into dbo.t2
    values(-1);

    insert into dbo.t2
    select col1
    from dbo.t1;

    select @@trancount as [@@trancount before truncate];

    truncate table dbo.t1;

commit transaction;

select @@trancount as [@@trancount after commit];

select *
from dbo.t1;

select *
from dbo.t2;

(enter image description here

Wie Sie sehen, wurde kein rollback erstellt, nur Ihr commit. Sie fügen (-1) in dbo.t2 ein und diese Zeile ist permanent vorhanden. Dies liegt am Fehler

Meldung 515, Ebene 16, Status 2, Zeile 18 Der Wert NULL kann nicht in die Spalte 'col1', Tabelle 'dbo.t2' eingefügt werden. Spalte erlaubt keine Nullen.

ist Anweisung, die nur endet . Die zweite Anweisung schlägt fehl, daher wurde keine Zeile eingefügt, aber das Einfügen von (-1) wurde nicht zurückgesetzt, und wie Sie nach dem Fehler sehen, ist Ihr transaction immer noch geöffnet . Es ist Ihr commit, der das Einfügen von -1 und die Tabelle truncation festschreibt.


Nun der zweite Test: Kommentar set xact_abort on, dies macht statment terminating nur Fehler batch aborting, alle statements innerhalb der Transaktion sind rolled back und die Ausführung wird sein unterbrochen, sobald der Fehler auftritt.

Die Tabelle t1 wird also niemals abgeschnitten, und die Einfügung von (-1) in t2 wird rolled back sein.

Und jetzt, wie Ihr Code geschrieben werden sollte:

set xact_abort on;

if object_id('dbo.t1') is not null drop table dbo.t1;
if object_id('dbo.t2') is not null drop table dbo.t2;
go

create table dbo.t1 (col1 int);
insert into dbo.t1 values(1), (null);

create table dbo.t2 (col1 int not null);
go

begin try
begin transaction

    insert into dbo.t2
    values(-1);

    insert into dbo.t2
    select col1
    from dbo.t1;

    select @@trancount as [@@trancount before truncate];

    truncate table dbo.t1;

commit transaction;
end try

begin catch
    select @@trancount as [@@trancount in catch before rollback];
    if @@rowcount > 0 rollback;
    throw;
end catch;

Ihr Code sollte immer xact_abort aktivieren und den Block try..catch haben.

Sie sollten rollback von catch ad throw the error ausführen

4
sepupic

Die Referenz von David ist wunderbar, aber wenn Sie nach einem praktischen Beispiel dafür suchen, was Sie tun sollten, ist hier ein Block für Sie. Ich habe Ihr Beispiel nicht genau dupliziert, da Sie die Probleme ohne es sehen können.

Ich baue eine Beispieltabelle auf und füge dort einige Daten ein. Ich versuche absichtlich, eine NULL in die Tabelle dbo.Fact einzufügen, was nicht erlaubt ist.

Ich verpacke meine Anweisungen dann in einen TRY/CATCH-Block. Im Catch-Block habe ich einen Rollback-Befehl sowie einen Wurf hinzugefügt, damit der ursprüngliche Fehler an den Client zurückgegeben wird. Wenn Sie den Wurf weglassen, wird kein Fehler zurückgegeben.

Wenn Sie Ihr ursprüngliches Verhalten sehen möchten, müssen Sie TRY/CATCH entfernen und nur die BEGIN TRANSACTION und COMMIT darin haben. Da auch mit dem try/catch SQL die Transaktion richtig zurückrollt.

/** Build up our sample tables and some data.
    **/ 

DROP TABLE IF EXISTS dbo.RawTable;
DROP TABLE IF EXISTS dbo.Fact;

CREATE TABLE dbo.RawTable
    (
    RefNo CHAR(10) NOT NULL
    , field1 CHAR(10) NULL
    , field2 CHAR(10) NULL
    );

CREATE TABLE dbo.Fact
    (
    RefNo CHAR(10) NOT NULL
    , field1 CHAR(10) NOT NULL
    , field2 CHAR(10) NOT NULL
    );

INSERT INTO dbo.RawTable 
    (RefNo, field1, field2)
VALUES ('T1', NULL, 'T1F2')
    , ('T2', 'T2F1', 'T1F2');

SELECT RefNo, field1, field2 FROM dbo.RawTable; 

GO

BEGIN TRY

    BEGIN TRANSACTION;

    INSERT INTO dbo.Fact 
    (RefNo, field1, field2)
    SELECT RefNo, field1, field2
    FROM dbo.RawTable AS T;

    TRUNCATE TABLE dbo.RawTable;

    COMMIT;

END TRY
BEGIN CATCH

    --If we are here and there are open transactions, then rollback.
    IF @@TRANCOUNT >= 1
    BEGIN
        ROLLBACK;
    END

    --We want the error to bubble back to the client so they know something went wrong.
    ;THROW;

END CATCH

GO

--Show some data.
SELECT RefNo, field1, field2 FROM dbo.Fact; 
SELECT RefNo, field1, field2 FROM dbo.RawTable;
2
Jonathan Fite