MySQL ne peut pas ajouter de contrainte de clé étrangère

314

J'essaie donc d'ajouter des contraintes de clé étrangère à ma base de données en tant qu'exigence de projet et cela a fonctionné la première fois ou deux sur des tables différentes, mais j'ai deux tables sur lesquelles j'obtiens une erreur lorsque j'essaie d'ajouter les contraintes de clé étrangère. Le message d'erreur que je reçois est:

ERREUR 1215 (HY000): impossible d'ajouter une contrainte de clé étrangère

C'est le SQL que j'utilise pour créer les tables, les deux tables incriminées sont Patientet Appointment.

SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=1;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES';

CREATE SCHEMA IF NOT EXISTS `doctorsoffice` DEFAULT CHARACTER SET utf8 ;
USE `doctorsoffice` ;

-- -----------------------------------------------------
-- Table `doctorsoffice`.`doctor`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `doctorsoffice`.`doctor` ;

CREATE  TABLE IF NOT EXISTS `doctorsoffice`.`doctor` (
  `DoctorID` INT(11) NOT NULL AUTO_INCREMENT ,
  `FName` VARCHAR(20) NULL DEFAULT NULL ,
  `LName` VARCHAR(20) NULL DEFAULT NULL ,
  `Gender` VARCHAR(1) NULL DEFAULT NULL ,
  `Specialty` VARCHAR(40) NOT NULL DEFAULT 'General Practitioner' ,
  UNIQUE INDEX `DoctorID` (`DoctorID` ASC) ,
  PRIMARY KEY (`DoctorID`) )
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;


-- -----------------------------------------------------
-- Table `doctorsoffice`.`medicalhistory`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `doctorsoffice`.`medicalhistory` ;

CREATE  TABLE IF NOT EXISTS `doctorsoffice`.`medicalhistory` (
  `MedicalHistoryID` INT(11) NOT NULL AUTO_INCREMENT ,
  `Allergies` TEXT NULL DEFAULT NULL ,
  `Medications` TEXT NULL DEFAULT NULL ,
  `ExistingConditions` TEXT NULL DEFAULT NULL ,
  `Misc` TEXT NULL DEFAULT NULL ,
  UNIQUE INDEX `MedicalHistoryID` (`MedicalHistoryID` ASC) ,
  PRIMARY KEY (`MedicalHistoryID`) )
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;


-- -----------------------------------------------------
-- Table `doctorsoffice`.`Patient`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `doctorsoffice`.`Patient` ;

CREATE  TABLE IF NOT EXISTS `doctorsoffice`.`Patient` (
  `PatientID` INT unsigned NOT NULL AUTO_INCREMENT ,
  `FName` VARCHAR(30) NULL ,
  `LName` VARCHAR(45) NULL ,
  `Gender` CHAR NULL ,
  `DOB` DATE NULL ,
  `SSN` DOUBLE NULL ,
  `MedicalHistory` smallint(5) unsigned NOT NULL,
  `PrimaryPhysician` smallint(5) unsigned NOT NULL,
  PRIMARY KEY (`PatientID`) ,
  UNIQUE INDEX `PatientID_UNIQUE` (`PatientID` ASC) ,
  CONSTRAINT `FK_MedicalHistory`
    FOREIGN KEY (`MEdicalHistory` )
    REFERENCES `doctorsoffice`.`medicalhistory` (`MedicalHistoryID` )
    ON DELETE CASCADE
    ON UPDATE CASCADE,
  CONSTRAINT `FK_PrimaryPhysician`
    FOREIGN KEY (`PrimaryPhysician` )
    REFERENCES `doctorsoffice`.`doctor` (`DoctorID` )
    ON DELETE CASCADE
    ON UPDATE CASCADE)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `doctorsoffice`.`Appointment`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `doctorsoffice`.`Appointment` ;

