Quando / perché utilizzare il collegamento in cascata in SQL Server?


149

Quando si impostano le chiavi esterne in SQL Server, in quali circostanze è necessario eseguirne la cascata durante l'eliminazione o l'aggiornamento e qual è il ragionamento alla base?

Questo probabilmente si applica anche ad altri database.

Cerco soprattutto esempi concreti di ogni scenario, preferibilmente da qualcuno che li abbia usati con successo.


4
Questa domanda non sembra strettamente correlata a SQL Server e sembra più una domanda teorica e generale. Sarebbe più utile per la comunità se si rimuove il sql-servertag.
Clapas,

4
@clapas Onestamente, se dovessi chiederlo oggi sarebbe fuori tema. Se non fosse per le visualizzazioni / i voti elevati che indicano che ha valore per la community, lo eliminerei.
Joel Coehoorn,

6
@JoelCoehoorn - Ovviamente questi tipi di domande hanno un valore. Questo valore non si dissipa a causa del passare del tempo. La domanda che ho in mente è: quanto valore stiamo perdendo vietando tali domande oggi?
P.Brian.Mackey,

2
@ P.Brian.Mackey Here, Here! Alcune delle migliori domande / risposte che ho visto sono quelle che sono state sottovalutate o dipinte come fuori tema ... ma si può dire dall'enorme quantità di voti positivi che molti hanno avuto la stessa identica domanda!
Anthony Griggs,

Le azioni a cascata richiedono blocchi serializzabili.
Mitch Wheat,

Risposte:


127

Riepilogo di ciò che ho visto finora:

  • Ad alcune persone non piace affatto la cascata.

Elimina a cascata

  • L'eliminazione a cascata può avere senso quando la semantica della relazione può comportare una descrizione esclusiva "fa parte di ". Ad esempio, un record OrderLine fa parte del suo ordine principale e OrderLines non verrà mai condiviso tra più ordini. Se l'Ordine dovesse svanire, anche OrderLine dovrebbe, e una linea senza un Ordine sarebbe un problema.
  • L'esempio canonico di Cascade Delete è SomeObject e SomeObjectItems, in cui non ha alcun senso l'esistenza di un record di elementi senza un record principale corrispondente.
  • Si dovrebbe non utilizzare Cascade Delete se si è preservare la storia o utilizzando un "soft / logica di eliminazione" in cui si imposta solo una colonna di bit cancellato a 1 / true.

Aggiornamento in cascata

  • L'aggiornamento a cascata può avere senso quando si utilizza una chiave reale anziché una chiave surrogata (colonna identità / incremento automatico) tra le tabelle.
  • L'esempio canonico per Cascade Update è quando si dispone di una chiave esterna mutabile, come un nome utente che può essere modificato.
  • Si dovrebbe non utilizzare Cascade aggiornamento con le chiavi che sono identità / colonne autoincremento.
  • L'aggiornamento in cascata viene utilizzato al meglio insieme a un vincolo univoco.

Quando utilizzare il collegamento in cascata

  • Potrebbe essere necessario ottenere una conferma ancora più forte da parte dell'utente prima di consentire a un'operazione di eseguire la cascata, ma dipende dall'applicazione.
  • Il collegamento a cascata può metterti nei guai se hai impostato le tue chiavi esterne in modo errato. Ma dovresti stare bene se lo fai bene.
  • Non è saggio usare il collegamento in cascata prima di averlo compreso a fondo. Tuttavia, è una funzione utile e quindi vale la pena dedicare del tempo a capire.

3
Si noti che gli aggiornamenti in cascata vengono spesso utilizzati anche dove le chiavi cosiddette "naturali" sembrano non essere queste chiavi univoche realmente efficaci. In effetti sono convinto che gli aggiornamenti in cascata siano necessari solo con modelli di database scarsamente normalizzati e sono un cancello aperto verso tabelle e codici disordinati.
Philippe Grondier,

1
Ti manca un punto importante, il collegamento in cascata può creare enormi problemi di prestazioni se ci sono molti record figlio.
HLGEM

