O MySQL não pode adicionar restrições de chave estrangeira

314

Portanto, estou tentando adicionar restrições de chave estrangeira ao meu banco de dados como um requisito do projeto e funcionou pela primeira vez ou duas em tabelas diferentes, mas tenho duas tabelas nas quais recebo um erro ao tentar adicionar as restrições de chave estrangeira. A mensagem de erro que recebo é:

ERRO 1215 (HY000): Não é possível adicionar restrição de chave estrangeira

Este é o SQL que estou usando para criar as tabelas, as duas tabelas incorretas são Patiente 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
fonte

Respostas:

779

Para encontrar o erro específico, execute o seguinte:

SHOW ENGINE INNODB STATUS;

E olhe na LATEST FOREIGN KEY ERRORseção.

O tipo de dados para a coluna filho deve corresponder exatamente à coluna pai. Por exemplo, uma vez que medicalhistory.MedicalHistoryIDé um INT, Patient.MedicalHistorytambém precisa ser um INT, não um SMALLINT.

Além disso, você deve executar a consulta set foreign_key_checks=0antes de executar a DDL para poder criar as tabelas em uma ordem arbitrária, em vez de precisar criar todas as tabelas pai antes das tabelas filho relevantes.

Ike Walker
fonte
3
Obrigado, tanto o tipo de dados inconsistency como Foreign_key_checks corrigiram o problema!
joshuaegclark
30
Foi causado por um agrupamento diferente nas tabelas para mim, um era UTF-8 e o outro era latin1.
ug_
6
Também tive que me certificar de que eu havia marcado "não assinado", pois esse era um INT não assinado, mesmo que meus tipos e comprimento correspondessem.
timbrown
1
Minhas tabelas foram criadas automaticamente com o mecanismo MyISAM! Obrigado Ike.
Captain Hypertext
3
Obrigado. Eu estava tentando set nullexcluir, mas a coluna era not null.
Matt
143

Eu havia definido um campo como "Não assinado" e outro não. Depois de definir as duas colunas como Não assinado, funcionou.

Satsara Gunaratne
fonte
lol mesmo. O MySQL poderia usar um tratamento de erro mais preciso nesse tipo de coisa.
dave
82
  • O motor deve ser o mesmo, por exemplo, InnoDB
  • O tipo de dados deve ser o mesmo e com o mesmo comprimento. por exemplo, VARCHAR (20)
  • O conjunto de colunas de agrupamento deve ser o mesmo. Por exemplo, utf8
    Watchout: mesmo que suas tabelas tenham o mesmo agrupamento, as colunas ainda podem ter um diferente.
  • Exclusivo - Chave estrangeira deve se referir ao campo exclusivo (geralmente chave primária) na tabela de referência.
am0wa
fonte
1
Melhor resposta de todos os tempos, depois de tentar quase tudo, descobri que tenho que adicionar explicitamente uniqueà coluna da tabela de referência, mesmo que seja uma Primary Key!!
Yahya
Sim, a melhor resposta de sempre ... em particular, o primeiro ponto! No meu caso, fiz uma migração (registrada de 2.5.14 a bookd 2.7.2), onde o script de migração não alterou o mecanismo de banco de dados; portanto, ao criar novas tabelas, recebi esse erro.
Bernhard
Melhor me responda também.
EngineerCoder
Seria ainda mais impressionante com dicas de como verificar / alterar. Para mim, foi uma diferença de agrupamento em nível de coluna e isso me deu a correção (graças para a idéia!): Stackoverflow.com/questions/1294117/...
sjgp
18

Tente usar o mesmo tipo de suas chaves primárias - int (11) - nas chaves estrangeiras - smallint (5) - também.

Espero que ajude!

Felypp Oliveira
fonte
mysql> cria um índice único index_bar_id em foos (bar_id); ... mysql> alterar tabela foos adicionar restrição index_bar_id chave estrangeira (bar_id) referências barras (id); sixarm.com/about/…
CookieCoder
11

Confirme se a codificação e o agrupamento de caracteres para as duas tabelas são iguais.

No meu próprio caso, uma das tabelas estava usando utf8e a outra estava usando latin1.

Eu tive outro caso em que a codificação era a mesma, mas o agrupamento era diferente. Um utf8_general_cio outroutf8_unicode_ci

Você pode executar este comando para definir a codificação e agrupamento para uma tabela.

ALTER TABLE tablename CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;

Espero que isso ajude alguém.

Goke Obasa
fonte
Bom, @Adegoke, great answer #
Edwin Ikechukwu Okonkwo
7

Para definir uma CHAVE ESTRANGEIRA na tabela B, você deve definir uma CHAVE na tabela A.

Na tabela A: INDEX id( id)

E então na tabela B,

CONSTRAINT `FK_id` FOREIGN KEY (`id`) REFERENCES `table-A` (`id`)
user3707075
fonte
Não sei exatamente o que você está dizendo, mas achei que minha sintaxe estava incorreta. Eu estava fazendo: alterar tabela aeronave adicionar restrição fk_somehting_unique chave estrangeira (operator_id) organização de referências, mas deveria ter feito: alterar tabela aeronave adicionar restrição fk_somehting_unique chave estrangeira (operator_id) organização de referências (id) ;
Michael Coxon
7