CREATE  TABLE IF NOT EXISTS `doctorsoffice`.`Appointment` (
  `AppointmentID` smallint(5) unsigned NOT NULL AUTO_INCREMENT ,
  `Date` DATE NULL ,
  `Time` TIME NULL ,
  `Patient` smallint(5) unsigned NOT NULL,
  `Doctor` smallint(5) unsigned NOT NULL,
  PRIMARY KEY (`AppointmentID`) ,
  UNIQUE INDEX `AppointmentID_UNIQUE` (`AppointmentID` ASC) ,
  CONSTRAINT `FK_Patient`
    FOREIGN KEY (`Patient` )
    REFERENCES `doctorsoffice`.`Patient` (`PatientID` )
    ON DELETE CASCADE
    ON UPDATE CASCADE,
  CONSTRAINT `FK_Doctor`
    FOREIGN KEY (`Doctor` )
    REFERENCES `doctorsoffice`.`doctor` (`DoctorID` )
    ON DELETE CASCADE
    ON UPDATE CASCADE)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `doctorsoffice`.`InsuranceCompany`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `doctorsoffice`.`InsuranceCompany` ;

CREATE  TABLE IF NOT EXISTS `doctorsoffice`.`InsuranceCompany` (
  `InsuranceID` smallint(5) NOT NULL AUTO_INCREMENT ,
  `Name` VARCHAR(50) NULL ,
  `Phone` DOUBLE NULL ,
  PRIMARY KEY (`InsuranceID`) ,
  UNIQUE INDEX `InsuranceID_UNIQUE` (`InsuranceID` ASC) )
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `doctorsoffice`.`PatientInsurance`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `doctorsoffice`.`PatientInsurance` ;

CREATE  TABLE IF NOT EXISTS `doctorsoffice`.`PatientInsurance` (
  `PolicyHolder` smallint(5) NOT NULL ,
  `InsuranceCompany` smallint(5) NOT NULL ,
  `CoPay` INT NOT NULL DEFAULT 5 ,
  `PolicyNumber` smallint(5) NOT NULL AUTO_INCREMENT ,
  PRIMARY KEY (`PolicyNumber`) ,
  UNIQUE INDEX `PolicyNumber_UNIQUE` (`PolicyNumber` ASC) ,
  CONSTRAINT `FK_PolicyHolder`
    FOREIGN KEY (`PolicyHolder` )
    REFERENCES `doctorsoffice`.`Patient` (`PatientID` )
    ON DELETE CASCADE
    ON UPDATE CASCADE,
  CONSTRAINT `FK_InsuranceCompany`
    FOREIGN KEY (`InsuranceCompany` )
    REFERENCES `doctorsoffice`.`InsuranceCompany` (`InsuranceID` )
    ON DELETE CASCADE
    ON UPDATE CASCADE)
ENGINE = InnoDB;

USE `doctorsoffice` ;


SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
joshuaegclark
la source

Réponses:

779

Pour trouver l'erreur spécifique, exécutez ceci:

SHOW ENGINE INNODB STATUS;

Et regardez dans la LATEST FOREIGN KEY ERRORsection.

Le type de données de la colonne enfant doit correspondre exactement à la colonne parent. Par exemple, puisque medicalhistory.MedicalHistoryIDest un INT, Patient.MedicalHistorydoit également être un INT, pas un SMALLINT.

En outre, vous devez exécuter la requête set foreign_key_checks=0avant d'exécuter la DDL afin de pouvoir créer les tables dans un ordre arbitraire plutôt que d'avoir à créer toutes les tables parentes avant les tables enfants pertinentes.

Ike Walker
la source
3
Merci, l'incohérence du type de données et foreign_key_checks ont résolu le problème!
joshuaegclark
30
A été causé par un classement différent sur les tables pour moi, l'un était UTF-8 et l'autre était latin1.
ug_
6
Je devais également m'assurer que j'avais coché "non signé" car il s'agissait d'un INT non signé même si mes types et ma longueur correspondaient.
timbrown
1
Mes tables étaient automatiquement créées avec le moteur MyISAM! Merci Ike.
Captain Hypertext
3
Merci. J'essayais de set nullsupprimer, mais la colonne était not null.
Matt
143

