MySQL Criando tabelas com chaves estrangeiras dando errno: 150

98

Estou tentando criar uma tabela no MySQL com duas chaves estrangeiras, que fazem referência às chaves primárias em 2 outras tabelas, mas estou recebendo um errno: erro 150 e não criará a tabela.

Aqui está o SQL para todas as 3 tabelas:

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;

Qualquer ajuda seria muito apreciada.

Bill Karwin
fonte
1
Você poderia postar a saída do erro e nos dizer qual comando (dos três) está causando o erro?
dave
4
O que é esse tique-taque por aí auto_increment? Isso não é válido. Auto_increment é uma palavra-chave, não um identificador.
Bill Karwin,

Respostas:

238

Eu tive o mesmo problema com ALTER TABLE ADD FOREIGN KEY.

Depois de uma hora, descobri que essas condições devem ser satisfeitas para não ocorrer o erro 150:

  1. A tabela pai deve existir antes de você definir uma chave estrangeira para referenciá-la. Você deve definir as tabelas na ordem certa: Tabela pai primeiro, depois a tabela filho. Se as duas tabelas fizerem referência uma à outra, você deverá criar uma tabela sem restrições FK, depois criar a segunda tabela e adicionar a restrição FK à primeira tabela com ALTER TABLE.

  2. As duas tabelas têm de suportar restrições de chave estrangeira, ie ENGINE=InnoDB. Outros mecanismos de armazenamento ignoram silenciosamente as definições de chave estrangeira, portanto, eles não retornam nenhum erro ou aviso, mas a restrição FK não é salva.

  3. As colunas referenciadas na tabela pai devem ser as colunas mais à esquerda de uma chave. Melhor se a chave no Parent for PRIMARY KEYou UNIQUE KEY.

  4. A definição FK deve fazer referência à (s) coluna (s) PK na mesma ordem que a definição PK. Por exemplo, se o FK REFERENCES Parent(a,b,c), o PK do pai não deve ser definido nas colunas em ordem (a,c,b).

  5. A (s) coluna (s) PK na tabela pai devem ter o mesmo tipo de dados que a (s) coluna (s) FK na tabela filho. Por exemplo, se uma coluna PK na tabela pai for UNSIGNED, certifique-se de definir UNSIGNEDpara a coluna correspondente no campo Tabela filho.

    Exceção: o comprimento das strings pode ser diferente. Por exemplo, VARCHAR(10)pode fazer referência VARCHAR(20)ou vice-versa.

  6. Qualquer coluna FK do tipo string deve ter o mesmo conjunto de caracteres e agrupamento que a (s) coluna (s) PK correspondente (s).

  7. Se já houver dados na tabela filho, cada valor na (s) coluna (s) FK deve corresponder a um valor na (s) coluna (s) PK da tabela pai. Verifique isso com uma consulta como:

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

    Deve retornar zero (0) valores não correspondidos. Obviamente, esta consulta é um exemplo genérico; você deve substituir seus nomes de tabela e nomes de coluna.

  8. Nem a tabela pai nem a tabela filho podem ser uma TEMPORARYtabela.

  9. Nem a tabela pai nem a tabela filho podem ser uma PARTITIONEDtabela.

  10. Se você declarar um FK com a ON DELETE SET NULLopção, a (s) coluna (s) FK devem ser anuláveis.

  11. Se você declarar um nome de restrição para uma chave estrangeira, o nome da restrição deve ser único em todo o esquema, não apenas na tabela em que a restrição é definida. Duas tabelas não podem ter sua própria restrição com o mesmo nome.

  12. Se houver qualquer outro FK em outras tabelas apontando para o mesmo campo para o qual você está tentando criar o novo FK, e eles estiverem malformados (isto é, agrupamento diferente), eles precisarão ser consistentes primeiro. Isso pode ser resultado de alterações anteriores em que SET FOREIGN_KEY_CHECKS = 0;foi utilizado com um relacionamento inconsistente definido por engano. Consulte a resposta de @andrewdotn abaixo para obter instruções sobre como identificar esses FKs problemáticos.

Espero que isto ajude.

maravilha
fonte
4
mais uma coisa que vale a pena acrescentar: se o PK da tabela-pai for mais de um campo, a ordem dos campos no FK deve ser a mesma que a ordem no PK
Kip
26
Isso inclui coisas como int(11) unsigned NOT NULLvs int(11) NOT NULL.
Glen Solsberry
4
ALTER TABLE nome_tabela ENGINE = InnoDB;
TolMera
12
Se a tabela for definida ENGINE = MyISAM ela não gera errno 150 porque ignora as declarações de chave estrangeira. É como dizer que a melhor maneira de evitar problemas com o motor do automóvel é dirigir um barco. :-)
Bill Karwin,
2
Além disso, se sua ON DELETEregra CONSTRAINT for, SET NULLcertifique-se de que a chave estrangeira pode ser NULL! Passei 30 minutos lendo essa resposta sem parar, certificando-me de que minhas tabelas atendiam às condições, mas ainda obtinha o Erro 150. Então, percebi que meu FK era um campo NOT NULL, o que significa que a regra era impossível de ser aplicada.
Martin Joiner
62

