MySQL Создание таблиц с внешними ключами с ошибкой: 150

98

Я пытаюсь создать таблицу в MySQL с двумя внешними ключами, которые ссылаются на первичные ключи в двух других таблицах, но я получаю ошибку errno: 150, и таблица не создается.

Вот SQL для всех трех таблиц:

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;

Любая помощь будет принята с благодарностью.

Билл Карвин
источник
1
Не могли бы вы опубликовать сообщение об ошибке и сообщить нам, какая команда (из трех) вызывает ошибку?
Дэйв
4
Что с обратными клещами auto_increment? Это неверно. Auto_increment - это ключевое слово, а не идентификатор.
Билл Карвин,

Ответы:

239

У меня была такая же проблема с ALTER TABLE ADD FOREIGN KEY.

Через час я обнаружил, что эти условия должны быть выполнены, чтобы не получить ошибку 150:

  1. Родительская таблица должна существовать до того, как вы определите внешний ключ для ссылки на нее. Вы должны определить таблицы в правильном порядке: сначала родительская таблица, затем дочерняя таблица. Если обе таблицы ссылаются друг на друга, вы должны создать одну таблицу без ограничений FK, затем создать вторую таблицу, а затем добавить ограничение FK в первую таблицу с помощью ALTER TABLE.

  2. Эти две таблицы должны поддерживать ограничения внешнего ключа, то есть ENGINE=InnoDB. Другие механизмы хранения молча игнорируют определения внешнего ключа, поэтому они не возвращают ошибок или предупреждений, но ограничение FK не сохраняется.

  3. Столбцы, на которые указывает ссылка в родительской таблице, должны быть крайними левыми столбцами ключа. Лучше всего, если ключ в родительском элементе будет PRIMARY KEYили UNIQUE KEY.

  4. Определение FK должно ссылаться на столбцы PK в том же порядке, что и определение PK. Например, если FK, REFERENCES Parent(a,b,c)то PK Родителя не должен определяться в столбцах по порядку (a,c,b).

  5. Столбцы PK в родительской таблице должны иметь тот же тип данных, что и столбцы FK в дочерней таблице. Например, если столбец PK в родительской таблице есть UNSIGNED, обязательно укажите UNSIGNEDсоответствующий столбец в поле дочерней таблицы.

    Исключение: длина строк может быть разной. Например, VARCHAR(10)можно ссылаться VARCHAR(20)или наоборот.

  6. Любые столбцы FK строкового типа должны иметь тот же набор символов и сопоставление, что и соответствующие столбцы PK.

  7. Если в дочерней таблице уже есть данные, каждое значение в столбце (ах) FK должно соответствовать значению в столбце (ах) родительской таблицы. Проверьте это с помощью такого запроса:

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

    Это должно вернуть ноль (0) несогласованных значений. Очевидно, что этот запрос является общим примером; вы должны заменить имена таблиц и столбцов.

  8. Ни родительская таблица, ни дочерняя таблица не могут быть TEMPORARYтаблицей.

  9. Ни родительская таблица, ни дочерняя таблица не могут быть PARTITIONEDтаблицей.

  10. Если вы объявляете FK с ON DELETE SET NULLопцией, то столбцы FK должны допускать значение NULL.

  11. Если вы объявляете имя ограничения для внешнего ключа, имя ограничения должно быть уникальным во всей схеме, а не только в таблице, в которой определено ограничение. Две таблицы могут не иметь собственного ограничения с одинаковым именем.

  12. Если в других таблицах есть какие-либо другие FK, указывающие на то же поле, для которого вы пытаетесь создать новый FK, и они имеют неправильный формат (т. Е. Другое сопоставление), их необходимо сначала согласовать. Это может быть результатом прошлых изменений, которые SET FOREIGN_KEY_CHECKS = 0;использовались с несовместимыми отношениями, определенными по ошибке. См. Ответ @andrewdotn ниже, чтобы узнать, как определить эти проблемные FK.

Надеюсь это поможет.

Marv-El
источник
4
еще одна вещь, которую стоит добавить: если PK родительской таблицы - это более одного поля, порядок полей в FK должен быть таким же, как порядок в PK
Кип
26
Это включает в себя такие вещи , как int(11) unsigned NOT NULLпротив int(11) NOT NULL.
Глен Солсберри
4
ALTER TABLE имя_таблицы ENGINE = InnoDB;
TolMera
12
Если таблица определена ENGINE = MyISAM, она не генерирует ошибку 150, потому что игнорирует объявления внешнего ключа. Это все равно что сказать, что лучший способ избежать проблем с двигателем автомобиля - это управлять лодкой. :-)
Билл Карвин
2
Кроме того, если ваше ON DELETEправило ОГРАНИЧЕНИЯ - SET NULLубедитесь, что внешний ключ действительно может иметь значение NULL! Я потратил 30 минут на чтение этого ответа снова и снова, проверяя, соответствуют ли мои таблицы условиям, но все еще получаю ошибку 150. Затем я заметил, что мое поле FK было НЕ NULL, что означает, что правило было невозможно применить.
Martin Joiner
62