J'avais défini un champ comme "Non signé" et un autre non. Une fois que j'ai défini les deux colonnes sur Non signé, cela a fonctionné.

Satsara Gunaratne
la source
lol même. MySQL pourrait utiliser une gestion des erreurs plus précise sur ce genre de choses.
dave
82
  • Le moteur doit être le même, par exemple InnoDB
  • Le type de données doit être identique et de même longueur. par exemple VARCHAR (20)
  • Le jeu de caractères des colonnes de classement doit être le même. par exemple utf8
    Watchout: Même si vos tables ont le même classement, les colonnes peuvent toujours en avoir un différent.
  • Unique - La clé étrangère doit faire référence au champ qui est unique (généralement la clé primaire) dans la table de référence.
am0wa
la source
1
Meilleure réponse jamais, Après avoir essayé presque tout, il s'est avéré que je dois ajouter explicitement uniqueà la colonne du tableau de référence même si c'est un Primary Key!!
Yahya
Oui, la meilleure réponse de tous les temps ... en particulier le premier point du sapin! Dans mon cas, j'ai fait une migration (réservé le 2.5.14 vers bookd 2.7.2), où le script de migration n'a pas changé le moteur de base de données, donc lors de la création de nouvelles tables, j'ai eu cette erreur.
Bernhard
Mieux me répondre aussi.
EngineerCoder
Ce serait encore plus génial avec des conseils sur la façon de vérifier / changer. Pour moi, c'était une différence de classement au niveau des colonnes (merci pour l'idée!) Et cela m'a donné le correctif: stackoverflow.com/questions/1294117/…
sjgp
18

Essayez d'utiliser le même type de clés primaires - int (11) - sur les clés étrangères - smallint (5) - également.

J'espère que ça aide!

Felypp Oliveira
la source
mysql> créer un index unique index_bar_id sur foos (bar_id); ... mysql> alter table foos add constraint index_bar_id clé étrangère (bar_id) références bars (id); sixarm.com/about/…
CookieCoder
11

Confirmez que le codage et le classement des caractères pour les deux tables sont les mêmes.

Dans mon propre cas, l'une des tables utilisait utf8et l'autre utilisait latin1.

J'ai eu un autre cas où l'encodage était le même mais le classement différent. L'un utf8_general_cil'autreutf8_unicode_ci

Vous pouvez exécuter cette commande pour définir l'encodage et le classement d'une table.

ALTER TABLE tablename CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;

J'espère que ça aidera quelqu'un.

Goke Obasa
la source
Nice one @Adegoke, great answer
Edwin Ikechukwu Okonkwo
7

Pour définir une CLÉ ÉTRANGÈRE dans le tableau B, vous devez définir une CLÉ dans le tableau A.

Dans le tableau A: INDEX id( id)

Et puis dans le tableau B,

CONSTRAINT `FK_id` FOREIGN KEY (`id`) REFERENCES `table-A` (`id`)
user3707075
la source
je ne sais pas exactement ce que vous dites, mais j'ai trouvé que ma syntaxe était incorrecte. Je faisais: modifier la table des avions ajouter une contrainte fk_somehting_unique clé étrangère (opérateur_id) référence l'organisation, mais j'aurais dû faire: modifier la table des avions ajouter la contrainte fk_somehting_unique clé étrangère (opérateur_id) référence l'organisation (id) ;
Michael Coxon
7

J'ai eu le même problème et la solution était très simple. Solution: les clés étrangères déclarées dans la table ne doivent pas être définies comme non nulles.

référence: Si vous spécifiez une action SET NULL, assurez-vous que vous n'avez pas déclaré les colonnes de la table enfant NOT NULL. ( ref )

Singh
la source
4