A mensagem genérica “errno 150” do MySQL “ significa que uma restrição de chave estrangeira não foi formada corretamente .” Como você provavelmente já sabe se está lendo esta página, a mensagem de erro genérica “errno: 150” é realmente inútil. Contudo:

Você pode obter a mensagem de erro real executando SHOW ENGINE INNODB STATUS;e, em seguida, procurando LATEST FOREIGN KEY ERRORna saída.

Por exemplo, esta tentativa de criar uma restrição de chave estrangeira:

CREATE TABLE t1
(id INTEGER);

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

falha com o erro Can't create table 'test.t2' (errno: 150). Isso não diz a ninguém nada útil além de que é um problema de chave estrangeira. Mas corra SHOW ENGINE INNODB STATUS;e ele 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.

Diz que o problema é que não consegue encontrar um índice. SHOW INDEX FROM t1mostra que não há nenhum índice para a tabela t1. Corrija isso, digamos, definindo uma chave primária em t1, e a restrição de chave estrangeira será criada com sucesso.

andrewdotn
fonte
4
SHOW ENGINE INNODB STATUSajudou-me a identificar imediatamente um problema que venho tentando diagnosticar há quase uma hora. Obrigado.
jatrim
No meu caso, isso indicava que uma tabela totalmente diferente que FK tinha o mesmo campo que eu estava tentando apontar era inconsistente e, portanto, não salvaria o novo ... presumindo que isso fosse usado SET FOREIGN_KEY_CHECKS = 0;durante uma importação / alteração malformada em um momento ou outro. Grande ajuda, obrigado.
oucil
25

Certifique-se de que as propriedades dos dois campos que você está tentando vincular a uma restrição sejam exatamente as mesmas.

Freqüentemente, a propriedade 'unsigned' em uma coluna de ID irá pegá-lo.

ALTER TABLE `dbname`.`tablename` CHANGE `fieldname` `fieldname` int(10) UNSIGNED NULL;
Jon Winstanley
fonte
Na minha experiência, vale a pena usar SHOW CREATE TABLE do MySQL em sua tabela principal para verificar exatamente quais sinalizadores estão definidos em sua coluna de índice principal e, em seguida, copiá-los para sua coluna de chave estrangeira. Pode haver coisas lá, como "não assinado", que não são óbvias.
Ambulare
10

Qual é o estado atual do seu banco de dados quando você executa este script? Está completamente vazio? Seu SQL funciona bem para mim ao criar um banco de dados do zero, mas errno 150 geralmente tem a ver com descartar e recriar tabelas que são parte de uma chave estrangeira. Estou tendo a sensação de que você não está trabalhando com um banco de dados 100% novo e novo.

Se você está errando ao fazer o "código-fonte" em seu arquivo SQL, você deve ser capaz de executar o comando "SHOW ENGINE INNODB STATUS" no prompt do MySQL imediatamente após o comando "fonte" para ver informações mais detalhadas sobre o erro.

Você pode querer verificar a entrada manual também:

Se você recriar uma tabela que foi eliminada, ela deve ter uma definição que esteja em conformidade com as restrições de chave estrangeira que fazem referência a ela. Ele deve ter os nomes e tipos de coluna corretos e deve ter índices nas chaves referenciadas, conforme declarado anteriormente. Se eles não forem satisfeitos, o MySQL retornará o erro número 1005 e fará referência ao erro 150 na mensagem de erro. Se o MySQL relatar um erro número 1005 de uma instrução CREATE TABLE, e a mensagem de erro referir-se ao erro 150, a criação da tabela falhou porque uma restrição de chave estrangeira não foi formada corretamente.

- Manual de referência do MySQL 5.1 .

Brent escreve código
fonte
5

Para pessoas que estão vendo este tópico com o mesmo problema:

Existem muitos motivos para erros como esse. Para uma lista bastante completa de causas e soluções de erros de chave estrangeira no MySQL (incluindo aqueles discutidos aqui), verifique este link:

Erros de chave estrangeira MySQL e Errno 150

Juacala
fonte
4

Para outros que encontrarem esta entrada do SO através do Google: Certifique-se de que você não está tentando fazer uma ação SET NULL em uma coluna de chave estrangeira (a ser) definida como "NOT NULL". Isso causou grande frustração até que me lembrei de fazer um CHECK ENGINE INNODB STATUS.

Eric L.
fonte
3

Definitivamente não é o caso, mas achei esse erro muito comum e pouco óbvio. O alvo de um FOREIGN KEYnão poderia ser PRIMARY KEY. A resposta que se tornou útil para mim é:

