8

我有两个已经存在的表,看起来(部分)大致如下:

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;

我想添加一个新的自动递增整数idparent并将其用作主键,同时仍保留old_pk为唯一键并允许其他表child在外键约束中引用它。不幸的是,简单地说ALTER TABLE parent DROP PRIMARY KEY是行不通的:

错误代码:1025

将“./data/#sql-4013_70f5e”重命名为“./data/parent”时出错(错误号:150)

一些谷歌搜索表明这是由于现有的外键引用来自child. 本质上,我需要一种方法来告诉 MySQL“使用另一列作为主键,但不要忘记原始列的唯一键”。child除了删除关键约束并在之后恢复它们 之外,有什么方法可以做到这一点?

假设我必须就地更改表,而不是创建具有相同数据的副本并稍后交换它们。我SET FOREIGN_KEY_CHECKS = 0在更改表格之前尝试过使用,但它似乎没有帮助。

4

2 回答 2

8

在删除主键之前向 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)
于 2009-08-05T06:16:44.040 回答
-4

我会用一个可能不受欢迎的建议来权衡这个问题。不要在数据库中使用外键约束 - 根据需要在存储过程中通过 TSQL 强制执行唯一键和其他约束。根据我的经验,在缩放环境中检查约束很少使用。

我对可能随之而来的反对意见/讨论持开放态度。我并不是说这个建议是正确的,只是说这是我工作过的商店的普遍看法。

一个请求:如果你对我投了反对票,也请留下一个简短的评论。在我使用关系数据库的 10 年左右的时间里,我认识的唯一使用检查约束的人是在不大规模的系统上工作。如果那些人反对我,那么我可以忍受。但是,如果您正在使用扩展系统并且检查约束对您来说是常态,我想知道您是谁,这样我就可以做一些阅读以了解我错过了什么。

于 2009-07-28T18:15:59.263 回答