Общее сообщение MySQL «errno 150» « означает, что ограничение внешнего ключа было неправильно сформировано ». Как вы, вероятно, уже знаете, если читаете эту страницу, типичное сообщение об ошибке «errno: 150» действительно бесполезно. Тем не мение:

Вы можете получить фактическое сообщение об ошибке, запустив SHOW ENGINE INNODB STATUS;и выполнив поиск LATEST FOREIGN KEY ERRORв выводе.

Например, эта попытка создать ограничение внешнего ключа:

CREATE TABLE t1
(id INTEGER);

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

выходит из строя с ошибкой Can't create table 'test.t2' (errno: 150). Это никому не говорит ничего полезного, кроме того, что это проблема внешнего ключа. Но беги, SHOW ENGINE INNODB STATUS;и он скажет:

------------------------
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.

Он говорит, что проблема в том, что он не может найти индекс. SHOW INDEX FROM t1показывает, что для таблицы вообще нет индексов t1. Исправьте это, скажем, определив первичный ключ t1, и ограничение внешнего ключа будет успешно создано.

Andrewdotn
источник
4
SHOW ENGINE INNODB STATUSпомог мне сразу определить проблему, которую я пытался диагностировать почти час. Спасибо.
jatrim
В моем случае это указывало на то, что совершенно другая таблица, которая FK имела то же поле, на которое я пытался указать, была несовместима и поэтому не могла сохранить новую ... при условии, что это было из-за использования SET FOREIGN_KEY_CHECKS = 0;во время импорта / изменения, которое было искажено в то или иное время. Большая помощь, спасибо.
oucil
25

Убедитесь, что свойства двух полей, которые вы пытаетесь связать с ограничением, в точности совпадают.

Часто вас уловит свойство «без знака» в столбце идентификатора.

ALTER TABLE `dbname`.`tablename` CHANGE `fieldname` `fieldname` int(10) UNSIGNED NULL;
Джон Уинстэнли
источник
По моему опыту, стоит использовать MySQL SHOW CREATE TABLE в вашей основной таблице, чтобы точно проверить, какие флаги установлены для вашего основного столбца индекса, а затем скопировать их в столбец внешнего ключа. Там могут быть неочевидные вещи, такие как "неподписанные".
Ambulare
10

Каково текущее состояние вашей базы данных при запуске этого сценария? Он совсем пустой? Ваш SQL отлично работает для меня при создании базы данных с нуля, но ошибка 150 обычно связана с удалением и воссозданием таблиц, которые являются частью внешнего ключа. У меня такое чувство, что вы работаете не со 100% свежей и новой базой данных.

Если вы ошибаетесь, когда "исходный" вводит ваш файл SQL, вы должны иметь возможность запустить команду "SHOW ENGINE INNODB STATUS" из командной строки MySQL сразу после команды "source", чтобы увидеть более подробную информацию об ошибке.

Вы также можете проверить ручной ввод:

Если вы повторно создаете таблицу, которая была отброшена, она должна иметь определение, соответствующее ограничениям внешнего ключа, на которые она ссылается. Он должен иметь правильные имена и типы столбцов, а также индексы по ссылочным ключам, как указывалось ранее. Если они не удовлетворены, MySQL возвращает номер ошибки 1005 и ссылается на ошибку 150 в сообщении об ошибке. Если MySQL сообщает об ошибке номер 1005 из оператора CREATE TABLE, а сообщение об ошибке относится к ошибке 150, создание таблицы завершилось неудачно, поскольку ограничение внешнего ключа было неправильно сформировано.

- Справочное руководство по MySQL 5.1 .

Брент пишет код
источник
5

Для людей, которые просматривают эту ветку с той же проблемой:

Есть много причин для появления подобных ошибок. Для получения достаточно полного списка причин и решений ошибок внешнего ключа в MySQL (включая обсуждаемые здесь) перейдите по этой ссылке:

Ошибки внешнего ключа MySQL и Errno 150

Джуакала
источник
4

Для других, кто нашел эту запись SO через Google: убедитесь, что вы не пытаетесь выполнить действие SET NULL для столбца (должен быть) внешнего ключа, определенного как «NOT NULL». Это вызывало большое разочарование, пока я не вспомнил, что нужно выполнить CHECK ENGINE INNODB STATUS.

Эрик Л.
источник
3

Определенно это не так, но я считаю эту ошибку довольно частой и неочевидной. Мишенью FOREIGN KEYне могло быть PRIMARY KEY. Ответ, который мне пригодился:

