Czy istnieje sposób na sprawdzenie, czy DELETE nie powiedzie się z powodu ograniczeń?

10

Chciałbym móc przewidzieć, czy operacja DELETE spowoduje naruszenie ograniczenia, bez faktycznego wykonania operacji usuwania.

Jakie są moje opcje? Czy istnieje prosty sposób na wykonanie „próbnego uruchomienia” USUŃ?

Jay Sullivan
źródło
Czy próbujesz zapobiec wyjątkowi dla samej instrukcji, czy próbujesz ułatwić obsługę błędów w większej partii, która zawiera to usunięcie?
Aaron Bertrand
3
Czy możesz sprawdzić, czy FK istnieje, i uruchomić instrukcję SELECT, aby sprawdzić wartości?
SQLRockstar
Aaron: Musimy uruchomić partię kilku USUŃ w osobnych transakcjach. Jeśli jedna zawiedzie, pozostałe są już popełnione. (Wiem, że zły projekt od samego początku, ale to nie jest moja aplikacja i się nie zmienia.) Najlepsze obejście w tej chwili brzmi jak sprawdzanie na sucho, aby sprawdzić, czy DELETE się nie powiedzie.
Jay Sullivan,
Nadal nie jestem pewien, czy rozumiem. Czy starasz się, aby reszta operacji zakończyła się powodzeniem, czy też próbujesz z góry sprawdzić, czy wszystkie operacje zakończą się powodzeniem, czy też żadna z nich nie powinna?
Aaron Bertrand
Aaron: Przepraszam, że nie wyjaśniłem, ale tak, staram się upewnić, że wszyscy odniosą sukces, albo żaden z nich nie odniesie sukcesu.
Jay Sullivan,

Odpowiedzi:

24

Jeśli Twoim celem jest przetworzenie wszystkich usunięć tylko wtedy, gdy wszystkie zakończą się powodzeniem, dlaczego nie użyć po prostu TRY / CATCH:

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

Jeśli celem jest umożliwienie pomyślnego usunięcia wszystkich sukcesów, nawet jeśli jedno lub więcej zakończy się niepowodzeniem, możesz użyć pojedynczej TRY / CATCH, np.

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
źródło
6

Jedną z opcji jest rozpoczęcie transakcji, uruchomienie operacji usuwania, a następnie zawsze wycofywanie:

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
źródło
1
A jeśli usunięcie się powiedzie, uruchom ponownie? Co jeśli usunięcie jest bardzo kosztowne? A jeśli usunięcie się nie powiedzie, co wtedy? Dokonałeś jednego usunięcia i dwóch wyborów. Jak zdecydować się na następne usunięcie, czy nie?
Aaron Bertrand
1
Jeśli są one częścią wymagań, należy z nimi postępować. Ta odpowiedź dotyczy „prostego” przebiegu próbnego ”.
GaTechThomas
Nie były, kiedy po raz pierwszy przesłałeś swoją odpowiedź, ale teraz zostały wyjaśnione.
Aaron Bertrand
4
@GaTechThomas Aaron wiele wnosi, więc czasem jest krótki, ale jestem pewien, że jego intencją nie było być agresywnym. Omówiłem to w The Heap i byłbym wdzięczny za możliwość zrobienia tego również z tobą.
Jack mówi, że spróbuj topanswers.xyz
1
@JackDouglas Przeczytałem komentarze Sterty, do których się odwołujesz i rozumiesz sens. Komentarze społeczności były rozsądne, z wyjątkiem części, w której zostałem nazwany klaunem za wskazanie jego agresji. Nie rozumiem, jak to ja byłem postrzegany jako agresywny. Zamieściłem uzasadnioną odpowiedź na postawione wówczas pytanie. Nie wymagało to jakości produkcji - czasem potrzebujesz tylko szybko i łatwo. Więc na moją odpowiedź dostaję ostre pytania. Wyglądało na to, że oczernia moją odpowiedź, aby jego została wybrana. Czy powinniśmy przenieść ten wątek w inne miejsce?
GaTechThomas,
0

Chciałbym ulepszyć rozwiązanie dostarczone przez Aarona Bertranda z pewnym kodem, na wypadek gdybyś chciał spróbować dodać dowolny element tabeli, zarządzanie wyjątkami do ignorowania kończy się niepowodzeniem lub zatrzymanie procesu po błędach.

Ten wybierze rekordy z tabeli, a następnie spróbuje je usunąć bez wyjątków:

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
źródło
1
Dlaczego? Jak to jest poprawa w stosunku do przyjętej odpowiedzi?
ToolmakerSteve,