2009-12-15 10 views
94

Nel fare:non è possibile eliminare o aggiornare una riga padre: un vincolo di chiave esterna non riesce

DELETE FROM `jobs` WHERE `job_id` =1 LIMIT 1 

errori IT:

#1451 - Cannot delete or update a parent row: a foreign key constraint fails 
(paymesomething.advertisers, CONSTRAINT advertisers_ibfk_1 FOREIGN KEY 
(advertiser_id) REFERENCES jobs (advertiser_id)) 

Qui sono le mie tabelle:

CREATE TABLE IF NOT EXISTS `advertisers` (
    `advertiser_id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    `name` varchar(255) NOT NULL, 
    `password` char(32) NOT NULL, 
    `email` varchar(128) NOT NULL, 
    `address` varchar(255) NOT NULL, 
    `phone` varchar(255) NOT NULL, 
    `fax` varchar(255) NOT NULL, 
    `session_token` char(30) NOT NULL, 
    PRIMARY KEY (`advertiser_id`), 
    UNIQUE KEY `email` (`email`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ; 


INSERT INTO `advertisers` (`advertiser_id`, `name`, `password`, `email`, `address`, `phone`, `fax`, `session_token`) VALUES 
(1, 'TEST COMPANY', '', '', '', '', '', ''); 

CREATE TABLE IF NOT EXISTS `jobs` (
    `job_id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    `advertiser_id` int(11) unsigned NOT NULL, 
    `name` varchar(255) NOT NULL, 
    `shortdesc` varchar(255) NOT NULL, 
    `longdesc` text NOT NULL, 
    `address` varchar(255) NOT NULL, 
    `time_added` int(11) NOT NULL, 
    `active` tinyint(1) NOT NULL, 
    `moderated` tinyint(1) NOT NULL, 
    PRIMARY KEY (`job_id`), 
    KEY `advertiser_id` (`advertiser_id`,`active`,`moderated`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ; 


INSERT INTO `jobs` (`job_id`, `advertiser_id`, `name`, `shortdesc`, `longdesc`, `address`, `active`, `moderated`) VALUES 
(1, 1, 'TEST', 'TESTTEST', 'TESTTESTES', '', 0, 0); 

ALTER TABLE `advertisers` 
    ADD CONSTRAINT `advertisers_ibfk_1` FOREIGN KEY (`advertiser_id`) REFERENCES `jobs` (`advertiser_id`); 

risposta

65

Così com'è, è necessario eliminare la riga dalla tabella degli inserzionisti prima di poter eliminare la riga nella tabella dei lavori a cui fa riferimento. Questo:

ALTER TABLE `advertisers` 
    ADD CONSTRAINT `advertisers_ibfk_1` FOREIGN KEY (`advertiser_id`) 
     REFERENCES `jobs` (`advertiser_id`); 

... è in realtà l'opposto di quello che dovrebbe essere. Così com'è, significa che dovresti avere un record nella tabella dei lavori prima degli inserzionisti. Quindi è necessario utilizzare:

ALTER TABLE `jobs` 
    ADD CONSTRAINT `advertisers_ibfk_1` FOREIGN KEY (`advertiser_id`) 
     REFERENCES `advertisers` (`advertiser_id`); 

Una volta corretta la relazione della chiave esterna, l'istruzione di eliminazione funzionerà.

+2

Nella prima riga: non pensi che dovrebbe essere "che fa riferimento" invece di "che lo fa riferimento"? O ho frainteso come dovrebbe funzionare la terminologia dei riferimenti? –

+5

@AbrahamPhilip Stavo pensando la stessa cosa. inserzionisti fa riferimento a lavori. – keyser

24

Sotto la tua design attuale (eventualmente imperfetto), è necessario eliminare la riga dalla tabella degli inserzionisti prima del è possibile eliminare la riga nella tabella dei lavori a cui fa riferimento.

In alternativa, è possibile impostare la chiave esterna in modo che un'eliminazione nella tabella padre causi l'eliminazione automatica delle righe nelle tabelle figlio. Questo è chiamato eliminazione a catena. Sembra qualcosa di simile:

ALTER TABLE `advertisers` 
ADD CONSTRAINT `advertisers_ibfk_1` 
FOREIGN KEY (`advertiser_id`) REFERENCES `jobs` (`advertiser_id`) 
ON DELETE CASCADE; 

Detto questo, come altri hanno già fatto notare, la vostra chiave esterna si sente come dovrebbe andare il contrario dal momento che il tavolo inserzionisti contiene davvero la chiave primaria e la tabella di posti di lavoro contiene la chiave straniera. Vorrei riscriverlo in questo modo:

ALTER TABLE `jobs` 
ADD FOREIGN KEY (`advertiser_id`) REFERENCES `advertisers` (`advertiser_id`); 

E la cancellazione a catena non sarà necessaria.

+1

Asaph, correggimi se ho torto, ma non ci saranno più lavori per inserzionista? –

+0

@Rashmi: sei corretto –

+0

@Rashmi Pandit: ho aggiunto ulteriore discussione alla mia risposta che affronta questo punto. – Asaph

3

Se ci sono più di un lavoro che ha lo stesso ADVERTISER_ID, allora la vostra chiave esterna dovrebbe essere:

ALTER TABLE `jobs` 
ADD CONSTRAINT `advertisers_ibfk_1` 
FOREIGN KEY (`advertiser_id`) 
REFERENCES `advertisers` (`advertiser_id`); 

In caso contrario (se viceversa nel tuo caso), se si desidera che le righe di inserzionista da eliminare automaticamente se la riga di lavoro viene eliminato aggiungere il 'ON DELETE CASCADE' opzione per il tuo estera chiave

ALTER TABLE `advertisers` 
ADD CONSTRAINT `advertisers_ibfk_1` 
FOREIGN KEY (`advertiser_id`) 
REFERENCES `jobs` (`advertiser_id`); 
ON DELETE CASCASE 

Partenza Foreign Key constraints

5

Penso che la chiave esterna sia indietro. Prova:

ALTER TABLE 'jobs' 
ADD CONSTRAINT `advertisers_ibfk_1` FOREIGN KEY (`advertiser_id`) REFERENCES `advertisers` (`advertiser_id`) 
0

Forse si dovrebbe cercare ON DELETE CASCADE

+26

Aggiungendo ciecamente un'eliminazione a catena (che distruggerà i dati) senza capire il problema è solo la cosa peggiore che si possa fare. –

1

Quando si crea database o creare tabelle

Si dovrebbe aggiungere quella linea in alto script di creare database o una tabella

SET @[email protected]@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=1; 

Ora vuoi cancellare i record dalla tabella?poi si scrive come

SET @[email protected]@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=1; 
DELETE FROM `jobs` WHERE `job_id` =1 LIMIT 1 

Buona fortuna!

150

Il modo semplice sarebbe disabilitare il controllo della chiave esterna; apportare le modifiche quindi riattivare il controllo della chiave esterna.

SET FOREIGN_KEY_CHECKS=0; -- to disable them 
SET FOREIGN_KEY_CHECKS=1; -- to re-enable them 
+92

Questa non è una soluzione al problema, ma piuttosto un work-around sporco che potrebbe non essere desiderato. – madfriend

+11

Nel mio caso: ho appena eseguito un file SQL di grandi dimensioni e una delle istruzioni finali non è riuscita, quindi voglio solo eliminare tutte le tabelle, correggere l'errore di sintassi ed eseguire nuovamente, rendendo esattamente ciò che stavo cercando. – ekerner

+1

Se si dovesse fare questo, perché non rimuovere solo tutti i vincoli? – Sablefoste

0

è necessario eliminarlo per ordine Ci sono dipendenze nelle tabelle

0

se avete bisogno di supporto client al più presto possibile, e non hanno accesso a

FOREIGN_KEY_CHECKS 

così che l'integrità dei dati può essere disabilitata:

1) cancella chiave esterna

ALTER TABLE `advertisers` 
DROP FOREIGN KEY `advertisers_ibfk_1`; 

2) attivare l'operazione cancellazione thruogh SQL o api

