Внешний ключ не работает в MySQL: почему я могу ВСТАВИТЬ значение, которого нет во внешнем столбце? - PullRequest
19 голосов
/ 19 декабря 2008

Я создал таблицу в MySQL:

CREATE TABLE actions ( A_id int NOT NULL AUTO_INCREMENT,
type ENUM('rate','report','submit','edit','delete') NOT NULL,
Q_id int NOT NULL,
U_id int NOT NULL,
date DATE NOT NULL,
time TIME NOT NULL,
rate tinyint(1),
PRIMARY KEY (A_id),
CONSTRAINT fk_Question FOREIGN KEY (Q_id) REFERENCES questions(P_id),
CONSTRAINT fk_User FOREIGN KEY (U_id) REFERENCES users(P_id));

Это создало таблицу, которую я просто хотел (хотя команда «DESCRIBE actions;» показала мне, что внешние ключи были ключами типа MUL, и я не уверен, что это значит). Однако, когда я пытаюсь ввести Q_id или U_id, который не существует в таблицах вопросов или пользователей, MySQL по-прежнему допускает эти значения.

Что я сделал не так? Как я могу запретить таблице с внешним ключом принимать неверные данные?

ОБНОВЛЕНИЕ 1

Если я добавлю TYPE=InnoDB в конец, я получу ошибку:

ОШИБКА 1005 (HY000): не удается создать таблицу './quotes/actions.frm' (номер ошибки: 150)

Почему это могло произойти?

ОБНОВЛЕНИЕ 2

Мне сказали, что важно обеспечить целостность данных с помощью функциональных внешних ключей, но также и то, что InnoDB не следует использовать с MySQL. Что вы рекомендуете?

Ответы [ 10 ]

53 голосов
/ 19 декабря 2008

Я полагаю, что вашим хранилищем по умолчанию является MyISAM, который игнорирует ограничения внешнего ключа. Он молча принимает объявление внешнего ключа, но не сохраняет ограничение и не применяет его впоследствии.

Однако он неявно создает индекс для столбцов, которые вы объявили для внешнего ключа. В MySQL «KEY» является синонимом «INDEX». Вот что показано в выводе DESCRIBE: индекс, но не ограничение.

Вы можете вставить недопустимые значения в таблицу прямо сейчас, потому что нет ограничений. Чтобы получить ограничение, обеспечивающее ссылочную целостность, вы должны использовать механизм хранения InnoDB:

CREATE TABLE actions (
  A_id int NOT NULL AUTO_INCREMENT,
  ...
  CONSTRAINT fk_Question FOREIGN KEY (Q_id) REFERENCES questions(P_id),
  CONSTRAINT fk_User FOREIGN KEY (U_id) REFERENCES users(P_id)
) ENGINE=InnoDB;

Я всегда думал, что со стороны MySQL было большой ошибкой молча игнорировать объявления ограничений внешнего ключа. Там нет ошибки или предупреждения, что механизм хранения не поддерживает их.

То же самое верно для ограничений CHECK. Кстати, ни один механизм хранения, используемый с MySQL, не поддерживает ограничения CHECK, но синтаксический анализатор SQL принимает их без жалоб.


Ошибка errno 150 возникает, когда она не может создать таблицу InnoDB, поскольку не может понять смысл ограничения внешнего ключа. Вы можете получить больше информации с помощью:

SHOW ENGINE INNODB STATUS;

Некоторые требования для внешних ключей InnoDB:

  • Ссылочная таблица также должна быть InnoDB.
  • Ссылочная таблица должна иметь индекс и первичный ключ.
  • Типы данных SQL для столбца FK и столбца PK со ссылкой должны быть идентичными. Например, INT не соответствует BIGINT или INT UNSIGNED.

Вы можете изменить механизм хранения таблицы, в которой есть данные:

ALTER TABLE actions ENGINE=InnoDB;

Это фактически копирует всю таблицу MyISAM в таблицу InnoDB, а затем, после успешного завершения, удаляет таблицу MyISAM и переименовывает новую таблицу InnoDB в имя прежней таблицы MyISAM. Это называется «реструктуризацией таблицы» и может занимать много времени, в зависимости от того, сколько данных в таблице. Перестройка таблицы происходит во время ALTER TABLE, даже в некоторых случаях, когда это может показаться ненужным.


Re ваше обновление 2:

Мне сказали, что важно обеспечить целостность данных с помощью функциональных внешних ключей, но также и то, что InnoDB не следует использовать с MySQL. Что вы рекомендуете?

