2009-10-28 7 views
15

in MSSQL 2005 ho appena colpito il messaggio di errore infame:Qual è il problema con la chiave esterna a cascata più percorsi e cicli?

Introducendo vincolo FOREIGN KEY XXX YYY sul tavolo può causare cicli o più percorsi a cascata. Specificare ON DELETE NO ACTION o ON UPDATE NO ACTION o modificare altri vincoli FOREIGN KEY.

Ora, StackOverflow ha diversi argomenti su questo messaggio di errore, così ho già la soluzione (nel mio caso dovrò utilizzare i trigger), ma io sono curioso di sapere perché v'è una tale problema a tutti.

A quanto mi risulta, ci sono fondamentalmente due scenari che vogliono evitare - un ciclo e percorsi multipli. Un ciclo sarebbe il punto in cui due tabelle hanno chiavi esterne a cascata l'una rispetto all'altra. OK, un ciclo può comprendere anche più tabelle, ma questo è il caso di base e sarà più semplice da analizzare.

percorsi multipli sarebbero quando TableA ha chiavi esterne per TableB e TableC, e TableB ha anche una chiave esterna per TableC. Ancora una volta - questo è il caso di base minimo.

non riesco a vedere gli eventuali problemi che potrebbero sorgere quando un record otterrebbe cancellati o aggiornati in una di queste tabelle. Certo, potrebbe essere necessario interrogare la stessa tabella più volte per vedere quali record devono essere aggiornati/eliminati, ma è davvero un problema? È un problema di prestazioni?

In altri argomenti SO le persone arrivano fino all'etichetta che utilizza cascate come "risky" e affermano che "resolving cascade paths is a complex problem". Perché? Dov'è il rischio? Dov'è il problema?

risposta

4

Si dispone di una tabella figlio con 2 percorsi a cascata dello stesso genitore: uno "Elimina", uno "Nullo".

Che cosa ha la precedenza? Cosa ti aspetti dopo? ecc

Nota: un trigger è codice e può aggiungere alcune informazioni o condizioni a una cascata.

0

Sono d'accordo che le cascate siano "rischiose" e dovrebbero essere evitate. (Personalmente preferisco applicare a cascata le modifiche manualmente piuttosto che avere un server SQL che si occupi automaticamente di loro). Questo è anche perché, anche se il server SQL cancellato milioni di righe, l'uscita sarebbe ancora visualizzato come

(1 row (s) affected)

+1

Sostituirlo con un trigger lo rende migliore? Naturalmente - esiste un approccio del genere come "non usare le chiavi estranee" e fare tutto dal codice. Non penso di doverti dire perché penso che sia una cattiva idea ... –

1

Il motivo per cui non voglia utilizzare eliminazione a catena ha a che fare con le prestazioni e bloccaggio . Sì, non è così male quando si elimina un record, ma prima o poi sarà necessario eliminare un grande gruppo di record e il database si fermerà.

Se si sta eliminando abbastanza dischi, SQL Server potrebbe degenerare in un blocco di tabella e nessuno può fare qualsiasi cosa con il tavolo fino a quando non è finito.

Recentemente abbiamo spostato uno dei nostri clienti sul proprio server. Come parte dell'accordo abbiamo anche dovuto eliminare tutti i record di quel cliente dal nostro server originale. Cancellare tutte le sue informazioni in lotti (in modo da non causare problemi con altri utenti) ha richiesto un paio di mesi. Se avessimo impostato la cancellazione a cascata, il database sarebbe stato inaccessibile agli altri client per un lungo periodo, poiché milioni di record sono stati eliminati in una transazione e centinaia di tabelle sono state bloccate fino al completamento della transazione.

Potrei anche vedere uno scenario in cui un deadlock potrebbe essersi verificato utilizzando l'eliminazione a cascata perché non abbiamo alcun controllo sull'ordine che il percorso a cascata avrebbe preso e il nostro database è in qualche modo denormalizzato con clientid che appare nella maggior parte delle tabelle. Quindi, se ha bloccato la tabella che aveva una chiave esterna anche in una terza tabella e la tabella client che si trovava in un percorso diverso, probabilmente non poteva controllare quella tabella per eliminarla dalla terza tabella perché questo è tutto una transazione e le serrature non saranno rilasciate fino a quando non è stato fatto. Quindi probabilmente non ci avrebbe permesso di impostare le eliminazioni a cascata se vedesse la possibilità di creare deadlock nella transazione.

