MySQL Création de tables avec des clés étrangères donnant errno: 150

98

J'essaie de créer une table dans MySQL avec deux clés étrangères, qui référencent les clés primaires dans 2 autres tables, mais j'obtiens une erreur errno: 150 et cela ne créera pas la table.

Voici le SQL pour les 3 tables:

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;

Toute aide serait grandement appréciée.

Bill Karwin
la source
1
Pourriez-vous publier la sortie d'erreur et nous dire quelle commande (des trois) est à l'origine de l'erreur?
dave le
4
Qu'y a-t-il avec les rétrocessions auto_increment? Ce n'est pas valable. Auto_increment est un mot-clé, pas un identifiant.
Bill Karwin

Réponses:

238

J'ai eu le même problème avec ALTER TABLE ADD FOREIGN KEY.

Après une heure, j'ai trouvé que ces conditions doivent être remplies pour ne pas obtenir l'erreur 150:

  1. La table Parent doit exister avant que vous ne définissiez une clé étrangère pour la référencer. Vous devez définir les tables dans le bon ordre: la table parent d'abord, puis la table enfant. Si les deux tables se réfèrent l'une à l'autre, vous devez créer une table sans contraintes FK, puis créer la deuxième table, puis ajouter la contrainte FK à la première table avecALTER TABLE .

  2. Les deux tables doivent toutes deux prendre en charge les contraintes de clé étrangère, c'est-à-dire ENGINE=InnoDB . Les autres moteurs de stockage ignorent silencieusement les définitions de clé étrangère, ils ne renvoient donc aucune erreur ou avertissement, mais la contrainte FK n'est pas enregistrée.

  3. Les colonnes référencées dans la table Parent doivent être les colonnes les plus à gauche d'une clé. Idéal si la clé du parent est PRIMARY KEYouUNIQUE KEY .

  4. La définition FK doit référencer la ou les colonnes PK dans le même ordre que la définition PK. Par exemple, si le FK REFERENCES Parent(a,b,c)alors le PK du parent ne doit pas être défini sur les colonnes dans l'ordre(a,c,b) .

  5. La ou les colonnes PK de la table Parent doivent être du même type de données que la ou les colonnes FK de la table Child. Par exemple, si une colonne PK de la table Parent est UNSIGNED, veillez à définir UNSIGNEDpour la colonne correspondante dans le champ Table enfant.

    Exception: la longueur des chaînes peut être différente. Par exemple, VARCHAR(10)peut référencer VARCHAR(20)ou vice versa.

  6. Toute colonne FK de type chaîne doit avoir le même jeu de caractères et le même classement que la ou les colonnes PK correspondantes.

  7. S'il existe déjà des données dans la table enfant, chaque valeur de la ou des colonnes FK doit correspondre à une valeur de la ou des colonnes PK de la table parent. Vérifiez cela avec une requête comme:

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

    Cela doit renvoyer zéro (0) valeurs sans correspondance. De toute évidence, cette requête est un exemple générique; vous devez remplacer vos noms de table et de colonne.

  8. Ni la table Parent ni la table Child ne peuvent être une TEMPORARYtable.

  9. Ni la table Parent ni la table Child ne peuvent être une PARTITIONEDtable.

  10. Si vous déclarez un FK avec l' ON DELETE SET NULLoption, la ou les colonnes FK doivent être Nullable.

  11. Si vous déclarez un nom de contrainte pour une clé étrangère, le nom de contrainte doit être unique dans tout le schéma, et pas uniquement dans la table dans laquelle la contrainte est définie. Deux tables peuvent ne pas avoir leur propre contrainte avec le même nom.

  12. S'il y a d'autres FK dans d'autres tables pointant sur le même champ pour lequel vous essayez de créer le nouveau FK, et qu'ils sont mal formés (c'est-à-dire un classement différent), ils devront d'abord être cohérents. Cela peut être le résultat de changements passés où a SET FOREIGN_KEY_CHECKS = 0;été utilisé avec une relation incohérente définie par erreur. Voir la réponse de @ andrewdotn ci-dessous pour savoir comment identifier ces problèmes de FK.

J'espère que cela t'aides.

merveille
la source
4
une dernière chose à ajouter: si le PK de la table parent est plus d'un champ, l'ordre des champs dans le FK doit être le même que l'ordre dans le PK
Kip
26
Cela inclut des choses comme int(11) unsigned NOT NULLvs int(11) NOT NULL.
Glen Solsberry
4
ALTER TABLE nom_table ENGINE = InnoDB;
TolMera
12
Si la table est définie ENGINE = MyISAM, elle ne génère pas le numéro d'erreur 150 car elle ignore les déclarations de clé étrangère. C'est comme dire que la meilleure façon d'éviter des problèmes avec le moteur de votre automobile est de conduire un bateau. :-)
Bill Karwin
2
De plus, si la ON DELETErègle de votre CONSTRAINT est, SET NULLassurez-vous que la clé étrangère peut être NULL! J'ai passé 30 minutes à lire cette réponse encore et encore, en m'assurant que mes tables remplissaient les conditions mais en obtenant toujours l'erreur 150. Ensuite, j'ai remarqué que mon FK était un champ NOT NULL, ce qui signifie que la règle était impossible à appliquer.
Martin Joiner
62

Le message générique "errno 150" de MySQL " signifie qu'une contrainte de clé étrangère n'a pas été correctement formée ." Comme vous le savez probablement déjà si vous lisez cette page, le message d'erreur générique «errno: 150» est vraiment inutile. Toutefois:

Vous pouvez obtenir le message d'erreur réel en exécutant SHOW ENGINE INNODB STATUS;et en recherchant LATEST FOREIGN KEY ERRORdans la sortie.

Par exemple, cette tentative de créer une contrainte de clé étrangère:

CREATE TABLE t1
(id INTEGER);

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

échoue avec l'erreur Can't create table 'test.t2' (errno: 150). Cela ne dit rien d'utile à personne si ce n'est que c'est un problème de clé étrangère. Mais courez SHOW ENGINE INNODB STATUS;et il dira:

------------------------
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.

Il dit que le problème est qu'il ne trouve pas d'index. SHOW INDEX FROM t1montre qu'il n'y a pas du tout d'index pour table t1. Corrigez cela, par exemple, en définissant une clé primaire sur t1, et la contrainte de clé étrangère sera créée avec succès.

andrewdotn
la source
4
SHOW ENGINE INNODB STATUSm'a aidé à identifier immédiatement un problème que j'avais essayé de diagnostiquer pendant près d'une heure. Merci.
jatrim
Dans mon cas, cela indiquait qu'une table totalement différente sur laquelle FK avait le même champ que j'essayais de pointer était incohérente et ne sauverait donc pas la nouvelle ... en supposant que cela provenait d'une utilisation SET FOREIGN_KEY_CHECKS = 0;lors d'une importation / modification malformée à un moment ou un autre. Grande aide, merci.
oucil le
25

Assurez-vous que les propriétés des deux champs que vous essayez de lier à une contrainte sont exactement les mêmes.

Souvent, la propriété «non signé» sur une colonne ID vous surprendra.

ALTER TABLE `dbname`.`tablename` CHANGE `fieldname` `fieldname` int(10) UNSIGNED NULL;
Jon Winstanley
la source
D'après mon expérience, il vaut la peine d'utiliser SHOW CREATE TABLE de MySQL sur votre table principale pour vérifier exactement quels indicateurs sont définis par rapport à votre colonne d'index principale, puis copiez-les dans votre colonne de clé étrangère. Il peut y avoir des choses là-bas, comme «non signé», qui ne sont pas évidentes.
Ambulare le
10

Quel est l'état actuel de votre base de données lorsque vous exécutez ce script? Est-ce complètement vide? Votre SQL fonctionne bien pour moi lors de la création d'une base de données à partir de zéro, mais errno 150 a généralement à voir avec la suppression et la recréation de tables faisant partie d'une clé étrangère. J'ai l'impression que vous ne travaillez pas avec une base de données 100% fraîche et nouvelle.

Si vous faites une erreur lors de la "source" de votre fichier SQL, vous devriez pouvoir exécuter la commande "SHOW ENGINE INNODB STATUS" à partir de l'invite MySQL immédiatement après la commande "source" pour voir des informations d'erreur plus détaillées.

Vous pouvez également consulter la saisie manuelle:

Si vous recréez une table qui a été supprimée, elle doit avoir une définition conforme aux contraintes de clé étrangère qui la référencent. Il doit avoir les bons noms et types de colonne, et il doit avoir des index sur les clés référencées, comme indiqué précédemment. Si ceux-ci ne sont pas satisfaits, MySQL renvoie le numéro d'erreur 1005 et fait référence à l'erreur 150 dans le message d'erreur. Si MySQL signale un numéro d'erreur 1005 à partir d'une instruction CREATE TABLE et que le message d'erreur fait référence à l'erreur 150, la création de la table a échoué car une contrainte de clé étrangère n'a pas été correctement formée.

- Manuel de référence MySQL 5.1 .

Brent écrit le code
la source
5

Pour les personnes qui consultent ce fil avec le même problème:

Il y a de nombreuses raisons pour obtenir des erreurs comme celle-ci. Pour une liste assez complète des causes et des solutions des erreurs de clé étrangère dans MySQL (y compris celles discutées ici), consultez ce lien:

Erreurs de clé étrangère MySQL et Errno 150

Juacala
la source
4

Pour les autres qui trouvent cette entrée SO via Google: assurez-vous que vous n'essayez pas d'effectuer une action SET NULL sur une colonne de clé étrangère (à être) définie comme "NOT NULL". Cela a causé une grande frustration jusqu'à ce que je me souvienne de faire un CHECK ENGINE INNODB STATUS.

Éric L.
la source
3

Ce n'est certainement pas le cas, mais j'ai trouvé cette erreur assez courante et peu évidente. La cible d'un FOREIGN KEYpourrait ne pas l'être PRIMARY KEY. La réponse qui me devient utile est:

Une FOREIGN KEY doit toujours être pointée vers un champ vrai PRIMARY KEY d'une autre table.

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));
I159
la source
3