3) aggiungere la chiave esterna torna allo schema

ALTER TABLE `advertisers` 
    ADD CONSTRAINT `advertisers_ibfk_1` FOREIGN KEY (`advertiser_id`) REFERENCES `jobs` (`advertiser_id`); 

tuttavia, si tratta di un hot-fix, quindi è da soli rischio, perché il difetto principale di tale approccio è che è necessario successivamente per mantenere l'integrità dei dati manualmente.

6

Se si vuole eliminare una tabella si dovrebbe eseguire la seguente query in un unico passaggio

FOREIGN_KEY_CHECKS SET = 0; DROP TABLE nome_tabella;

1

Che ne dite di questa alternativa ho usato: consentire la chiave esterna per essere NULL e quindi scegliere ON DELETE SET NULL.

Personalmente preferisco utilizzando sia "ON UPDATE CASCADE", così come "ON DELETE SET NULL" per evitare inutili complicazioni, ma sul set up è possibile un approccio diverso. Inoltre, i valori delle chiavi straniere di NULL possono portare a complicazioni in quanto non si sa cosa sia successo esattamente lì. Quindi questo cambiamento dovrebbe essere in stretta relazione con il funzionamento del codice dell'applicazione.

Spero che questo aiuti.

2

Ho avuto questo problema nella migrazione laravel troppo
l'ordine delle tabelle goccia in giù() metodo che conta

Schema::dropIfExists('groups'); 
Schema::dropIfExists('contact'); 

potrebbero non funzionare, ma se si cambia l'ordine, funziona.

Schema::dropIfExists('contact'); 
Schema::dropIfExists('groups'); 
0

È possibile creare un trigger per eliminare le righe di riferimento prima di eliminare il lavoro.

DELIMITER $$ 
    CREATE TRIGGER before_jobs_delete 
     BEFORE DELETE ON jobs 
     FOR EACH ROW 
    BEGIN 
     delete from advertisers where advertiser_id=OLD.advertiser_id; 
    END$$ 
    DELIMITER ; 
Problemi correlati