Eu tive o mesmo problema e a solução foi muito simples. Solução: chaves estrangeiras declaradas na tabela não devem ser definidas como nulas.

referência: se você especificar uma ação SET NULL, certifique-se de não ter declarado as colunas na tabela filha como NOT NULL. ( ref )

Singh
fonte
4

Verifique as seguintes regras:

  • Primeiro verifica se os nomes são dados corretamente para nomes de tabelas

  • O segundo tipo de dados correto atribui à chave estrangeira?

Bhaskar Bhatt
fonte
4

Verifique se as duas tabelas estão no formato InnoDB. Mesmo se alguém estiver no formato MyISAM, a restrição de chave estrangeira não funcionará.

Além disso, outra coisa é que, ambos os campos devem ser do mesmo tipo. Se um é INT, o outro também deve ser INT. Se um é VARCHAR, o outro também deve ser VARCHAR, etc.

Vijay Srinivas
fonte
3

Eu enfrentei o problema e consegui resolvê-lo, garantindo que os tipos de dados correspondessem exatamente.

Eu estava usando o SequelPro para adicionar a restrição e ela estava tornando a chave primária como não assinada por padrão.

Aprendiz
fonte
2

Verifique a assinatura nas duas colunas da tabela. Se a coluna da tabela de referência for ASSINADA, a coluna da tabela de referência também deverá ser ASSINADA.

katwekibs
fonte
1

NOTA: As tabelas a seguir foram retiradas de algum site quando eu fazia alguma pesquisa e desenvolvimento no banco de dados. Portanto, a convenção de nomenclatura não é adequada.

Para mim, o problema era que minha tabela pai tinha um conjunto de caracteres diferente daquele que eu estava criando.

Tabela pai (PRODUCTS)

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

Tabela filho que teve um 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 PARA

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

Meu problema era que eu estava tentando criar a tabela de relações antes de outras tabelas!

Ahmad Mobaraki
fonte
SET foreign_key_checks = 0;
LeeGee 20/04
0

Ocorreu um erro semelhante ao criar chave estrangeira em uma tabela Muitos para Muitos, onde a chave primária consistia em 2 chaves estrangeiras e outra coluna normal. Corrigi o problema corrigindo o nome da tabela referenciada, ou seja, empresa, conforme mostrado no código corrigido abaixo:

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

Eu tive um erro semelhante com duas chaves estrangeiras para tabelas diferentes, mas com os mesmos nomes de chave! Renomeei as chaves e o erro desapareceu)

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

Ocorreu um erro semelhante, mas no meu caso estava faltando declarar o pk como auto_increment.

Para o caso de ser útil para qualquer pessoa

Luiz Rolim
fonte
0

Eu recebi o mesmo erro. A causa no meu caso foi:

  1. Criei um backup de um banco de dados via phpmyadmin, copiando o banco de dados inteiro.
  2. Criei um novo banco de dados com o mesmo nome que o antigo banco de dados o selecionou.
  3. Comecei um script SQL para criar tabelas e dados atualizados.
  4. Eu entendi o erro. Além disso, quando eu desabilitei Foreign_key_checks. Embora o banco de dados esteja completamente vazio.

A causa foi: Desde que usei o phpmyadmin para criar algumas chaves estrangeiras no banco de dados renomeado - as chaves estrangeiras foram criadas com um prefixo de nome de banco de dados, mas o prefixo de nome de banco de dados não foi atualizado. Portanto, ainda havia referências no backup-db apontando para o recém-criado db.

lsblsb
fonte
0

Minha solução é talvez um pouco embaraçosa e conta a história de por que às vezes você deve olhar para o que tem à sua frente em vez dessas postagens :)

Eu já havia executado um engenheiro avançado antes, o que falhou, o que significava que meu banco de dados já tinha algumas tabelas; então, eu estava sentado tentando consertar falhas de restrições de chave estrangeira tentando garantir que tudo estava perfeito, mas foi executado no tabelas criadas anteriormente, portanto não prevaleceu.

DenLilleMand
fonte
0

Uma causa adicional desse erro ocorre quando suas tabelas ou colunas contêm palavras - chave reservadas :

Às vezes se esquece deles.

EssGee
fonte
0

No meu caso, houve um erro de sintaxe que não foi explicitamente notificado pelo console do MySQL ao executar a consulta. No entanto, SHOW ENGINE INNODB STATUSa LATEST FOREIGN KEY ERRORseção de comando relatou,

  Syntax error close to:

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

Eu tive que deixar um espaço em branco no meio REFERENCESe rolefazê-lo funcionar.

vicke4
fonte
0

Para mim, foi - você não pode omitir o prefixo da tabela de banco de dados atual se criar um FK para um banco de dados não atual que faça referência ao banco de dados atual:

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

Se eu omitir "currrent_db". para a tabela de usuários, recebo o erro FK. Interessante que MOSTRE O ESTADO DO MOTOR INNODB; não mostra nada neste caso.

barba
fonte
-1

Eu tive esse mesmo problema, corrigi o nome do mecanismo como Innodb nas tabelas pai e filho e corrigi o nome do campo de referência FOREIGN KEY ( c_id) REFERENCES x9o_parent_table( c_id)
e, em seguida, ele funciona bem e as tabelas são instaladas corretamente. Isso será usado por completo para alguém.

subramaniano
fonte