13
@HLGEM - Non vedo la rilevanza. Se le operazioni a cascata causano un rallentamento, il processo manuale equivalente provocherebbe lo stesso rallentamento o non sarebbe protetto correttamente nel caso in cui fosse necessario eseguire il rollback della transazione.
Joel Coehoorn,

3
Perché dovrebbe importare se esiste un aggiornamento a cascata su una colonna IDENTITY o auto-increment? Posso capire perché non sarebbe necessario perché non dovresti aver bisogno di cambiare quei valori (arbitrari), ma se uno di loro fosse cambiato, almeno l'integrità referenziale sarebbe intatta.
Kenny Evitt,

3
10 proiettili? Bene, ora sappiamo che Joel non sta sparando un revolver.
Neil N,

69

Le chiavi esterne sono il modo migliore per garantire l'integrità referenziale di un database. Evitare le cascate a causa della magia è come scrivere tutto in assemblea perché non ti fidi della magia dietro i compilatori.

Ciò che è male è l'uso sbagliato di chiavi esterne, come ad esempio crearle all'indietro.

L'esempio di Juan Manuel è l'esempio canonico, se usi il codice ci sono molte più possibilità di lasciare documenti falsi nel database che verranno e ti morderanno.

Gli aggiornamenti a cascata sono utili, ad esempio, quando si hanno riferimenti ai dati tramite qualcosa che può cambiare, ad esempio una chiave primaria della tabella di un utente è la combinazione nome, cognome. Quindi desideri che le modifiche in quella combinazione si propaghino ovunque siano referenziate.

@Aidan, la chiarezza a cui ti riferisci ha un costo elevato, la possibilità di lasciare dati spuri nel tuo database, che non è piccolo . Per me, di solito è solo la mancanza di familiarità con il DB e l'incapacità di trovare quali FK sono in atto prima di lavorare con il DB che favoriscono quella paura. O quello, o l'uso improprio costante di cascata, usandolo dove le entità non erano concettualmente correlate, o dove devi preservare la storia.


7
Usare quel tipo di chiave primaria "naturale" è in primo luogo una pessima idea.
Nick Johnson,

1
L'idea era di mostrare un esempio degli aggiornamenti a cascata, sono d'accordo però non è l'esempio migliore. I percorsi dei file potrebbero essere un esempio migliore.
Vinko Vrsalovic,

4
RE: Commento diretto ad Aidan. No, lasciare CASCADE su un FK non aumenta la possibilità di lasciare dati spuri. Riduce la possibilità che un comando influisca su più dati di quanto previsto e aumenta il codice. Tralasciando gli FK si lascia del tutto la possibilità di dati spuri.
Shannon Severance,

5
Avendo visto almeno due volte nella mia carriera le conseguenze minacciose per il business di una incomprensibile eliminazione a cascata, non sono molto incline a usarle da solo in tutti tranne che nei casi più chiari. In entrambi i casi i dati erano stati cancellati a seguito di una cascata che avrebbe dovuto essere conservata ma che non lo era - e che mancava non veniva rilevata fino a quando il normale ciclo di backup non aveva perso la possibilità di un facile ripristino. Vinko è corretto da un punto di vista puramente logico, tuttavia nel mondo reale l'uso delle cascate espone alla fallibilità umana e alle conseguenze impreviste più di quanto vorrei.
Cruachan,

7
@Cruachan: la regola, a mio avviso, è semplice. Se i dati non sono strettamente correlati come inutili senza i dati parent, allora non garantisce una relazione a cascata. Questo è quello che ho cercato di affrontare nell'ultima frase della mia risposta.
Vinko Vrsalovic,

17

Non uso mai le eliminazioni a cascata.

Se voglio qualcosa rimosso dal database, voglio dire esplicitamente al database cosa voglio eliminare.

Ovviamente sono una funzione disponibile nel database e potrebbero esserci momenti in cui è corretto utilizzarli, ad esempio se si dispone di una tabella 'ordine' e di una tabella 'orderItem' è possibile che si desideri cancellare gli elementi quando si elimina un ordine.

Mi piace la chiarezza che ottengo dal farlo nel codice (o nella procedura memorizzata) piuttosto che accadendo "magico".

Per lo stesso motivo, non sono neanche un fan dei trigger.

