C'è un modo per testare se DELETE fallirà a causa di vincoli?


10

Mi piacerebbe essere in grado di prevedere se un DELETE si imbatterà in una violazione del vincolo, senza effettivamente eseguire l'eliminazione.

Quali sono le mie opzioni per farlo? Esiste un modo semplice per eseguire una "corsa a secco" di un CANC?


Stai cercando di impedire l'eccezione per questa istruzione da solo o stai cercando di facilitare la gestione degli errori in un batch più grande che contiene questa eliminazione?
Aaron Bertrand

3
Potresti verificare se esiste un FK ed eseguire un'istruzione SELECT per verificare i valori?
SQLRockstar,

Aaron: dobbiamo eseguire un batch di più DELETE in transazioni separate. Se uno fallisce, gli altri sono già impegnati. (Design errato sin dall'inizio, lo so, ma non è la mia applicazione, e non sta cambiando.) La migliore soluzione al momento sembra fare un controllo a secco per vedere se i DELETE falliranno.
Jay Sullivan,

Non sono ancora sicuro di aver capito. Stai cercando di far sì che il resto delle eliminazioni abbia esito positivo o stai cercando di verificare in anticipo che tutte le eliminazioni abbiano esito positivo o nessuna di esse dovrebbe?
Aaron Bertrand

Aaron: Mi dispiace non averlo chiarito, ma sì, sto cercando di assicurarmi che tutti abbiano successo, o nessuno di loro abbia successo.
Jay Sullivan,

Risposte:


24

Se il tuo obiettivo è quello di elaborare tutte le eliminazioni solo se tutte hanno esito positivo, perché non utilizzare TRY / CATCH:

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

Se l'obiettivo è consentire a tutte le eliminazioni riuscite di avere esito positivo anche se uno o più falliscono, è possibile utilizzare TRY / CATCH individuali, ad es.

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

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

6

Un'opzione è iniziare una transazione, eseguire la cancellazione e quindi eseguire sempre il rollback:

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

1
E se l'eliminazione ha esito positivo, eseguirla di nuovo? Cosa succede se l'eliminazione è molto costosa? E se la cancellazione fallisce, cosa succede? Hai fatto una cancellazione e due selezioni. Come decido di passare alla prossima eliminazione o no?
Aaron Bertrand

1
Se quelli fanno parte dei requisiti, devono essere gestiti. Questa risposta riguarda una "semplice corsa a secco".
GaTechThomas,

Beh, non lo erano quando hai inviato la tua prima risposta, ma ora sono stati chiariti.
Aaron Bertrand

4
@GaTechThomas Aaron contribuisce molto, quindi a volte è breve, ma sono certo che la sua intenzione non fosse quella di essere aggressivo. Ne ho discusso in The Heap e sarei grato per l'opportunità di farlo anche con te.
Jack dice di provare topanswers.xyz il

1
@JackDouglas Ho letto i commenti di The Heap a cui fai riferimento e capisci il punto. I commenti della comunità erano ragionevoli, tranne per la parte in cui sono stato chiamato un pagliaccio per sottolineare la sua aggressività. Non capisco come sono quello che è stato visto come aggressivo. Ho pubblicato una risposta legittima alla domanda così come era posta in quel momento. Non ha richiesto la qualità della produzione - a volte basta solo un semplice e rapido. Quindi sulla mia risposta ottengo domande appuntite. L'aspetto era che stava denigrando la mia risposta in modo che la sua venisse scelta. Dovremmo prendere questa discussione altrove?
GaTechThomas

0

Vorrei migliorare la soluzione fornita da Aaron Bertrand con un po 'di codice, nel caso in cui si desideri provare ad aggiungere qualsiasi elemento di una tabella, gestire le eccezioni per ignorare i guasti o anche interrompere il processo dopo gli errori.

Questo selezionerà i record dalla tabella e quindi tenterà di eliminarli senza eccezioni:

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

1
Perché? In che modo si tratta di un miglioramento rispetto alla risposta accettata?
ToolmakerSteve
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.