MySQL no puede agregar restricción de clave externa

314

Así que estoy tratando de agregar restricciones de clave externa a mi base de datos como un requisito del proyecto y funcionó la primera o dos veces en tablas diferentes, pero tengo dos tablas en las que obtengo un error al intentar agregar las restricciones de clave externa. El mensaje de error que recibo es:

ERROR 1215 (HY000): no se puede agregar la restricción de clave externa

Este es el SQL que estoy usando para crear las tablas, las dos tablas ofensivas son Patienty 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
fuente

Respuestas:

779

Para encontrar el error específico, ejecute esto:

SHOW ENGINE INNODB STATUS;

Y mira en la LATEST FOREIGN KEY ERRORsección.

El tipo de datos para la columna secundaria debe coincidir exactamente con la columna primaria. Por ejemplo, dado que medicalhistory.MedicalHistoryIDes un INT, Patient.MedicalHistorytambién debe ser un INT, no un SMALLINT.

Además, debe ejecutar la consulta set foreign_key_checks=0antes de ejecutar el DDL para poder crear las tablas en un orden arbitrario en lugar de tener que crear todas las tablas principales antes de las tablas secundarias relevantes.

Ike Walker
fuente
3
Gracias, ¡tanto la inconsistencia de tipo de datos como la de Foreign_key_checks solucionó el problema!
joshuaegclark
30
Fue causado por una clasificación diferente en las tablas para mí, uno era UTF-8 y el otro era latin1.
ug_
66
También tuve que asegurarme de que había marcado "sin firmar" ya que este era un INT sin firmar a pesar de que mis tipos y longitud coincidían.
timbrown
1
¡Mis tablas se crearon automáticamente con el motor MyISAM! Gracias Ike
Capitán Hipertexto
3
Gracias. Estaba tratando de set nulleliminar, pero la columna estaba not null.
Matt
143

Había establecido un campo como "Unsigned" y otro no. Una vez que configuré ambas columnas como Unsigned, funcionó.

Satsara Gunaratne
fuente
jajaja lo mismo. MySQL podría usar un manejo de errores más preciso en este tipo de cosas.
Dave
82
  • El motor debe ser el mismo, por ejemplo, InnoDB
  • El tipo de datos debe ser el mismo y con la misma longitud. por ejemplo, VARCHAR (20)
  • El conjunto de caracteres Collation Columns debe ser el mismo. por ejemplo, utf8
    Watchout: incluso si sus tablas tienen la misma clasificación, las columnas podrían tener una diferente.
  • Único : la clave externa debe referirse al campo que es único (generalmente clave principal) en la tabla de referencia.
am0wa
fuente
1
La mejor respuesta, después de probar casi todo, resultó que tengo que agregar explícitamente uniquea la columna de la tabla de referencia a pesar de que es un Primary Key!!
Yahya
Sí, la mejor respuesta ... ¡en particular el primer punto! En mi caso, realicé una migración (reservé 2.5.14 a bookd 2.7.2), donde el script de migración no cambió el motor de la base de datos, por lo que al crear nuevas tablas recibí este error.
Bernhard
Mejor contéstame también.
EngineerCoder
Sería aún más increíble con consejos sobre cómo verificar / cambiar. Para mí fue una diferencia de colación a nivel de columna (¡gracias por la idea!) Y esto me dio la solución: stackoverflow.com/questions/1294117/…
sjgp
18

Intente utilizar el mismo tipo de sus claves principales - int (11) - en las claves externas - smallint (5) - también.

¡Espero eso ayude!

Felypp Oliveira
fuente
mysql> crea un índice único index_bar_id en foos (bar_id); ... mysql> alter table foos agregar restricción index_bar_id clave externa (bar_id) referencias barras (id); sixarm.com/about/…
CookieCoder
11

Confirme que la codificación y clasificación de caracteres para las dos tablas es la misma.

En mi propio caso, una de las tablas estaba usando utf8y la otra estaba usando latin1.

Tuve otro caso donde la codificación era la misma pero la intercalación diferente. Uno utf8_general_ciel otroutf8_unicode_ci

Puede ejecutar este comando para establecer la codificación y la clasificación para una tabla.

ALTER TABLE tablename CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;

Espero que esto ayude a alguien.

Goke Obasa
fuente
Nice one @Adegoke, gran respuesta
Edwin Ikechukwu Okonkwo
7

Para establecer una CLAVE EXTRANJERA en la Tabla B, debe establecer una CLAVE en la tabla A.

En la tabla A: ÍNDICE id( id)

Y luego en la tabla B,

CONSTRAINT `FK_id` FOREIGN KEY (`id`) REFERENCES `table-A` (`id`)
usuario3707075
fuente
No estoy seguro exactamente de lo que está diciendo, pero descubrí que mi sintaxis era incorrecta. Estaba haciendo: alterar tabla de aeronaves agregar restricción fk_somehting_unique organización de referencias de clave externa (operator_id), pero debería haber hecho: alterar tabla de aeronaves agregar restricción fk_somehting_unique organización de referencias de clave externa (operator_id) (id) ;
Michael Coxon
7