Una cosa da notare è che se si elimina un 'ordine' si otterrà il rapporto '1 riga interessata' anche se l'eliminazione in cascata ha rimosso 50 'orderItem.


34
Perché non sbarazzarsi anche delle chiavi primarie? Otterresti la chiarezza di garantire valori univoci nel tuo codice.
MusiGenesis,

4
@MusiGenesis, Aidan non stava sostenendo la rimozione dell'FK. L'FK protegge ancora i dati, ma senza CASCADE ON .... la magia inaspettata non accade.
Shannon Severance,

7
@Vinko: Elimina e aggiorna hanno una semantica predefinita ben definita. Cambiare il comportamento tramite una cascata o un trigger per fare più lavoro lascia una possibilità più del previsto. No, non lavoro senza test e sì, i miei database sono documentati. Ma ricordo ogni pezzo di documentazione mentre scrivevo il codice? Se voglio una semantica di livello superiore, come eliminare genitore e figli, allora scriverò e userò un SP per farlo.
Shannon Severance,

3
@Vinko. il problema della magia non è con sviluppatori competenti o DBA, è con Joe interen 5 anni dopo a cui è stato assegnato un 'semplice' compito di manutenzione quando il DBA è in vacanza e che quindi rovina i dati aziendali senza che nessuno se ne accorga. Le cascate hanno il loro posto, ma è importante considerare tutte le circostanze, compresi i fattori umani, prima di distribuirle.
Cruachan,

5
@Vinko: Perché SP 'Gasp'? Gli SP sono in modo provocatorio la strada da percorrere laddove il database è una risorsa aziendale fondamentale. C'è un argomento forte nel tipo di circostanze di cui si stava parlando per limitare tutti gli accessi ai dati agli SP, o almeno tutti tranne Select. Vedi la mia risposta sotto stackoverflow.com/questions/1171769/…
Cruachan,

13

Lavoro molto con le eliminazioni a cascata.

È bello sapere chi lavora contro il database potrebbe non lasciare mai dati indesiderati. Se le dipendenze crescono, cambio semplicemente i vincoli nel diagramma in Management Studio e non devo modificare sp o dataacces.

Detto questo, ho 1 problema con le eliminazioni a cascata e questo è riferimenti circolari. Ciò porta spesso a parti del database che non hanno eliminazioni a cascata.


1
So che questo è molto vecchio, ma +1 per menzionare il problema di riferimento circolare con CASCADE DELETE.
Codice Maverick,

2
Perdonate una domanda noob: cosa succede realmente se si ottiene un riferimento circolare?
Tim Lovell-Smith il

10

Faccio molto lavoro di database e raramente trovo utili le eliminazioni in cascata. L'unica volta che li ho usati in modo efficace è in un database di report che viene aggiornato da un lavoro notturno. Mi assicuro che tutti i dati modificati vengano importati correttamente eliminando tutti i record di livello superiore che sono stati modificati dall'ultima importazione, quindi reimportare i record modificati e tutto ciò che li riguarda. Mi evita di dover scrivere molte eliminazioni complicate che guardano dal basso verso l'alto del mio database.

Non ritengo che le eliminazioni in cascata siano tanto brutte quanto i trigger in quanto eliminano solo i dati, i trigger possono contenere tutti i tipi di cose brutte all'interno.

In generale, evito del tutto le eliminazioni reali e utilizzo invece le eliminazioni logiche (ovvero avere una colonna di bit chiamata isDeleted che viene impostata su true).


2
Mi hai incuriosito per saperne di più. Perché preferisci fortemente le eliminazioni logiche? I dati con cui stai lavorando hanno qualcosa a che fare con esso?
Tim Lovell-Smith il

9

Un esempio è quando si hanno dipendenze tra entità ... cioè: Document -> DocumentItems (quando si elimina Document, DocumentItems non ha motivo di esistere)


5

Utilizzare l'eliminazione a cascata nel punto in cui si desidera rimuovere il record con l'FK se il record PK di riferimento è stato rimosso. In altre parole, dove il record non ha senso senza il record di riferimento.

Trovo che l'eliminazione a cascata sia utile per garantire che i riferimenti morti vengano rimossi per impostazione predefinita anziché causare eccezioni nulle.