Vérifiez les règles suivantes:

  • Vérifie d'abord si les noms sont donnés correctement pour les noms de table

  • Le deuxième bon type de données donne à la clé étrangère?

Bhaskar Bhatt
la source
4

Veuillez vous assurer que les deux tableaux sont au format InnoDB. Même si l'un est au format MyISAM, la contrainte de clé étrangère ne fonctionnera pas.

En outre, une autre chose est que les deux champs doivent être du même type. Si l'un est INT, l'autre doit également être INT. Si l'un est VARCHAR, l'autre doit également être VARCHAR, etc.

Vijay Srinivas
la source
3

J'ai fait face au problème et j'ai pu le résoudre en m'assurant que les types de données correspondaient exactement.

J'utilisais SequelPro pour ajouter la contrainte et cela rendait la clé primaire non signée par défaut.

Apprenant
la source
2

Vérifiez la signature sur vos deux colonnes de table. Si la colonne de la table de référence est SIGNÉE, la colonne de la table référencée doit également être SIGNÉE.

katwekibs
la source
1

REMARQUE: Les tableaux suivants ont été extraits d'un site lorsque je faisais de la R&D sur la base de données. La convention de dénomination n'est donc pas appropriée.

Pour moi, le problème était que ma table parent avait un jeu de caractères différent de celui de celui que je créais.

Table des parents (PRODUITS)

