Gibt es eine Möglichkeit zu testen, ob DELETE aufgrund von Einschränkungen fehlschlägt?

10

Ich möchte vorhersagen können, ob ein DELETE auf eine Einschränkungsverletzung stößt, ohne das Löschen tatsächlich durchzuführen.

Welche Möglichkeiten habe ich dazu? Gibt es eine einfache Möglichkeit, einen "Trockenlauf" eines DELETE durchzuführen?

Jay Sullivan
quelle
Versuchen Sie, die Ausnahme nur für diese Anweisung zu verhindern, oder versuchen Sie, die Fehlerbehandlung in einem größeren Stapel zu vereinfachen, der diese Löschung enthält?
Aaron Bertrand
3
Könnten Sie überprüfen, ob ein FK vorhanden ist, und eine SELECT-Anweisung ausführen, um die Werte zu überprüfen?
SQLRockstar
Aaron: Wir müssen einen Stapel von mehreren DELETEs in separaten Transaktionen ausführen. Wenn einer ausfällt, sind die anderen bereits festgeschrieben. (Schlechtes Design von Anfang an, ich weiß, aber es ist nicht meine Anwendung und es ändert sich nicht.) Die derzeit beste Problemumgehung klingt nach einer Trockenprüfung, um festzustellen, ob die DELETEs fehlschlagen.
Jay Sullivan
Ich bin mir immer noch nicht sicher, ob ich das verstehe. Versuchen Sie, den Rest der Löschvorgänge erfolgreich zu machen, oder versuchen Sie im Voraus zu überprüfen, ob alle Löschvorgänge erfolgreich sind, oder sollte keiner von ihnen erfolgreich sein?
Aaron Bertrand
Aaron: Tut mir leid, dass ich es nicht klar gemacht habe, aber ja, ich versuche sicherzustellen, dass sie alle erfolgreich sind oder keiner von ihnen erfolgreich ist.
Jay Sullivan

Antworten:

24

Wenn Sie alle Löschvorgänge nur dann verarbeiten möchten, wenn alle erfolgreich sind, verwenden Sie einfach TRY / CATCH:

BEGIN TRANSACTION;
BEGIN TRY
  DELETE #1;
  DELETE #2;
  DELETE #3;
  COMMIT TRANSACTION;
END TRY
BEGIN CATCH
  ROLLBACK TRANSACTION;
END CATCH

Wenn das Ziel darin besteht, dass alle erfolgreichen Löschvorgänge erfolgreich sind, auch wenn einer oder mehrere fehlschlagen, können Sie einzelne TRY / CATCH verwenden, z

BEGIN TRY
  DELETE #1;
END TRY
BEGIN CATCH
  PRINT 1;
END CATCH

BEGIN TRY
  DELETE #2;
END TRY
BEGIN CATCH
  PRINT 1;
END CATCH
Aaron Bertrand
quelle
6

Eine Möglichkeit besteht darin, eine Transaktion zu starten, das Löschen auszuführen und dann immer ein Rollback durchzuführen:

begin tran

delete Table1 where col1 = 1

-- Test whether it is there
select * from Table1 where col1 = 1

rollback tran

-- Confirm that it is still there
select * from Table1 where col1 = 1
GaTechThomas
quelle
1
Und wenn das Löschen erfolgreich ist, führen Sie es erneut aus? Was ist, wenn das Löschen sehr teuer ist? Und wenn das Löschen fehlschlägt, was dann? Sie haben eine Löschung und zwei Auswahlen durchgeführt. Wie entscheide ich mich, beim nächsten Löschen fortzufahren oder nicht?
Aaron Bertrand
1
Wenn diese Teil der Anforderungen sind, sollten sie behandelt werden. Diese Antwort bezieht sich auf einen "einfachen" Trockenlauf ".
GaTechThomas
Nun, sie waren es nicht, als Sie Ihre Antwort zum ersten Mal eingereicht haben, aber sie wurden jetzt geklärt.
Aaron Bertrand
4
@GaTechThomas Aaron trägt viel bei, deshalb ist er manchmal kurz, aber ich bin sicher, dass er nicht aggressiv sein wollte. Ich habe dies in The Heap besprochen und wäre dankbar, wenn Sie dies auch mit Ihnen tun könnten.
Jack sagt, versuchen Sie topanswers.xyz
1
@ JackDouglas Ich habe die Heap-Kommentare gelesen, auf die Sie verweisen und die Sie verstehen. Die Kommentare der Community waren vernünftig, mit Ausnahme des Teils, in dem ich als Clown bezeichnet wurde, um auf seine Aggression hinzuweisen. Ich verstehe nicht, wie ich derjenige bin, der als aggressiv angesehen wurde. Ich habe eine legitime Antwort auf die Frage gestellt, wie sie damals gestellt wurde. Es wurde nicht nach Produktionsqualität gefragt - manchmal braucht man nur schnell und einfach. Bei meiner Antwort bekomme ich gezielte Fragen. Der Anschein war, dass er meine Antwort verunglimpfte, damit seine ausgewählt wurde. Sollen wir diesen Thread woanders hinbringen?
GaTechThomas
0

Ich möchte die von Aaron Bertrand bereitgestellte Lösung mit etwas Code verbessern, falls Sie versuchen möchten, ein Element einer Tabelle hinzuzufügen, die Ausnahmen zu ignorieren, um Fehler zu ignorieren, oder den Prozess nach Fehlern zu stoppen.

Dieser wählt die Datensätze aus der Tabelle aus und versucht dann, sie ohne Ausnahmen zu löschen:

DECLARE @MaxErrors INT
SET @MaxErrors = 5;    // Setting 0 will stop process after the first error!

SELECT
    [Id]
    , ROW_NUMBER() OVER (ORDER BY Id ASC) AS [Index]
INTO #DeletingItems
FROM myTable

DECLARE @Current INT, @Max INT, @Id INT, @TotErrors INT
SELECT
    @Current = 1
    , @TotErrors = 0
    , @Max = MAX([Index])
FROM #DeletingTable

WHILE @Current <= @Max
BEGIN
    SELECT
        @Id = [Id]
    FROM #DeletingItems
    WHERE
        [Index] = @Index;

    BEGIN TRANSACTION;    
    BEGIN TRY    
        DELETE FROM myTable WHERE [Id] = @Id;

        COMMIT TRANSACTION;    
    END TRY    
    BEGIN CATCH    
        ROLLBACK TRANSACTION;

        SET @TotErrors = @TotErrors + 1;

        IF @TotErrors > @MaxErrors
            BREAK;
    END CATCH

    SET @Current = @Current + 1;
END
MAXE
quelle
1
Warum? Wie ist dies eine Verbesserung gegenüber der akzeptierten Antwort?
ToolmakerSteve