Uma FOREIGN KEY sempre deve ser apontada para um campo true PRIMARY KEY de outra tabela.

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

Como apontado por @andrewdotn, a melhor maneira é ver o erro detalhado (SHOW ENGINE INNODB STATUS; ) em vez de apenas um código de erro.

Um dos motivos pode ser que já exista um índice com o mesmo nome, pode estar em outra tabela. Como prática, recomendo prefixar o nome da tabela antes do nome do índice para evitar tais conflitos. por exemplo, em vez de idx_userIdusar idx_userActionMapping_userId.

Muito mais
fonte
3

Por favor, certifique-se primeiro de que

  1. você está usando tabelas InnoDB.
  2. O campo para FOREIGN KEY tem o mesmo tipo e comprimento (!) que o campo de origem.

Tive o mesmo problema e resolvi. Eu tinha INT não assinado para um campo e apenas inteiro para outro campo.

Juljan
fonte
2

Dica útil, use SHOW WARNINGS;após tentar sua CREATEconsulta e você receberá o erro, bem como o aviso mais detalhado:

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

Portanto, neste caso, é hora de recriar minha mesa!

Sturrockad
fonte
1

Isso geralmente acontece quando você tenta originar o arquivo em um banco de dados existente. Elimine todas as tabelas primeiro (ou o próprio banco de dados). E então o arquivo de origem com SET foreign_key_checks = 0;no início e SET foreign_key_checks = 1;no final.

wholenewstrain
fonte
1

Encontrei outro motivo para a falha ... nomes de tabelas com distinção entre maiúsculas e minúsculas.

Para esta definição de tabela

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

Esta definição de tabela funciona

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

enquanto este falha

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

O fato de que funcionou no Windows e falhou no Unix me levou algumas horas para descobrir. Espero que ajude outra pessoa.

Tim
fonte
1

MySQL Workbench 6.3 para Mac OS.

Problema: errno 150 na tabela X ao tentar fazer Forward Engineering em um diagrama DB, 20 de 21 tiveram sucesso, 1 falhou. Se FKs na tabela X foram excluídos, o erro foi movido para uma tabela diferente que não estava falhando antes.

Todos os mecanismos de tabelas foram alterados para myISAM e funcionou perfeitamente.

insira a descrição da imagem aqui

Eduardo chongkan
fonte
0

Também vale a pena verificar se você não está operando acidentalmente no banco de dados errado. Este erro ocorrerá se a tabela externa não existir. Por que o MySQL precisa ser tão enigmático?

SystemParadox
fonte
0

Certifique-se de que as chaves estrangeiras não estejam listadas como exclusivas no pai. Eu tive esse mesmo problema e resolvi demarcando-o como não único.

Raza
fonte
0

No meu caso, foi devido ao fato de que o campo que era um campo de chave estrangeira tinha um nome muito longo, ou seja. foreign key (some_other_table_with_long_name_id). Tente mais curto. A mensagem de erro é um pouco enganosa nesse caso.

Além disso, como @Jon mencionou anteriormente - as definições de campo devem ser as mesmas (cuidado com o unsignedsubtipo).

Kangur
fonte
0

(Notas laterais muito grandes para um comentário)

Não há necessidade de um AUTO_INCREMENTid em uma tabela de mapeamento; livre-se disso.

Altere o PRIMARY KEYpara (role_id, role_group_id)(em qualquer ordem). Isso tornará os acessos mais rápidos.

Como você provavelmente deseja mapear as duas direções, adicione também um INDEXcom essas duas colunas na ordem oposta. (Não há necessidade de fazer isso UNIQUE.)

Mais dicas: http://mysql.rjweb.org/doc.php/index_cookbook_mysql#speeding_up_wp_postmeta

Rick James
fonte
0

Quando a restrição de chave estrangeira é baseado no varchartipo, em seguida, para além da lista fornecida pelamarv-el a coluna de destino deve ter uma restrição exclusiva.

Ralph
fonte
0

execute a linha abaixo antes de criar a tabela: SET FOREIGN_KEY_CHECKS = 0;

A opção FOREIGN_KEY_CHECKS especifica se deve ou não verificar as restrições de chave estrangeira para tabelas InnoDB.

- Especifique para verificar as restrições de chave estrangeira (este é o padrão)

SET FOREIGN_KEY_CHECKS = 1;

 

- Não verifique as restrições de chave estrangeira

SET FOREIGN_KEY_CHECKS = 0;

Quando usar: Desativar temporariamente as restrições referenciais (defina FOREIGN_KEY_CHECKS como 0) é útil quando você precisa recriar as tabelas e carregar dados em qualquer ordem pai-filho

user11949964
fonte
-1

Eu encontrei o mesmo problema, mas descobri que não tinha a tabela pai. Então, eu apenas edito a migração pai antes da migração filho. Apenas faça.

韩向飞
fonte
1
deveria ter sido um comentário em vez de uma resposta
hannad rehman