MySQL Creando tablas con claves externas dando errno: 150

98

Estoy tratando de crear una tabla en MySQL con dos claves externas, que hacen referencia a las claves primarias en otras 2 tablas, pero obtengo un error errno: 150 y no creará la tabla.

Aquí está el SQL para las 3 tablas:

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;

Cualquier ayuda será muy apreciada.

Bill Karwin
fuente
1
¿Podría publicar el resultado del error y decirnos qué comando (de los tres) está causando el error?
Dave
4
¿Qué pasa con los tics al revés auto_increment? Eso no es válido. Auto_increment es una palabra clave, no un identificador.
Bill Karwin

Respuestas:

238

Tuve el mismo problema con ALTER TABLE ADD FOREIGN KEY.

Después de una hora, descubrí que se deben cumplir estas condiciones para no obtener el error 150:

  1. La tabla principal debe existir antes de definir una clave externa para hacer referencia a ella. Debe definir las tablas en el orden correcto: la tabla principal primero, luego la tabla secundaria. Si ambas tablas se referencian entre sí, debe crear una tabla sin restricciones FK, luego crear la segunda tabla y luego agregar la restricción FK a la primera tabla con ALTER TABLE.

  2. Las dos tablas deben admitir restricciones de clave externa, es decir ENGINE=InnoDB. Otros motores de almacenamiento ignoran silenciosamente las definiciones de clave externa, por lo que no devuelven ningún error o advertencia, pero la restricción FK no se guarda.

  3. Las columnas a las que se hace referencia en la tabla principal deben ser las columnas más a la izquierda de una clave. Mejor si la clave en el Padre es PRIMARY KEYo UNIQUE KEY.

  4. La definición de FK debe hacer referencia a la (s) columna (s) PK en el mismo orden que la definición PK. Por ejemplo, si el FK REFERENCES Parent(a,b,c), el PK principal no debe definirse en las columnas en orden (a,c,b).

  5. Las columnas PK de la tabla principal deben ser del mismo tipo de datos que las columnas FK de la tabla secundaria. Por ejemplo, si una columna PK en la tabla principal es UNSIGNED, asegúrese de definir UNSIGNEDpara la columna correspondiente en el campo de la tabla secundaria.

    Excepción: la longitud de las cadenas puede ser diferente. Por ejemplo, VARCHAR(10)puede hacer referencia VARCHAR(20)o viceversa.

  6. Cualquier columna FK de tipo cadena debe tener el mismo conjunto de caracteres y colación que las columnas PK correspondientes.

  7. Si ya hay datos en la tabla secundaria, todos los valores de la (s) columna (s) FK deben coincidir con un valor en la (s) columna (s) PK de la tabla principal. Verifique esto con una consulta como:

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

    Esto debe devolver cero (0) valores no coincidentes. Obviamente, esta consulta es un ejemplo genérico; debe sustituir los nombres de sus tablas y columnas.

  8. Ni la tabla principal ni la tabla secundaria pueden ser una TEMPORARYtabla.

  9. Ni la tabla principal ni la tabla secundaria pueden ser una PARTITIONEDtabla.

  10. Si declara un FK con la ON DELETE SET NULLopción, entonces la (s) columna (s) FK deben ser anulables.

  11. Si declara un nombre de restricción para una clave externa, el nombre de restricción debe ser único en todo el esquema, no solo en la tabla en la que se define la restricción. Es posible que dos tablas no tengan su propia restricción con el mismo nombre.

  12. Si hay otros FK en otras tablas que apuntan al mismo campo para el que está intentando crear el nuevo FK y tienen un formato incorrecto (es decir, una intercalación diferente), primero deberán ser coherentes. Esto puede ser el resultado de cambios pasados ​​donde SET FOREIGN_KEY_CHECKS = 0;se utilizó con una relación inconsistente definida por error. Consulte la respuesta de @ andrewdotn a continuación para obtener instrucciones sobre cómo identificar estos problemas de FK.

Espero que esto ayude.

maravilla
fuente
4
una cosa más que vale la pena agregar: si el PK de la tabla principal es más de un campo, el orden de los campos en el FK debe ser el mismo que el orden en el PK
Kip
26
Esto incluye cosas como int(11) unsigned NOT NULLvs int(11) NOT NULL.
Glen Solsberry
4
ALTER TABLE nombre_tabla ENGINE = InnoDB;
TolMera
12
Si la tabla está definida ENGINE = MyISAM, no genera errno 150 porque ignora las declaraciones de clave externa. Es como decir que la mejor manera de evitar problemas con el motor de su automóvil es conducir un bote. :-)
Bill Karwin
2
Además, si la ON DELETEregla de su CONSTRAINT es, SET NULLasegúrese de que la clave externa pueda ser NULL. Pasé 30 minutos leyendo esta respuesta una y otra vez, asegurándome de que mis tablas cumplieran con las condiciones pero aún obteniendo el Error 150. Entonces noté que mi FK era un campo NO NULO, lo que significa que la regla era imposible de aplicar.
Martin Joiner
62