Кто тебе это сказал? Это абсолютно неверно. InnoDB имеет лучшую производительность, чем MyISAM (хотя InnoDB требует большего внимания к настройке конфигурации ), InnoDB поддерживает атомарные изменения, транзакции, внешние ключи, а InnoDB гораздо более устойчив к повреждению данных в авария.

Если вы не используете старую неподдерживаемую версию MySQL (5.0 или более раннюю), вы должны использовать InnoDB в качестве используемого по умолчанию механизма хранения и использовать MyISAM, только если вы можете продемонстрировать конкретную рабочую нагрузку, которая дает преимущества от MyISAM.

6 голосов
/ 21 октября 2011

Просто для того, чтобы избавить других от головной боли, которую я пережил, - когда жираф коснулся, убедитесь, что для @FOREIGN_KEY_CHECKS установлено значение 1.

SELECT @@ FOREIGN_KEY_CHECKS

SET FOREIGN_KEY_CHECKS = 1

6 голосов
/ 01 апреля 2010

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

Родительская таблица:

CREATE TABLE NameSubject (
  Autonumber INT NOT NULL AUTO_INCREMENT,
  NameorSubject nvarchar(255),
  PRIMARY KEY (Autonumber)
 ) ENGINE=InnoDB;

Детский стол:

CREATE TABLE Volumes (
  Autonumber INT NOT NULL,
  Volume INT,
  Pages nvarchar(50),
  Reel int,
  Illustrations bit,
  SSMA_TimeStamp timestamp,
  Foreign KEY (Autonumber) references NameSubject(Autonumber)
  ON  update cascade 
)engine=innodb;

"ON update cascade" сотворил для меня магию.

Я надеюсь, что это работает для других. Желаем удачи.

1 голос
/ 01 июня 2014

Этот ответ сэкономил бы мне много времени, если бы я увидел его первым. Попробуйте следующие три шага, которые я заказал по частоте ошибок новичка:

(1) Измените таблицу на InnodDB, добавив «ENGINE = InnoDB» в свои операторы «CREATE TABLE».

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

(2) Убедитесь, что ограничения внешнего ключа действительно проверяются, выполнив "SET foreign_key_checks = 'ON'"

(3) Добавьте «ОБНОВЛЕНИЕ КАСКАДА» в декларацию внешнего ключа.

Примечание: убедитесь, что каскадирование - это то поведение, которое вам нужно. Есть и другие варианты ...

1 голос
/ 19 декабря 2008

проблема, скорее всего, в том, что questions.p_id и users.p_id не определены как INT NOT NULL. для работы внешних ключей определение столбцов с обеих сторон внешнего ключа должно точно совпадать, за исключением auto_increment и default.

1 голос
/ 19 декабря 2008

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

http://forums.mysql.com/read.php?22,19755,43805

Автор, Эдвин Дандо, говорит:

обе таблицы должны быть INNODB. Поле внешнего ключа должно иметь индекс. Поле ключевого ключа и поле, на которое ссылаются, должны быть одного типа (я использую только целое число), и после нескольких часов боли они должны быть НЕ ПОДПИСАНЫ.

0 голосов
/ 17 сентября 2014

Что ж, я полагаю, что опция «Пропустить создание FORIEN KEYS» отмечена, это может произойти в разделе «Опции» процесса «Форвард инжиниринг».

Forward Engineering - Options section

0 голосов
/ 30 июня 2013

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

Если вы объявите свои внешние ключи как таковые

id  INTEGER UNSIGNED    REFERENCES    A_Table(id)

Тогда внешний ключ, кажется, игнорируется, чтобы применить ограничение без (по-видимому) необходимости использования какой-либо из команд SET, используйте следующее объявление.

id  INTEGER UNSIGNED,
CONSTRAINT fk_id  FOREIGN KEY (id)  REFERENCES A_Table(id)

Этот способ решил проблему для меня. Не уверен, почему, как многие говорят, первое объявление является лишь сокращением ко второму варианту.

0 голосов
/ 21 сентября 2011

Я думаю, что некоторые из тех, у кого возникла эта проблема, могут начинать с некоторых примеров баз данных, представленных на веб-сайте ORACLE для MYSQL (например, sakila DB). Не забудьте "включить ограничения внешнего ключа" в конце вашего скрипта (например, в начале скрипта DB sakila они выключены)

SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL';

создайте свои таблицы здесь

тогда не забывайте это:

SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
0 голосов
/ 19 декабря 2008

Как уже отмечалось, ваша таблица должна быть InnoDB для применения ограничений FK.

Я столкнулся с «Не могу создать таблицу» только в том случае, когда я пытаюсь создать ограничение внешнего ключа, где мой локальный столбец отличается от внешнего столбца.

...