Comme indiqué par @andrewdotn, le meilleur moyen est de voir l'erreur détaillée ( SHOW ENGINE INNODB STATUS;) au lieu d'un simple code d'erreur.

L'une des raisons pourrait être qu'un index existe déjà avec le même nom, peut-être dans une autre table. En pratique, je recommande de préfixer le nom de la table avant le nom de l'index pour éviter de telles collisions. par exemple au lieu d' idx_userIdutiliser idx_userActionMapping_userId.

Beaucoup plus
la source
3

Veuillez d'abord vous assurer que

  1. vous utilisez des tables InnoDB.
  2. Le champ FOREIGN KEY a le même type et la même longueur (!) que le champ source.

J'ai eu le même problème et je l'ai résolu. J'avais INT non signé pour un champ et juste un entier pour un autre champ.

Juljan
la source
2

Conseil utile, utilisez SHOW WARNINGS;après avoir essayé votre CREATErequête et vous recevrez l'erreur ainsi que l'avertissement plus détaillé:

    ---------------------------------------------------------------------------------------------------------+
| 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)                                                                                                                                                                           |
+---------+------+--------------------------------------------------------------------------                          --------------------------------------------------------------------------------------------                          ---------------+

Alors dans ce cas, il est temps de recréer ma table!

Sturrockad
la source
1