5

ON Elimina cascata:

Quando si desidera eliminare le righe nella tabella figlio Se la riga corrispondente viene eliminata nella tabella padre.

Se non viene utilizzata l' eliminazione in cascata, verrà generato un errore per l' integrità referenziale .

Aggiornamento ON Cascade:

Quando si desidera che la modifica della chiave primaria venga aggiornata in chiave esterna


5

Ho sentito parlare di DBA e / o "Politica aziendale" che vietano l'utilizzo di "On Delete Cascade" (e altri) esclusivamente a causa di esperienze negative in passato. In un caso un ragazzo ha scritto tre trigger che hanno finito per chiamarsi l'un l'altro. Tre giorni di recupero hanno comportato un divieto totale dei trigger, tutto a causa delle azioni di un idjit.

Naturalmente a volte sono necessari trigger invece di "On Delete cascade", come quando è necessario conservare alcuni dati figlio. Ma in altri casi, è perfettamente valido utilizzare il metodo a cascata On Delete. Un vantaggio chiave di "On Delete cascade" è che cattura TUTTI i bambini; una procedura di attivazione / memorizzazione scritta personalizzata potrebbe non funzionare se non è stata codificata correttamente.

Credo che lo sviluppatore dovrebbe essere autorizzato a prendere la decisione in base allo sviluppo e alle specifiche. Un divieto di tappeto basato su una brutta esperienza non dovrebbe essere il criterio; il processo di pensiero "Non usare mai" è nella migliore delle ipotesi draconiano. È necessario effettuare una chiamata di giudizio ogni volta e apportare modifiche al variare del modello di business.

Non è questo lo sviluppo?


Non pensavo che avrebbe cancellato tutto ... vuoi dire che la funzione fa davvero quello che dice di fare? ...
Joel Coehoorn,

3

Un motivo per inserire un'eliminazione a cascata (anziché farlo nel codice) è migliorare le prestazioni.

Caso 1: con eliminazione in cascata

 DELETE FROM table WHERE SomeDate < 7 years ago;

Caso 2: senza eliminazione in cascata

 FOR EACH R IN (SELECT FROM table WHERE SomeDate < 7 years ago) LOOP
   DELETE FROM ChildTable WHERE tableId = R.tableId;
   DELETE FROM table WHERE tableId = R.tableid;
   /* More child tables here */
 NEXT

In secondo luogo, quando si aggiunge una tabella figlio aggiuntiva con un'eliminazione a cascata, il codice nel caso 1 continua a funzionare.

Vorrei solo inserire una cascata in cui la semantica della relazione è "parte di". Altrimenti un po 'di idiota cancellerà metà del tuo database quando lo fai:

DELETE FROM CURRENCY WHERE CurrencyCode = 'USD'

5
Non sapendo quale base di dati usi, suggerirei che la tua eliminazione manuale funziona peggio dell'eliminazione a cascata perché non è impostata su base. Nella maggior parte delle basi di dati è possibile eliminare in base a un join in un'altra tabella e quindi disporre di un'eliminazione basata su set, molto più veloce rispetto al ciclo attraverso i record.
HLGEM,

2

Cerco di evitare cancellazioni o aggiornamenti che non ho richiesto esplicitamente in SQL Server.

O attraverso la cascata o attraverso l'uso di trigger. Tendono a morderti nel culo un po 'di tempo, sia quando si cerca di rintracciare un bug o quando si diagnosticano problemi di prestazioni.

Dove li userei è nel garantire coerenza per non molto sforzo. Per ottenere lo stesso effetto, è necessario utilizzare le stored procedure.


2

Come tutti gli altri qui, trovo che le eliminazioni in cascata siano davvero solo marginalmente utili (non è davvero molto lavoro per eliminare i dati di riferimento in altre tabelle - se ci sono molte tabelle, devi semplicemente automatizzare questo con uno script) ma davvero fastidioso quando qualcuno inavvertitamente elimina alcuni dati importanti che è difficile da ripristinare.

L'unico caso in cui utilizzerei è se i dati nella tabella sono altamente controllati (ad esempio, autorizzazioni limitate) e aggiornati o eliminati solo attraverso un processo controllato (come un aggiornamento software) che è stato verificato.