Tuve el mismo problema y la solución fue muy simple. Solución: las claves externas declaradas en la tabla no deben establecerse como no nulas.

referencia: si especifica una acción SET NULL, asegúrese de no haber declarado las columnas en la tabla secundaria como NOT NULL. ( ref )

Singh
fuente
4

Verifique las siguientes reglas:

  • Primero verifica si los nombres se dan correctamente para los nombres de tabla

  • Segundo tipo de datos correcto dar a la clave externa?

Bhaskar Bhatt
fuente
4

Asegúrese de que ambas tablas estén en formato InnoDB. Incluso si uno está en formato MyISAM, entonces, la restricción de clave externa no funcionará.

Además, otra cosa es que ambos campos deben ser del mismo tipo. Si uno es INT, entonces el otro también debería ser INT. Si uno es VARCHAR, el otro también debe ser VARCHAR, etc.

Vijay Srinivas
fuente
3

Enfrenté el problema y pude resolverlo asegurándome de que los tipos de datos coincidieran exactamente.

Estaba usando SequelPro para agregar la restricción y estaba haciendo que la clave primaria no estuviera firmada por defecto.

Aprendiz
fuente
2

Verifique la firma en ambas columnas de su tabla. Si la columna de la tabla de referencia está FIRMADA, la columna de la tabla de referencia también debe FIRMARSE.

Katwekibs
fuente
1

NOTA: Las siguientes tablas fueron tomadas de algún sitio cuando estaba haciendo I + D en la base de datos. Entonces la convención de nomenclatura no es apropiada.

Para mí, el problema era que mi tabla principal tenía un conjunto de caracteres diferente al del que estaba creando.

Tabla principal (PRODUCTOS)

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

Tabla secundaria que tuvo un problema (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
);

MODIFICADO A

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
fuente
1

¡Mi problema era que estaba tratando de crear la tabla de relaciones antes que otras tablas!

Ahmad Mobaraki
fuente
SET foreign_key_checks = 0;
LeeGee
0

Tuve un error similar al crear una clave foránea en una tabla Many to Many donde la clave primaria constaba de 2 claves foráneas y otra columna normal. Solucioné el problema corrigiendo el nombre de la tabla referenciada, es decir, la compañía, como se muestra en el código corregido a continuación:

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
fuente
0

¡Tuve un error similar con dos claves foráneas para tablas diferentes pero con los mismos nombres de clave! Cambié el nombre de las teclas y el error desapareció)

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

Tuve un error similar, pero en mi caso no pude declarar el pk como auto_increment.

Por si acaso podría ser útil para cualquiera

Luiz Rolim
fuente
0

Tengo el mismo error. La causa en mi caso fue:

  1. Creé una copia de seguridad de una base de datos a través de phpmyadmin copiando toda la base de datos.
  2. Creé un nuevo db con el mismo nombre que el viejo db lo había seleccionado.
  3. Comencé un script SQL para crear tablas y datos actualizados.
  4. Recibí el error También cuando deshabilité foreign_key_checks. Aunque la base de datos estaba completamente vacía.

La causa fue: dado que usé phpmyadmin para crear algunas claves externas en la base de datos renombrada, las claves externas se crearon con un prefijo de nombre de base de datos pero el prefijo de nombre de base de datos no se actualizó. Así que todavía había referencias en la copia de seguridad-db que apuntaban a la nueva base de datos creada.

lsblsb
fuente
0

Mi solución es quizás un poco vergonzosa y cuenta la historia de por qué a veces debes mirar lo que tienes frente a ti en lugar de estas publicaciones :)

Había dirigido a un ingeniero de avanzada antes, lo que falló, por lo que eso significaba que mi base de datos ya tenía algunas tablas, luego he estado sentado tratando de corregir fallas de restricciones de claves externas tratando de asegurarme de que todo fuera perfecto, pero funcionó contra el tablas creadas previamente, por lo que no prevalecerá.

DenLilleMand
fuente
0

Una causa adicional de este error es cuando sus tablas o columnas contienen palabras clave reservadas :

A veces uno olvida estos.

EssGee
fuente
0

En mi caso, hubo un error de sintaxis que la consola MySQL no notificó explícitamente al ejecutar la consulta. Sin embargo, SHOW ENGINE INNODB STATUSla LATEST FOREIGN KEY ERRORsección del comando informó:

  Syntax error close to:

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

Tuve que dejar un espacio en blanco entre REFERENCESy rolehacer que funcione.

vicke4
fuente
0

Para mí lo fue: no puede omitir el prefijo de la tabla de base de datos actual si crea un FK para una base de datos no actual que hace referencia a la base de datos actual:

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

Si omito "currrent_db". para la tabla de usuarios, me sale el error FK. Interesante que SHOW ENGINE INNODB STATUS; No muestra nada en este caso.

bateo
fuente
-1

Tuve este mismo problema, luego corregí el nombre del motor como Innodb en las tablas padre e hijo y corrigí el nombre del campo de referencia c_idREFERENCIAS DE LLAVE EXTRANJERA x9o_parent_table( c_id),
luego funciona bien y las tablas se instalan correctamente. Esto se utilizará por completo para alguien.

subramanian
fuente