Cela se produit généralement lorsque vous essayez de créer un fichier dans une base de données existante. Supprimez d'abord toutes les tables (ou la base de données elle-même). Et puis le fichier source avec SET foreign_key_checks = 0;au début et SET foreign_key_checks = 1;à la fin.

wholenewstrain
la source
1

J'ai trouvé une autre raison pour laquelle cela échoue ... les noms de table sensibles à la casse.

Pour cette définition de table

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

Cette définition de table fonctionne

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

alors que celui-ci échoue

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

Le fait qu'il a fonctionné sous Windows et a échoué sous Unix m'a pris quelques heures à comprendre. J'espère que cela aide quelqu'un d'autre.

Tim
la source
1

MySQL Workbench 6.3 pour Mac OS.

Problème: erreur n ° 150 sur la table X lors de la tentative de Forward Engineering sur un diagramme DB, 20 sur 21 ont réussi, 1 échoué. Si les FK de la table X ont été supprimés, l'erreur a été déplacée vers une table différente qui n'échouait pas auparavant.

J'ai changé le moteur de toutes les tables en myISAM et cela a très bien fonctionné.

entrez la description de l'image ici

Eduardo Chongkan
la source
0

Vérifiez également que vous n'opérez pas accidentellement sur la mauvaise base de données. Cette erreur se produit si la table étrangère n'existe pas. Pourquoi MySQL doit-il être si cryptique?