El mensaje genérico "errno 150" de MySQL " significa que una restricción de clave externa no se formó correctamente ". Como probablemente ya sepa si está leyendo esta página, el mensaje de error genérico "errno: 150" es realmente inútil. Sin embargo:

Puede obtener el mensaje de error real ejecutando SHOW ENGINE INNODB STATUS;y luego buscando LATEST FOREIGN KEY ERRORen el resultado.

Por ejemplo, este intento de crear una restricción de clave externa:

CREATE TABLE t1
(id INTEGER);

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

falla con el error Can't create table 'test.t2' (errno: 150). Eso no le dice a nadie nada útil aparte de que es un problema de clave externa. Pero corre SHOW ENGINE INNODB STATUS;y te 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.

Dice que el problema es que no puede encontrar un índice. SHOW INDEX FROM t1muestra que no hay ningún índice para la tabla t1. Solucione eso, por ejemplo, definiendo una clave primaria activada t1, y la restricción de clave externa se creará correctamente.

andrewdotn
fuente
4
SHOW ENGINE INNODB STATUSme ayudó a identificar de inmediato un problema que había estado tratando de diagnosticar durante casi una hora. Gracias.
jatrim
En mi caso, esto indicó que una tabla totalmente diferente en la que FK hizo el mismo campo al que estaba tratando de señalar era inconsistente y, por lo tanto, no guardaría la nueva ... suponiendo que se usara SET FOREIGN_KEY_CHECKS = 0;durante una importación / alteración que estaba mal formada en un momento u otro. Gran ayuda, gracias.
oucil
25

Asegúrese de que las propiedades de los dos campos que intenta vincular con una restricción sean exactamente las mismas.

A menudo, la propiedad 'sin firmar' en una columna de ID lo sorprenderá.

ALTER TABLE `dbname`.`tablename` CHANGE `fieldname` `fieldname` int(10) UNSIGNED NULL;
Jon Winstanley
fuente
En mi experiencia, vale la pena usar SHOW CREATE TABLE de MySQL en su tabla principal para verificar exactamente qué indicadores están configurados en su columna de índice principal, luego cópielos en su columna de clave externa. Puede haber cosas allí, como "sin firmar" que no son obvias.
Ambulare
10

¿Cuál es el estado actual de su base de datos cuando ejecuta este script? ¿Está completamente vacío? Su SQL funciona bien para mí cuando creo una base de datos desde cero, pero errno 150 generalmente tiene que ver con eliminar y recrear tablas que son parte de una clave externa. Tengo la sensación de que no está trabajando con una base de datos nueva y 100% nueva.

Si está cometiendo un error cuando "fuente" -ing su archivo SQL, debería poder ejecutar el comando "SHOW ENGINE INNODB STATUS" desde el indicador de MySQL inmediatamente después del comando "fuente" para ver información de error más detallada.

También puede consultar la entrada manual:

Si vuelve a crear una tabla que se eliminó, debe tener una definición que se ajuste a las restricciones de clave externa que la hacen referencia. Debe tener los nombres y tipos de columna correctos, y debe tener índices en las claves referenciadas, como se indicó anteriormente. Si no se cumplen, MySQL devuelve el número de error 1005 y se refiere al error 150 en el mensaje de error. Si MySQL informa un número de error 1005 de una instrucción CREATE TABLE, y el mensaje de error se refiere al error 150, la creación de la tabla falló porque una restricción de clave externa no se formó correctamente.

- Manual de referencia de MySQL 5.1 .

Brent escribe código
fuente
5

Para las personas que ven este hilo con el mismo problema:

Hay muchas razones para obtener errores como este. Para obtener una lista bastante completa de causas y soluciones de errores de clave externa en MySQL (incluidos los que se describen aquí), consulte este enlace:

Errores de clave externa de MySQL y Errno 150

juacala
fuente
4

Para otros que encuentran esta entrada SO a través de Google: asegúrese de que no está tratando de realizar una acción SET NULL en una columna de clave externa (a ser) definida como "NOT NULL". Eso me causó una gran frustración hasta que me acordé de hacer una COMPROBACIÓN DEL ESTADO DE INNODB DEL MOTOR.

Eric L.
fuente
3

Definitivamente no es el caso, pero este error me pareció bastante común y poco obvio. El objetivo de un FOREIGN KEYpodría no serlo PRIMARY KEY. La respuesta que me resulta útil es:

Una CLAVE EXTRANJERA siempre debe apuntar a un campo verdadero CLAVE PRIMARIA de otra tabla.

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

