Questo è il punto centrale dei vincoli delle chiavi esterne: ti impediscono di eliminare i dati a cui si fa riferimento altrove per mantenere l'integrità referenziale.
Esistono due opzioni:
- Elimina prima le righe
INVENTORY_ITEMS, quindi le righe da STOCK_ARTICLES.
- Utilizzare
ON DELETE CASCADEper la definizione chiave.
1: eliminazione nell'ordine corretto
Il modo più efficiente per farlo varia in base alla complessità della query che decide quali righe eliminare. Un modello generale potrebbe essere:
BEGIN TRANSACTION
SET XACT_ABORT ON
DELETE INVENTORY_ITEMS WHERE STOCK_ARTICLE IN (<select statement that returns stock_article.id for the rows you are about to delete>)
DELETE STOCK_ARTICLES WHERE <the rest of your current delete statement>
COMMIT TRANSACTION
Questo va bene per semplici query o per eliminare un singolo articolo di scorta, ma dato la tua dichiarazione di eliminazione contiene una WHERE NOT EXISTSclausola di annidamento che al suo interno WHERE INpotrebbe produrre un piano molto inefficiente, quindi prova con una dimensione realistica del set di dati e riorganizza la query se necessario.
Nota anche le dichiarazioni di transazione: assicurati che entrambe le eliminazioni siano complete o nessuna delle due funzioni. Se l'operazione sta già avvenendo all'interno di una transazione, dovrai ovviamente modificarla in modo che corrisponda alla transazione corrente e al processo di gestione degli errori.
2: utilizzare ON DELETE CASCADE
Se aggiungi l'opzione a cascata alla tua chiave esterna, SQL Server lo farà automaticamente per te, rimuovendo le righe INVENTORY_ITEMSper soddisfare il vincolo che nulla dovrebbe fare riferimento alle righe che stai eliminando. Basta aggiungere ON DELETE CASCADEalla definizione FK in questo modo:
ALTER TABLE <child_table> WITH CHECK
ADD CONSTRAINT <fk_name> FOREIGN KEY(<column(s)>)
REFERENCES <parent_table> (<column(s)>)
ON DELETE CASCADE
Un vantaggio qui è che l'eliminazione è un'istruzione atomica che riduce (sebbene, come al solito, non la rimozione al 100%) la necessità di preoccuparsi delle impostazioni di transazione e blocco. La cascata può persino operare su più livelli genitore / figlio / nipote / ... se esiste un solo percorso tra genitore e tutti i discendenti (cercare "percorsi multipli in cascata" per esempi di dove potrebbe non funzionare).
NOTA: io, e molti altri, ritengo che le eliminazioni in cascata siano pericolose, quindi se si utilizza questa opzione fare molta attenzione a documentarla correttamente nella progettazione del database in modo che tu e altri sviluppatori non inciampiate sul pericolo in seguito . Evito le eliminazioni a cascata laddove possibile per questo motivo.
Un problema comune causato dalle eliminazioni in cascata è quando qualcuno aggiorna i dati rilasciando e ricreando le righe invece di usare UPDATEo MERGE. Questo è spesso visto dove è necessario "aggiornare le righe già esistenti, inserire quelle che non lo fanno" (a volte chiamato operazione UPSERT) e le persone che non sono consapevoli dell'istruzione MERGEtrovano più facile fare:
DELETE <all rows that match IDs in the new data>
INSERT <all rows from the new data>
di
-- updates
UPDATE target
SET <col1> = source.<col1>
, <col2> = source.<col2>
...
, <colN> = source.<colN>
FROM <target_table> AS target JOIN <source_table_or_view_or_statement> AS source ON source.ID = target.ID
-- inserts
INSERT <target_table>
SELECT *
FROM <source_table_or_other> AS source
LEFT OUTER JOIN
<target_table> AS target
ON target.ID = source.ID
WHERE target.ID IS NULL
Il problema qui è che l'istruzione delete passerà in cascata alle righe figlio e l'istruzione insert non le ricrea, quindi durante l'aggiornamento della tabella padre si perderanno accidentalmente i dati dalle tabelle figlio.
Sommario
Sì, devi prima eliminare le righe figlio.
C'è un'altra opzione: ON DELETE CASCADE.
Ma ON DELETE CASCADEpuò essere pericoloso , quindi usalo con cura.
Nota a margine: usare MERGE(o UPDATE-e-- INSERTdove MERGEnon è disponibile) quando è necessaria UPSERTun'operazione, non DELETE -poi-sostituire-con- INSERTper evitare di cadere in trappole posate da altre persone che usano ON DELETE CASCADE.
INVENTORY_ITEMSaggiunti tra i dueDELETE.