SystèmeParadox
la source
0

Assurez-vous que les clés étrangères ne sont pas répertoriées comme uniques dans le parent. J'ai eu ce même problème et je l'ai résolu en le délimitant comme n'étant pas unique.

Raza
la source
0

Dans mon cas, cela était dû au fait que le champ qui était un champ de clé étrangère avait un nom trop long, c'est-à-dire. foreign key (some_other_table_with_long_name_id). Essayez qc plus court. Le message d'erreur est un peu trompeur dans ce cas.

De plus, comme @Jon l'a mentionné précédemment, les définitions de champ doivent être les mêmes (attention au unsignedsous-type).

Kangur
la source
0

(Notes latérales trop grosses pour un commentaire)

Il n'y a pas besoin d'un AUTO_INCREMENTidentifiant dans une table de mappage; débarrassez-vous-en.

Remplacez le PRIMARY KEYpar (role_id, role_group_id)(dans l'un ou l'autre ordre). Cela rendra les accès plus rapides.

Puisque vous souhaitez probablement mapper les deux directions, ajoutez également un INDEXavec ces deux colonnes dans l'ordre opposé. (Il n'est pas nécessaire de le faire UNIQUE.)

Plus de conseils: http://mysql.rjweb.org/doc.php/index_cookbook_mysql#speeding_up_wp_postmeta

Rick James
la source
0

Lorsque la contrainte de clé étrangère est basée sur le varchartype, en plus de la liste fournie parmarv-el la colonne cible, elle doit avoir une contrainte unique.

Ralph
la source
0

exécutez la ligne ci-dessous avant de créer la table: SET FOREIGN_KEY_CHECKS = 0;

L'option FOREIGN_KEY_CHECKS spécifie s'il faut vérifier ou non les contraintes de clé étrangère pour les tables InnoDB.

- Spécifiez pour vérifier les contraintes de clé étrangère (c'est la valeur par défaut)

SET FOREIGN_KEY_CHECKS = 1;

 

- Ne pas vérifier les contraintes de clé étrangère

SET FOREIGN_KEY_CHECKS = 0;

Quand l'utiliser: La désactivation temporaire des contraintes référentielles (définissez FOREIGN_KEY_CHECKS sur 0) est utile lorsque vous devez recréer les tables et charger des données dans n'importe quel ordre parent-enfant

user11949964
la source
-1

J'ai rencontré le même problème, mais je vérifie que je n'avais pas la table parent. Je viens donc de modifier la migration parentale devant la migration enfant. Simplement fais-le.

韩向飞
la source
1
cela aurait dû être un commentaire au lieu d'une réponse
hannad rehman