Como señaló @andrewdotn, la mejor manera es ver el error detallado ( SHOW ENGINE INNODB STATUS;) en lugar de solo un código de error.

Una de las razones podría ser que ya existe un índice con el mismo nombre, puede estar en otra tabla. Como práctica, recomiendo prefijar el nombre de la tabla antes del nombre del índice para evitar tales colisiones. por ejemplo, en lugar de idx_userIdusar idx_userActionMapping_userId.

Mucho más
fuente
3

Asegúrese al principio de que

  1. está utilizando tablas InnoDB.
  2. El campo para FOREIGN KEY tiene el mismo tipo y longitud (!) que el campo fuente.

Tuve el mismo problema y lo solucioné. Tenía INT sin firmar para un campo y solo entero para otro campo.

Juljan
fuente
2

Consejo útil, utilícelo SHOW WARNINGS;después de intentar su CREATEconsulta y recibirá el error, así como una advertencia más detallada:

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

Entonces, en este caso, ¡es hora de volver a crear mi mesa!

sturrockad
fuente
1

Esto suele suceder cuando intenta crear un archivo en una base de datos existente. Suelta todas las tablas primero (o la propia base de datos). Y luego el archivo fuente con SET foreign_key_checks = 0;al principio y SET foreign_key_checks = 1;al final.

noticias enteras
fuente
1

Encontré otra razón por la que esto falla ... nombres de tablas sensibles a mayúsculas y minúsculas.

Para esta definición de tabla

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

Esta definición de tabla funciona

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

mientras que este falla

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

El hecho de que funcionara en Windows y fallara en Unix me tomó un par de horas entenderlo. Espero que ayude a alguien más.

Tim
fuente
1

MySQL Workbench 6.3 para Mac OS.

Problema: errno 150 en la tabla X al intentar hacer ingeniería directa en un diagrama de base de datos, 20 de 21 tuvieron éxito, 1 falló. Si se eliminaron los FK en la tabla X, el error se movió a una tabla diferente que antes no fallaba.

Se cambió el motor de todas las tablas a myISAM y funcionó bien.

ingrese la descripción de la imagen aquí

Eduardo Chongkan
fuente
0

También vale la pena comprobar que no está operando accidentalmente en la base de datos incorrecta. Este error ocurrirá si la tabla externa no existe. ¿Por qué MySQL tiene que ser tan críptico?

SystemParadox
fuente
0

Asegúrese de que las claves externas no aparezcan como únicas en el padre. Tuve este mismo problema y lo resolví demarcándolo como no único.

Raza
fuente
0

En mi caso, se debió al hecho de que el campo que era un campo de clave externa tenía un nombre demasiado largo, es decir. foreign key (some_other_table_with_long_name_id). Prueba algo más corto. El mensaje de error es un poco engañoso en ese caso.

Además, como @Jon mencionó anteriormente, las definiciones de campo deben ser las mismas (tenga cuidado con el unsignedsubtipo).

Kangur
fuente
0

(Notas al margen demasiado grandes para un comentario)

No hay necesidad de una AUTO_INCREMENTidentificación en una tabla de mapeo; desaste de eso.

Cambie PRIMARY KEYa (role_id, role_group_id)(en cualquier orden). Esto hará que los accesos sean más rápidos.

Como probablemente desee mapear ambas direcciones, agregue también una INDEXcon esas dos columnas en el orden opuesto. (No hay necesidad de hacerlo UNIQUE).

Más consejos: http://mysql.rjweb.org/doc.php/index_cookbook_mysql#speeding_up_wp_postmeta

Rick James
fuente
0

Cuando la restricción de clave externa se basa en el varchartipo, además de la lista proporcionada pormarv-el la columna de destino, debe haber una restricción única.

Ralph
fuente
0

ejecutar debajo de la línea antes de crear la tabla: SET FOREIGN_KEY_CHECKS = 0;

La opción FOREIGN_KEY_CHECKS especifica si se deben verificar o no las restricciones de clave externa para las tablas InnoDB.

- Especifique para verificar las restricciones de clave externa (este es el predeterminado)

SET FOREIGN_KEY_CHECKS = 1;

 

- No verifique las restricciones de clave externa

SET FOREIGN_KEY_CHECKS = 0;

Cuándo usarlo: deshabilitar temporalmente las restricciones de referencia (establecer FOREIGN_KEY_CHECKS en 0) es útil cuando necesita volver a crear las tablas y cargar datos en cualquier orden padre-hijo

usuario11949964
fuente
-1

Encontré el mismo problema, pero comprobé que no tenía la tabla principal. Así que solo edito la migración principal frente a la migración secundaria. Simplemente hazlo.

韩向飞
fuente
1
esto debería haber sido un comentario en lugar de una respuesta
hannad rehman