1

Una cancellazione o un aggiornamento a S che rimuove un valore di chiave esterna trovato in alcune tuple di R può essere gestito in tre modi:

  1. Rifiuto
  2. Propagazione
  3. vanificazione.

La propagazione viene definita a cascata.

Esistono due casi:

‣ Se è stata eliminata una tupla in S, eliminare le tuple R che si riferivano ad essa.

‣ Se una tupla in S è stata aggiornata, aggiorna il valore nelle tuple R che si riferiscono ad essa.


0

Se stai lavorando su un sistema con molti moduli diversi in versioni diverse, può essere molto utile se gli elementi eliminati in cascata fanno parte / sono di proprietà del titolare della PK. Altrimenti, tutti i moduli richiederebbero patch immediate per ripulire i loro elementi dipendenti prima di eliminare il proprietario PK, oppure la relazione con la chiave esterna verrebbe omessa completamente, probabilmente lasciando tonnellate di immondizia nel sistema se la pulizia non fosse eseguita correttamente.

Ho appena introdotto l'eliminazione a cascata per una nuova tabella di intersezione tra due tabelle già esistenti (l'intersezione da eliminare solo), dopo che l'eliminazione a cascata era stata scoraggiata da un po 'di tempo. Inoltre, non è male se i dati vengono persi.

Tuttavia, è una cosa negativa nelle tabelle di elenco simili a enum: qualcuno elimina la voce 13 - giallo dalla tabella "colori" e tutti gli elementi gialli nel database vengono eliminati. Inoltre, questi a volte vengono aggiornati in un modo elimina tutto inserisce tutto, portando a un'integrità referenziale totalmente omessa. Ovviamente è sbagliato, ma come cambierete un software complesso che è in esecuzione da molti anni, con l'introduzione della vera integrità referenziale a rischio di effetti collaterali imprevisti?

Un altro problema è quando i valori della chiave esterna originale devono essere conservati anche dopo l'eliminazione della chiave primaria. È possibile creare una colonna tombstone e un'opzione ON DELETE SET NULL per l'FK originale, ma ciò richiede ancora trigger o codice specifico per mantenere il valore chiave ridondante (tranne dopo l'eliminazione PK).


0

Le eliminazioni in cascata sono estremamente utili quando si implementano entità di tipo e sottotipo logiche in un database fisico.

Quando vengono utilizzate tabelle di super-tipi e sottotipi separate per implementare fisicamente super-tipi / sottotipi (al contrario di raggruppare tutti gli attributi di sottotipo in una singola tabella di super-tipi fisici), esiste un -una relazione tra queste tabelle e il problema diventa quindi come mantenere le chiavi primarie al 100% sincronizzate tra queste tabelle.

Le eliminazioni in cascata possono essere uno strumento molto utile per:

1) Assicurarsi che l'eliminazione di un record di super-tipo elimini anche il record di sottotipo singolo corrispondente.

2) Assicurarsi che l'eventuale eliminazione di un record di sottotipo elimini anche il record di super-tipo. Ciò si ottiene implementando un trigger di eliminazione "anziché" sulla tabella dei sottotipi che va ed elimina il corrispondente record di super-tipo, che a sua volta elimina il record di sottotipo.

L'uso delle eliminazioni in cascata in questo modo garantisce che non esista mai alcun record di super-tipo o sottotipo orfano, indipendentemente dal fatto che si elimini prima il record di super-tipo o il record di sottotipo.


Buon esempio. In JPA è la tabella unita InheritanceStrategy. Per 1): normalmente si utilizza un framework di livello di persistenza (EclipseLink, Hibernate, ...), che implementa la sequenza eliminata per un'entità unita per eliminare prima la parte unita, quindi la parte super. Ma se si dispone di più software di base, ad esempio un processo di importazione o archiviazione, è conveniente poter semplicemente eliminare l'entità emettendo un'eliminazione sulla super parte. Riguardo a 2): essere d'accordo, ma in quel caso il cliente dovrebbe già essere consapevole che sta lavorando su una parte unita / secondaria dell'entità.
leo
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.