products | CREATE TABLE `products` (
  `productCode` varchar(15) NOT NULL,
  `productName` varchar(70) NOT NULL,
  `productLine` varchar(50) NOT NULL,
  `productScale` varchar(10) NOT NULL,
  `productVendor` varchar(50) NOT NULL,
  `productDescription` text NOT NULL,
  `quantityInStock` smallint(6) NOT NULL,
  `buyPrice` decimal(10,2) NOT NULL,
  `msrp` decimal(10,2) NOT NULL,
  PRIMARY KEY (`productCode`),
  KEY `productLine` (`productLine`),
  CONSTRAINT `products_ibfk_1` FOREIGN KEY (`productLine`) REFERENCES `productlines` (`productLine`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

Table enfant ayant rencontré un problème (PRICE_LOGS)

price_logs | CREATE TABLE `price_logs` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `productCode` varchar(15) DEFAULT NULL,
  `old_price` decimal(20,2) NOT NULL,
  `new_price` decimal(20,2) NOT NULL,
  `added_on` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `productCode` (`productCode`),
  CONSTRAINT `price_logs_ibfk_1` FOREIGN KEY (`productCode`) REFERENCES `products` (`productCode`) ON DELETE CASCADE ON UPDATE CASCADE
);

MODIFIÉ À

price_logs | CREATE TABLE `price_logs` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `productCode` varchar(15) DEFAULT NULL,
  `old_price` decimal(20,2) NOT NULL,
  `new_price` decimal(20,2) NOT NULL,
  `added_on` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `productCode` (`productCode`),
  CONSTRAINT `price_logs_ibfk_1` FOREIGN KEY (`productCode`) REFERENCES `products` (`productCode`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1 
Channaveer Hakari
la source
1

Mon problème était que j'essayais de créer la table des relations avant les autres tables!

Ahmad Mobaraki
la source
SET foreign_key_checks = 0;
LeeGee
0

J'ai eu une erreur similaire en créant une clé étrangère dans une table plusieurs à plusieurs où la clé primaire se composait de 2 clés étrangères et d'une autre colonne normale. J'ai résolu le problème en corrigeant le nom de la table référencée, c'est-à-dire la société, comme indiqué dans le code corrigé ci-dessous:

create table company_life_cycle__history -- (M-M)
(
company_life_cycle_id tinyint unsigned not null,
Foreign Key (company_life_cycle_id) references company_life_cycle(id) ON DELETE    CASCADE ON UPDATE CASCADE,
company_id MEDIUMINT unsigned not null,
Foreign Key (company_id) references company(id) ON DELETE CASCADE ON UPDATE CASCADE,
activity_on date NOT NULL,
PRIMARY KEY pk_company_life_cycle_history (company_life_cycle_id, company_id,activity_on),
created_on datetime DEFAULT NULL,
updated_on datetime DEFAULT NULL,
created_by varchar(50) DEFAULT NULL,
updated_by varchar(50) DEFAULT NULL
);
iltaf khalid
la source
0

J'ai eu une erreur similaire avec deux clés étrangères pour des tables différentes mais avec les mêmes noms de clés! J'ai renommé des clés et l'erreur a disparu)

Олег Всильдеревьев
la source
0

J'ai eu une erreur similaire, mais dans mon cas, il me manquait de déclarer le pk comme auto_increment.

Juste au cas où cela pourrait être utile à n'importe qui

Luiz Rolim
la source
0

J'ai eu la même erreur. La cause dans mon cas était:

  1. J'ai créé une sauvegarde d'une base de données via phpmyadmin en copiant toute la base de données.
  2. J'ai créé un nouveau db avec le même nom que l'ancien db l'avait sélectionné.
  3. J'ai commencé un script SQL pour créer des tables et des données mises à jour.
  4. J'ai eu l'erreur. Aussi quand j'ai désactivé foreign_key_checks. Même si la base de données était complètement vide.

La cause était: depuis que j'ai utilisé phpmyadmin pour créer des clés étrangères dans la base de données renommée - les clés étrangères ont été créées avec un préfixe de nom de base de données mais le préfixe de nom de base de données n'a pas été mis à jour. Il y avait donc toujours des références dans la base de données de sauvegarde pointant vers la base de données nouvellement créée.

lsblsb
la source
0

Ma solution est peut-être un peu embarrassante et raconte pourquoi vous devriez parfois regarder ce que vous avez devant vous au lieu de ces messages :)

J'avais déjà dirigé un ingénieur avancé, ce qui a échoué, ce qui signifie que ma base de données avait déjà quelques tables, puis je me suis assis à essayer de corriger les échecs de contraintes de clés étrangères en essayant de s'assurer que tout était parfait, mais il s'est heurté à la tables précédemment créées, il ne devait donc pas prévaloir.

DenLilleMand
la source
0

Une autre cause de cette erreur est lorsque vos tables ou colonnes contiennent des mots clés réservés :

Parfois, on les oublie.

EssGee
la source
0

Dans mon cas, il y a eu une erreur de syntaxe qui n'a pas été explicitement notifiée par la console MySQL lors de l'exécution de la requête. Cependant, SHOW ENGINE INNODB STATUSla LATEST FOREIGN KEY ERRORsection du commandement a rapporté,

  Syntax error close to:

  REFERENCES`role`(`id`) ON DELETE CASCADE) ENGINE = InnoDB DEFAULT CHARSET = utf8

J'ai dû laisser un espace entre REFERENCESet rolepour le faire fonctionner.

vicke4
la source
0

Pour moi, c'était - vous ne pouvez pas omettre de préfixer la table de base de données actuelle si vous créez un FK pour une base de données non actuelle référençant la base de données actuelle:

USE currrent_db;
ALTER TABLE other_db.tasks ADD CONSTRAINT tasks_fk FOREIGN KEY (user_id) REFERENCES currrent_db.users (id);

Si j'omets "currrent_db". pour la table des utilisateurs, j'obtiens l'erreur FK. Intéressant que SHOW ENGINE INNODB STATUS; ne montre rien dans ce cas.

Bearoff
la source
-1

J'ai eu ce même problème, puis j'ai corrigé le nom du moteur en tant qu'Innodb dans les tables parent et enfant et corrigé le nom du champ de référence FOREIGN KEY ( c_id) REFERENCES x9o_parent_table( c_id)
puis cela fonctionne correctement et les tables sont installées correctement. Ce sera une utilisation complète pour quelqu'un.

subramanian
la source