MySQL Creazione di tabelle con chiavi esterne che danno errno: 150


98

Sto cercando di creare una tabella in MySQL con due chiavi esterne, che fanno riferimento alle chiavi primarie in altre 2 tabelle, ma ricevo un errore errno: 150 e non creerà la tabella.

Ecco l'SQL per tutte e 3 le tabelle:

CREATE TABLE role_groups (
  `role_group_id` int(11) NOT NULL `AUTO_INCREMENT`,
  `name` varchar(20),
  `description` varchar(200),
  PRIMARY KEY (`role_group_id`)
) ENGINE=InnoDB;

CREATE TABLE IF NOT EXISTS `roles` (
  `role_id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50),
  `description` varchar(200),
  PRIMARY KEY (`role_id`)
) ENGINE=InnoDB;

create table role_map (
  `role_map_id` int not null `auto_increment`,
  `role_id` int not null,
  `role_group_id` int not null,
  primary key(`role_map_id`),
  foreign key(`role_id`) references roles(`role_id`),
  foreign key(`role_group_id`) references role_groups(`role_group_id`)
) engine=InnoDB;

Qualsiasi aiuto sarebbe molto apprezzato.


1
Potresti pubblicare l'output dell'errore e dirci quale comando (dei tre) sta causando l'errore?
dave

4
Cosa sono le zecche in giro auto_increment? Non è valido. Auto_increment è una parola chiave, non un identificatore.
Bill Karwin,

Risposte:


238

Ho avuto lo stesso problema con ALTER TABLE ADD FOREIGN KEY.

Dopo un'ora, ho scoperto che queste condizioni devono essere soddisfatte per non ottenere l'errore 150:

  1. La tabella padre deve esistere prima di definire una chiave esterna per farvi riferimento. È necessario definire le tabelle nell'ordine corretto: prima la tabella padre, quindi la tabella figlia. Se entrambe le tabelle fanno riferimento l'una all'altra, è necessario creare una tabella senza vincoli FK, quindi creare la seconda tabella, quindi aggiungere il vincolo FK alla prima tabella con ALTER TABLE.

  2. Le due tabelle devono supportare entrambe i vincoli di chiave esterna, ad es ENGINE=InnoDB. Altri motori di archiviazione ignorano silenziosamente le definizioni di chiavi esterne, quindi non restituiscono errori o avvisi, ma il vincolo FK non viene salvato.

  3. Le colonne di riferimento nella tabella padre devono essere le colonne più a sinistra di una chiave. Meglio se la chiave nel genitore è PRIMARY KEYo UNIQUE KEY.

  4. La definizione FK deve fare riferimento alle colonne PK nello stesso ordine della definizione PK. Ad esempio, se l'FK, REFERENCES Parent(a,b,c)il PK principale non deve essere definito nelle colonne in ordine (a,c,b).

  5. Le colonne PK nella tabella Parent devono essere dello stesso tipo di dati delle colonne FK nella tabella Child. Ad esempio, se una colonna PK nella tabella Parent è UNSIGNED, assicurati di definire UNSIGNEDla colonna corrispondente nel campo Tabella Child.

    Eccezione: la lunghezza delle stringhe può essere diversa. Ad esempio, VARCHAR(10)può fare riferimento VARCHAR(20)o viceversa.

  6. Qualsiasi colonna FK di tipo stringa deve avere lo stesso set di caratteri e regole di confronto delle colonne PK corrispondenti.

  7. Se sono già presenti dati nella tabella figlio, ogni valore nelle colonne FK deve corrispondere a un valore nelle colonne PK della tabella padre. Controlla questo con una query come:

    SELECT COUNT(*) FROM Child LEFT OUTER JOIN Parent ON Child.FK = Parent.PK 
    WHERE Parent.PK IS NULL;

    Deve restituire zero (0) valori non corrispondenti. Ovviamente, questa query è un esempio generico; è necessario sostituire i nomi delle tabelle e delle colonne.

  8. Né la tabella padre né la tabella figlio possono essere una TEMPORARYtabella.

  9. Né la tabella padre né la tabella figlio possono essere una PARTITIONEDtabella.

  10. Se si dichiara un FK con l' ON DELETE SET NULLopzione, le colonne FK devono essere annullabili.

  11. Se si dichiara un nome di vincolo per una chiave esterna, il nome del vincolo deve essere univoco nell'intero schema, non solo nella tabella in cui è definito il vincolo. Due tabelle potrebbero non avere il proprio vincolo con lo stesso nome.

  12. Se sono presenti altri FK in altre tabelle che puntano allo stesso campo per il quale si sta tentando di creare il nuovo FK, e sono malformati (ovvero regole di confronto diverse), sarà necessario renderli coerenti prima. Questo può essere il risultato di modifiche passate in cui è SET FOREIGN_KEY_CHECKS = 0;stato utilizzato con una relazione incoerente definita per errore. Vedi la risposta di @ andrewdotn di seguito per istruzioni su come identificare questi problemi di FK.

Spero che questo ti aiuti.


4
un'altra cosa che vale la pena aggiungere: se il PK della tabella genitore è più di un campo, l'ordine dei campi nella FK deve essere lo stesso dell'ordine nel PK
Kip

26
Questo include cose come int(11) unsigned NOT NULLvs int(11) NOT NULL.
Glen Solsberry

4
ALTER TABLE nome_tabella ENGINE = InnoDB;
TolMera

12
Se la tabella è definita ENGINE = MyISAM non genera errno 150 perché ignora le dichiarazioni di chiave esterna. È come dire che il modo migliore per evitare problemi con il motore della tua automobile è guidare una barca. :-)
Bill Karwin

2
Inoltre, se la ON DELETEregola del tuo CONSTRAINT è SET NULLassicurarti che la chiave esterna possa effettivamente essere NULL! Ho passato 30 minuti a leggere questa risposta più e più volte, assicurandomi che le mie tabelle soddisfacessero le condizioni ma ottenendo ancora l'errore 150. Poi ho notato che il mio FK era un campo NOT NULL, il che significa che la regola era impossibile da applicare.
Martin Joiner

62

Il messaggio generico "errno 150" di MySQL " significa che un vincolo di chiave esterna non è stato formato correttamente ." Come probabilmente già saprai se stai leggendo questa pagina, il messaggio di errore generico "errno: 150" è davvero inutile. Però:

È possibile ottenere il messaggio di errore effettivo eseguendo SHOW ENGINE INNODB STATUS;e quindi cercando LATEST FOREIGN KEY ERRORnell'output.

Ad esempio, questo tentativo di creare un vincolo di chiave esterna:

CREATE TABLE t1
(id INTEGER);

CREATE TABLE t2
(t1_id INTEGER,
 CONSTRAINT FOREIGN KEY (t1_id) REFERENCES t1 (id));

fallisce con l'errore Can't create table 'test.t2' (errno: 150). Questo non dice a nessuno niente di utile se non che è un problema di chiave esterna. Ma corri SHOW ENGINE INNODB STATUS;e dirà:

------------------------
LATEST FOREIGN KEY ERROR
------------------------
130811 23:36:38 Error in foreign key constraint of table test/t2:
FOREIGN KEY (t1_id) REFERENCES t1 (id)):
Cannot find an index in the referenced table where the
referenced columns appear as the first columns, or column types
in the table and the referenced table do not match for constraint.

Dice che il problema è che non riesce a trovare un indice. SHOW INDEX FROM t1mostra che non ci sono affatto indici per table t1. Risolvilo, ad esempio definendo una chiave primaria su t1, e il vincolo di chiave esterna verrà creato correttamente.


4
SHOW ENGINE INNODB STATUSmi ha aiutato a identificare immediatamente un problema che stavo cercando di diagnosticare per quasi un'ora. Grazie.
jatrim

Nel mio caso, questo indicava che una tabella completamente diversa che conteneva lo stesso campo a cui stavo tentando di indicare era incoerente e quindi non salverebbe quella nuova ... supponendo che fosse utilizzata SET FOREIGN_KEY_CHECKS = 0;durante un'importazione / modifica che era malformata prima o poi. Grande aiuto, grazie.
oucil

25

Assicurati che le proprietà dei due campi che stai cercando di collegare con un vincolo siano esattamente le stesse.

Spesso, la proprietà "non firmato" su una colonna ID ti sorprenderà.

ALTER TABLE `dbname`.`tablename` CHANGE `fieldname` `fieldname` int(10) UNSIGNED NULL;

Nella mia esperienza, vale la pena utilizzare SHOW CREATE TABLE di MySQL sulla tabella principale per verificare esattamente quali flag sono impostati sulla colonna dell'indice principale, quindi copiarli nella colonna della chiave esterna. Potrebbero esserci cose, come "non firmato", che non sono ovvie.
Ambulare

10

Qual è lo stato attuale del tuo database quando esegui questo script? È completamente vuoto? Il tuo SQL funziona bene per me quando creo un database da zero, ma errno 150 di solito ha a che fare con l'eliminazione e la ricreazione di tabelle che fanno parte di una chiave esterna. Ho la sensazione che tu non stia lavorando con un database nuovo e fresco al 100%.

Se si verifica un errore durante la "sorgente" del file SQL, si dovrebbe essere in grado di eseguire il comando "SHOW ENGINE INNODB STATUS" dal prompt di MySQL immediatamente dopo il comando "source" per visualizzare informazioni più dettagliate sull'errore.

Potresti voler controllare anche l'immissione manuale:

Se si ricrea una tabella che è stata eliminata, deve avere una definizione conforme ai vincoli di chiave esterna che fanno riferimento ad essa. Deve avere i nomi e i tipi di colonna di destra e deve avere indici sulle chiavi di riferimento, come affermato in precedenza. Se questi non sono soddisfatti, MySQL restituisce il numero di errore 1005 e fa riferimento all'errore 150 nel messaggio di errore. Se MySQL riporta un numero di errore 1005 da un'istruzione CREATE TABLE e il messaggio di errore fa riferimento all'errore 150, la creazione della tabella non è riuscita perché un vincolo di chiave esterna non è stato formato correttamente.

- Manuale di riferimento di MySQL 5.1 .


5

Per le persone che stanno visualizzando questo thread con lo stesso problema:

Ci sono molti motivi per ottenere errori come questo. Per un elenco abbastanza completo delle cause e delle soluzioni degli errori di chiave esterna in MySQL (inclusi quelli discussi qui), controlla questo link:

Errori di chiave esterna MySQL ed Errno 150


4

Per gli altri che trovano questa voce SO tramite Google: assicurati di non provare a eseguire un'azione SET NULL su una colonna di chiave esterna (to be) definita "NOT NULL". Ciò ha causato grande frustrazione fino a quando non mi sono ricordato di fare un CONTROLLO STATO INNODB DEL MOTORE.


3

Sicuramente non è così, ma ho trovato questo errore piuttosto comune e non ovvio. L'obiettivo di una FOREIGN KEYpotrebbe non essere PRIMARY KEY. La risposta che mi diventa utile è:

Una CHIAVE ESTERA deve sempre essere puntata su un campo true PRIMARY KEY di un'altra tabella.

CREATE TABLE users(
   id INT AUTO_INCREMENT PRIMARY KEY,
   username VARCHAR(40));

CREATE TABLE userroles(
   id INT AUTO_INCREMENT PRIMARY KEY,
   user_id INT NOT NULL,
   FOREIGN KEY(user_id) REFERENCES users(id));

3

Come indicato da @andrewdotn, il modo migliore è vedere l'errore dettagliato ( SHOW ENGINE INNODB STATUS;) invece di un semplice codice di errore.

Uno dei motivi potrebbe essere che un indice già esiste con lo stesso nome, potrebbe essere in un'altra tabella. Come pratica, consiglio di anteporre il nome della tabella al nome dell'indice per evitare tali collisioni. ad esempio invece di idx_userIdutilizzare idx_userActionMapping_userId.


3

Per favore assicurati prima di quello

  1. stai usando le tabelle InnoDB.
  2. il campo per FOREIGN KEY ha lo stesso tipo e lunghezza (!) del campo di origine.

Ho avuto lo stesso problema e l'ho risolto. Avevo INT non firmato per un campo e solo intero per l'altro campo.


2

Suggerimento utile, usa SHOW WARNINGS;dopo aver provato la tua CREATEquery e riceverai l'errore e l'avviso più dettagliato:

    ---------------------------------------------------------------------------------------------------------+
| Level   | Code | Message                                                                                                                                                                                                                                 |
+---------+------+--------------------------------------------------------------------------                          --------------------------------------------------------------------------------------------                          ---------------+
| Warning |  150 | Create table 'fakeDatabase/exampleTable' with foreign key constraint failed. There is no index in the referenced table where the referenced columns appear as the first columns.
|
| Error   | 1005 | Can't create table 'exampleTable' (errno:150)                                                                                                                                                                           |
+---------+------+--------------------------------------------------------------------------                          --------------------------------------------------------------------------------------------                          ---------------+

Quindi, in questo caso, è ora di ricreare la mia tabella!


1

Questo di solito accade quando si tenta di inserire il file di origine nel database esistente. Elimina prima tutte le tabelle (o il DB stesso). E poi il file sorgente con SET foreign_key_checks = 0;all'inizio e SET foreign_key_checks = 1;alla fine.


1

Ho trovato un altro motivo per cui questo non riesce ... nomi di tabelle con distinzione tra maiuscole e minuscole.

Per questa definizione di tabella

CREATE TABLE user (
  userId int PRIMARY KEY AUTO_INCREMENT,
  username varchar(30) NOT NULL
) ENGINE=InnoDB;

Questa definizione di tabella funziona

CREATE TABLE product (
  id int PRIMARY KEY AUTO_INCREMENT,
  userId int,
  FOREIGN KEY fkProductUser1(userId) REFERENCES **u**ser(userId)
) ENGINE=InnoDB;

mentre questo fallisce

CREATE TABLE product (
  id int PRIMARY KEY AUTO_INCREMENT,
  userId int,
  FOREIGN KEY fkProductUser1(userId) REFERENCES User(userId)
) ENGINE=InnoDB;

Il fatto che funzionasse su Windows e fallisse su Unix mi ci sono volute un paio d'ore per capirlo. Spero che aiuti qualcun altro.


1

MySQL Workbench 6.3 per Mac OS.

Problema: errno 150 sulla tabella X durante il tentativo di eseguire Forward Engineering su un diagramma DB, 20 su 21 sono riusciti, 1 non è riuscito. Se gli FK nella tabella X sono stati eliminati, l'errore è stato spostato in una tabella diversa che prima non aveva avuto esito negativo.

Modificato il motore di tutte le tabelle in myISAM e ha funzionato perfettamente.

inserisci qui la descrizione dell'immagine


0

Vale anche la pena controllare che non si stia operando accidentalmente sul database sbagliato. Questo errore si verificherà se la tabella esterna non esiste. Perché MySQL deve essere così criptico?


0

Assicurati che le chiavi esterne non siano elencate come uniche nel genitore. Ho avuto lo stesso problema e l'ho risolto demarcandolo come non unico.


0

Nel mio caso era dovuto al fatto che il campo che era un campo di chiave esterna aveva un nome troppo lungo, cioè. foreign key (some_other_table_with_long_name_id). Prova qc più breve. Il messaggio di errore è un po 'fuorviante in questo caso.

Inoltre, come menzionato in precedenza da @Jon, le definizioni dei campi devono essere le stesse (attenzione al unsignedsottotipo).


0

(Note a margine troppo grandi per un commento)

Non è necessario un AUTO_INCREMENTID in una tabella di mappatura; sbarazzarsi di esso.

Modificare il PRIMARY KEYin (role_id, role_group_id)(in entrambi gli ordini). Questo renderà gli accessi più veloci.

Poiché probabilmente vuoi mappare entrambe le direzioni, aggiungi anche una INDEXcon quelle due colonne nell'ordine opposto. (Non è necessario farlo UNIQUE.)

Altri suggerimenti: http://mysql.rjweb.org/doc.php/index_cookbook_mysql#speeding_up_wp_postmeta


0

Quando il vincolo di chiave esterna si basa sul varchartipo, quindi in aggiunta alla lista fornita dalmarv-el la colonna di destinazione deve avere un vincolo univoco.


0

eseguire sotto la riga prima di creare la tabella: SET FOREIGN_KEY_CHECKS = 0;

L'opzione FOREIGN_KEY_CHECKS specifica se controllare o meno i vincoli di chiave esterna per le tabelle InnoDB.

- Specifica di controllare i vincoli della chiave esterna (questa è l'impostazione predefinita)

SET FOREIGN_KEY_CHECKS = 1;

 

- Non controllare i vincoli della chiave esterna

SET FOREIGN_KEY_CHECKS = 0;

Quando utilizzarlo: la disattivazione temporanea dei vincoli referenziali (impostare FOREIGN_KEY_CHECKS su 0) è utile quando è necessario ricreare le tabelle e caricare i dati in qualsiasi ordine padre-figlio


-1

Ho riscontrato lo stesso problema, ma controllo che non avevo la tabella padre. Quindi modifico semplicemente la migrazione dei genitori davanti alla migrazione dei figli. Fallo e basta.


1
questo avrebbe dovuto essere un commento invece di una risposta
hannad rehman il
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.