Изменение первичного ключа MySQL при наличии ограничений внешнего ключа

У меня есть две уже существующие таблицы, которые выглядят (частично) примерно так:

CREATE TABLE parent (
    old_pk CHAR(8) NOT NULL PRIMARY KEY
) ENGINE=InnoDB;

CREATE TABLE child (
    parent_key CHAR(8),
    FOREIGN KEY (parent_key) REFERENCES parent(old_pk)
        ON UPDATE CASCADE ON DELETE CASCADE
) ENGINE=InnoDB;

Я хочу добавить новое целое число с автоинкрементом id столбец к parent и использовать его в качестве первичного ключа, сохраняя при этом old_pk в качестве уникального ключа и позволяя другим таблицам, таким как child ссылаться на него в противоречиях внешних ключей. К сожалению, просто говоря ALTER TABLE parent DROP PRIMARY KEY не работает:

Код ошибки: 1025

Ошибка при переименовании "./data/#sql-4013_70f5e" в "./data/parent" (номер ошибки: 150)

Некоторые прибегают к поиску в Google, что это связано с существующей ссылкой на внешний ключ от child, По сути, мне нужен способ сказать MySQL "использовать этот другой столбец в качестве первичного ключа, но не забывайте об уникальном ключе оригинала". Есть ли способ сделать это, кроме как просто отбросить ключевые ограничения из child и восстановить их потом?

Предположим, что я должен изменить таблицы на месте, а не создавать копии с теми же данными и менять их позже. Я пытался использовать SET FOREIGN_KEY_CHECKS = 0 до изменения таблицы, но это, похоже, не помогает.

2 ответа

Решение

Добавьте индекс (это может быть даже УНИКАЛЬНО) к old_pk перед удалением первичного ключа:

mysql> CREATE TABLE parent (
    ->     old_pk CHAR(8) NOT NULL PRIMARY KEY
    -> ) ENGINE=InnoDB;
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE child (
    ->     parent_key CHAR(8),
    ->     FOREIGN KEY (parent_key) REFERENCES parent(old_pk)
    ->         ON UPDATE CASCADE ON DELETE CASCADE
    -> ) ENGINE=InnoDB;
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO parent VALUES ('a');
Query OK, 1 row affected (0.01 sec)

mysql> CREATE INDEX old_pk_unique ON parent (old_pk);
Query OK, 1 row affected (0.01 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE parent DROP PRIMARY KEY;
Query OK, 1 row affected (0.01 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> INSERT INTO child VALUES ('a');
Query OK, 1 row affected (0.00 sec)

mysql> SHOW CREATE TABLE parent;
+--------+------------------------------------------------------------------------------------------------------------------------------+
| Table  | Create Table                                                                                                                 |
+--------+------------------------------------------------------------------------------------------------------------------------------+
| parent | CREATE TABLE `parent` (
  `old_pk` char(8) NOT NULL,
  KEY `old_pk_unique` (`old_pk`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+--------+------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> INSERT INTO child VALUES ('b');
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`test/child`, CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_key`) REFERENCES `parent` (`old_pk`) ON DELETE CASCADE ON UPDATE CASCADE)

mysql> INSERT INTO parent VALUES ('b');
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO child VALUES ('b');
Query OK, 1 row affected (0.01 sec)

mysql> ALTER TABLE parent ADD id INT;
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> UPDATE parent SET id = 1 WHERE old_pk = 'a';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> UPDATE parent SET id = 2 WHERE old_pk = 'b';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> ALTER TABLE parent ADD PRIMARY KEY (id);
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> SHOW CREATE TABLE parent;
+--------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table  | Create Table                                                                                                                                                                             |
+--------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| parent | CREATE TABLE `parent` (
  `old_pk` char(8) NOT NULL,
  `id` int(11) NOT NULL default '0',
  PRIMARY KEY  (`id`),
  KEY `old_pk_unique` (`old_pk`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+--------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

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

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

Запрос: Если вы отрицаете меня, пожалуйста, оставьте также короткий комментарий. В течение примерно 10 лет я работал с реляционными базами данных. Единственные знакомые мне люди, которые используют ограничения проверки, работают в системах, которые не масштабируются. Если это люди, которые осуждают меня, я могу жить с этим. Но если вы работаете в масштабируемой системе и проверочные ограничения являются для вас нормой, я хотел бы знать, кто вы, поэтому я могу немного почитать, чтобы увидеть, что я пропустил.

Другие вопросы по тегам