Un altro motivo per evitare le eliminazioni a cascata è che a volte l'esistenza di un record figlio è una ragione sufficiente per non eliminare il record padre. Ad esempio, se hai una tabella clienti e quel cliente ha avuto ordini in passato, non vorrai cancellarlo e perdere le informazioni sull'ordine effettivo.

+4

Se hai a che fare con milioni di righe, quasi sempre devi usare soluzioni alternative come il batching, disabilitare le chiavi esterne, eseguire il lavoro nei momenti di inattività, ecc. Ma quello non è uno scenario quotidiano. Le persone si preparano per questo genere di cose in anticipo. Lo testano in un ambiente di test. Realizzano script lunghi e contorti che modificano il DB in modo da ottenere prestazioni accettabili. E una volta fatto è dimenticato. Ma "ON DELETE CASCADE" è pensato per essere utilizzato nella vita di tutti i giorni, quando non si eliminano milioni di righe ma al massimo poche centinaia alla volta. –

+0

E deadlock su cascade (o anche eliminazioni semplici per più righe nella stessa tabella) possono comunque accadere, impedendo più percorsi a cascata e cicli non fa un bit per renderlo migliore. –

1

consideri una tabella di dipendenti:

CREATE TABLE Employee 
(
    EmpID INTEGER NOT NULL PRIMARY KEY, 
    Name VARCHAR(40) NOT NULL, 
    MgrID INTEGER NOT NULL REFERENCES Employee(EmpID) ON DELETE CASCADE 
); 

INSERT INTO Employees( 1, "Bill", 1); 
INSERT INTO Employees( 23, "Steve", 1); 
INSERT INTO Employees(234212, "Helen", 23); 

Ora supponiamo Bill si ritira:

DELETE FROM Employees WHERE Name = "Bill"; 

Ooooppps; tutti sono stati licenziati!

[Possiamo discutere se i dettagli della sintassi sono corretti; il concetto sta, penso.]

+13

Direi che in questo caso l'ON DELETE CASCADE è stato usato in modo errato. Spiacente, è colpa tua se costruisci un programma buggy - non è compito del linguaggio di programmazione proteggerti dalla scrittura di codice errato. –

-1

Penso che utilizzare o meno un'opzione ON DELETE CASCADE sia una questione del modello di business che si sta implementando. Una relazione tra due oggetti di business potrebbe essere una semplice "associazione", in cui entrambi i fini della relazione sono correlati, ma in ogni caso oggetti indipendenti il ​​cui ciclo di vita è diverso e controllato da altra logica. Esistono tuttavia anche relazioni di "aggregazione", in cui un oggetto potrebbe effettivamente essere visto come "genitore" o "proprietario" di un oggetto "figlio" o "dettaglio". Esiste la nozione ancora più forte di una relazione di "composizione", in cui un oggetto esiste unicamente come una composizione di un numero di parti. Nel caso di "associazione", di solito non si dichiara un vincolo ON DELETE CASCADE. Per aggregazioni o composizioni, tuttavia, ON DELETE CASCADE aiuta a mappare il modello di business nel database in modo più accurato e in modo dichiarativo. Questo è il motivo per cui mi infastidisce il fatto che MS SQL Server limiti l'uso di questa opzione a un singolo percorso a cascata. Se non sbaglio, molti altri sistemi di database SQL ampiamente utilizzati non impongono tali restrizioni.

+0

E questa è una risposta ... come? –

1

Penso che il problema è che quando si effettua un percorso "ON DELETE CASCADE" e l'altro "ON DELETE RESTRICT" o "NO ACTION" il risultato (il risultato) è imprevedibile. Dipende da quale trigger di cancellazione (questo è anche un trigger, ma non devi costruirti da solo) verrà eseguito per primo.

+0

Con le transazioni, questo non dovrebbe essere un problema: la riga viene eliminata, elimina le cascate, quindi l'eliminazione viene limitata e l'intera operazione viene ripristinata. – Brilliand