2011-01-01 26 views
14

Ho una tabella MySQL straniera autoreferenziale cui definizione è la seguente:Eliminazione di una riga con una chiave

 
CREATE TABLE `guestbook` (
    `Id` int(10) unsigned NOT NULL, 
    `ThreadId` int(10) unsigned NOT NULL, 
    PRIMARY KEY (`Id`), 
    KEY `ThreadId` (`ThreadId`), 
    CONSTRAINT `guestbook_ibfk_1` FOREIGN KEY (`ThreadId`) REFERENCES `guestbook` (`Id`) 
) ENGINE=InnoDB; 

e attualmente c'è solo 1 riga nella tabella:

 
mysql> select * from guestbook; 
+-----+----------+ 
| Id | ThreadId | 
+-----+----------+ 
| 211 |  211 | 
+-----+----------+ 

Il problema è che non c'è modo di cancellare questa riga senza rompere il vincolo.

 
mysql> delete from guestBook; 
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`polaris`.`guestbook`, CONSTRAINT `guestbook_ibfk_1` FOREIGN KEY (`ThreadId`) REFERENCES `guestbook` (`Id`)) 

Siccome la colonna è stata definita ThreadId non nullo, è anche possibile impostare il ThreadId un valore diverso temporaneamente per eliminare la riga. C'è un modo per cancellare la riga senza cambiare la definizione della tabella o far cadere l'intera tabella?

+1

Una riga che è una riga secondaria per se stessa è strana. – Chandu

+4

Wow è semplicemente fantastico, hai creato una struttura a tabelle che una volta che ha le righe devi disattivare i vincoli per eliminare l'ultima riga. Peccato che l'ID non fosse 22. –

+0

@Conrad: Sì, ma questo può sempre accadere con qualsiasi chiave esterna autoreferenziale. : $ –

risposta

19

È possibile disabilitare temporaneamente i vincoli di chiave con questa query:

SET foreign_key_checks = 0; 
+0

Grazie, questo sembra essere il modo migliore per ora. –

+0

Dove dovrebbe essere eseguita questa query? Su quale tavolo e come? Faresti meglio a modificare la tua risposta e renderla più chiara. – Trix

0

Ya Disattivare temporaneamente la chiave esterna

set foreign_key_checks=0; 
+0

sembra che sia foreign_key_checks, non foreign_key_constraints. :) –

5

Sono disponibili varie soluzioni. L'approccio suggerito da altri ...

SET foreign_key_checks = 0; 

... disabiliterà le chiavi esterne di ogni tavolo. Questo non è adatto per l'uso in un ambiente condiviso.

Un altro approccio è quello di cadere la chiave esterna utilizzando

ALTER TABLE `guestbook` 
    DROP FOREIGN KEY `guestbook_ibfk_1` 
/

Siamo in grado di risolvere i dati utilizzando DML, e quindi ripristinare la chiave esterna utilizzando:

ALTER TABLE `guestbook` 
    ADD CONSTRAINT `guestbook_ibfk_1` FOREIGN KEY (`ThreadId`) 
     REFERENCES `guestbook` (`Id`) 
/

Ma c'è un modo per cambia i dati senza eseguire alcun DDL? Beh, siamo in grado di inserire un nuovo record e modificare il record corrente per farvi riferimento:

INSERT INTO `guestbook` VALUES (212, 211) 
/
UPDATE `guestbook` 
SET `ThreadId` = 212 
WHERE `Id` = 211 
/

osservatori più attenti avranno notato che non abbiamo ancora finito con una co-dipendenza, solo tra i record. Quindi non abbiamo davvero avanzato; ora abbiamo due record che non possiamo cancellare, invece di uno. (Per inciso ciò si applica a qualsiasi DML che potremmo eseguire mentre la chiave esterna è rilasciata o disabilitata). Quindi, forse abbiamo bisogno di riconsiderare il modello dei dati. Stiamo modellando un grafico con dipendenze circolari o una gerarchia?

Una struttura di dati gerarchica richiede almeno un nodo radice, un record da cui possono dipendere altri record ma che di per sé non dipende da nessun record. Il solito modo di implementare questo è rendere facoltativa la colonna chiave esterna. Al livello più in alto della gerarchia, il record deve avere un valore NULL in quella colonna. Se ci dovrebbe essere solo un nodo root di questo tipo o se molti sarebbero consentiti è una questione per le tue regole di business.

ALTER TABLE `guestbook` MODIFY `ThreadId` int(10) unsigned 
/

In termini di modellazione questo non è diverso da un disco che è proprio padrone, ma è una soluzione più intuitiva.

3

L'impossibilità di eliminare una riga autoreferenziale è di lunga data known bug/outstanding feature request in MySQL.

In molte situazioni in cui si strofina su questo problema, è possibile NULL la chiave esterna prima di eseguire l'eliminazione, quindi la soluzione alternativa riguarda solo le righe che si desidera (utilizza la stessa clausola WHERE).

0

Se si imposta uno ON DELETE SET NULL sulla chiave esterna, è possibile eliminare un riferimento automatico. Se non si specifica un valore ON DELETE, MySQL assume automaticamente il valore RESTRICT.

Ovviamente, assicurarsi che la colonna sia NULLABLE. Si può anche provare SET DEFAULT a seconda di ciò che è l'impostazione predefinita. Ma ricorda che NO ACTION è solo un alias per RESTRICT in MySQL!

Testato solo su MySQL 5.6 (che non è stato rilasciato quando questa domanda era stata originariamente pubblicata).

3

Se si inserisce un'azione ON DELETE CASCADE sulla chiave esterna, si dovrebbe essere in grado di eliminare le righe che si auto-referenziano.

CONSTRAINT `guestbook_ibfk_1` FOREIGN KEY (`ThreadId`) REFERENCES `guestbook` (`Id`) ON DELETE CASCADE 

Il vantaggio questo ha rispetto all'uso di ON DELETE SET NULL è che non c'è bisogno di modificare lo schema di fare il "ThreadId" colonna Null.

+0

Non capisco perché questa risposta non sia la risposta migliore e corretta. – RusAlex

Problemi correlati