FOREIGN KEY всегда должен указываться на поле PRIMARY KEY true другой таблицы.

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
источник
3

Как указано @andrewdotn, лучший способ - увидеть подробную ошибку (SHOW ENGINE INNODB STATUS; ), а не просто код ошибки.

Одна из причин может заключаться в том, что индекс с таким же именем уже существует, может быть в другой таблице. На практике я рекомендую ставить имя таблицы перед именем индекса, чтобы избежать таких конфликтов. например, вместо idx_userIdиспользования idx_userActionMapping_userId.

Гораздо более
источник
3

Пожалуйста, сначала убедитесь, что

  1. вы используете таблицы InnoDB.
  2. поле для FOREIGN KEY имеет тот же тип и длину (!), что и исходное поле.

У меня была такая же проблема, и я ее исправил. У меня был беззнаковый INT для одного поля и просто целое число для другого поля.

Юльян
источник
2

Полезный совет, используйте SHOW WARNINGS;после попытки CREATEзапроса, и вы получите сообщение об ошибке, а также более подробное предупреждение:

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

Итак, в этом случае пришло время воссоздать мою таблицу!

Sturrockad
источник
1

Обычно это происходит, когда вы пытаетесь добавить исходный файл в существующую базу данных. Сначала удалите все таблицы (или саму БД). И затем исходный файл с SET foreign_key_checks = 0;в начале и SET foreign_key_checks = 1;в конце.

весь
источник
1

Я нашел еще одну причину, по которой это не удается ... имена таблиц с учетом регистра.

Для этого определения таблицы

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

Это определение таблицы работает

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

тогда как этот терпит неудачу

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

На то, что он работал в Windows и не работал в Unix, у меня ушло несколько часов. Надеюсь, это поможет кому-то другому.

Тим
источник
1

MySQL Workbench 6.3 для Mac OS.

Проблема: ошибка 150 в таблице X при попытке выполнить форвардное проектирование на диаграмме БД, 20 из 21 успешных, 1 не удалось. Если FK в таблице X были удалены, ошибка перемещалась в другую таблицу, в которой раньше не было ошибок.

Изменил весь движок таблиц на myISAM, и он работал нормально.

введите описание изображения здесь

Эдуардо Чонгкан
источник
0

Также стоит убедиться, что вы случайно не работаете с неправильной базой данных. Эта ошибка возникает, если сторонняя таблица не существует. Почему MySQL должен быть таким загадочным?

SystemParadox
источник
0

Убедитесь, что внешние ключи не указаны как уникальные в родительском элементе. У меня была такая же проблема, и я решил ее, обозначив ее как не уникальную.

Раза
источник
0

В моем случае это было из-за того, что поле, которое было полем внешнего ключа, имело слишком длинное имя, т.е. foreign key (some_other_table_with_long_name_id). Попробуйте что-нибудь короче. В этом случае сообщение об ошибке может ввести в заблуждение.

Кроме того, как упоминал ранее @Jon - определения полей должны быть одинаковыми (обратите внимание на unsignedподтип).

Кангур
источник
0

(Боковые заметки слишком велики для комментария)

Нет необходимости в AUTO_INCREMENTидентификаторе в таблице сопоставления; избавиться от этого.

Изменение PRIMARY KEYк (role_id, role_group_id)(в любом порядке). Это ускорит доступ.

Поскольку вы, вероятно, захотите отобразить оба направления, также добавьте INDEXэти два столбца в обратном порядке. (Нет необходимости делать это UNIQUE.)

Дополнительные советы: http://mysql.rjweb.org/doc.php/index_cookbook_mysql#speeding_up_wp_postmeta

Рик Джеймс
источник
0

перед созданием таблицы выполнить строку ниже: SET FOREIGN_KEY_CHECKS = 0;

Параметр FOREIGN_KEY_CHECKS указывает, следует ли проверять ограничения внешнего ключа для таблиц InnoDB.

- Укажите, чтобы проверить ограничения внешнего ключа (это значение по умолчанию)

SET FOREIGN_KEY_CHECKS = 1;

 

- Не проверять ограничения внешнего ключа

УСТАНОВИТЬ FOREIGN_KEY_CHECKS = 0;

Когда использовать: временное отключение реляционных ограничений (установите FOREIGN_KEY_CHECKS на 0) полезно, когда вам нужно воссоздать таблицы и загрузить данные в любом порядке родитель-потомок.

пользователь11949964
источник
-1

Я столкнулся с той же проблемой, но проверяю, что у меня нет родительской таблицы. Поэтому я просто редактирую родительскую миграцию перед дочерней миграцией. Просто сделай это.

韩向飞
источник
1
это должен был быть комментарий, а не ответ
hannad rehman