MySQL外键 - 如何跨表强制执行一对一?

时间:2008-09-22 08:29:30

标签: mysql

如果我在MySQL中有一个代表基类的表,并且我有一堆表来表示派生类中的字段,每个表都用外键引用回基表,有什么办法吗?让MySQL强制派生表和基表之间的一对一关系,还是必须在代码中完成?

使用以下快速'n'脏架构作为示例,有没有办法让MySQL确保product_cd和product_dvd中的行不能共享相同的product_id?有没有更好的方法来设计架构以允许数据库强制执行此关系,或者它是否根本不可能?

CREATE TABLE IF NOT EXISTS `product` (
    `product_id` int(10) unsigned NOT NULL auto_increment,
    `product_name` varchar(50) NOT NULL,
    `description` text NOT NULL,
    PRIMARY KEY  (`product_id`)
) ENGINE = InnoDB;

CREATE TABLE `product_cd` (
    `product_cd_id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
    `product_id` INT UNSIGNED NOT NULL ,
    `artist_name` VARCHAR( 50 ) NOT NULL ,
    PRIMARY KEY ( `product_cd_id` ) ,
    INDEX ( `product_id` )
) ENGINE = InnoDB;

ALTER TABLE `product_cd` ADD FOREIGN KEY ( `product_id` ) 
    REFERENCES `product` (`product_id`) 
    ON DELETE RESTRICT ON UPDATE RESTRICT ;

CREATE TABLE `product_dvd` (
    `product_dvd_id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
    `product_id` INT UNSIGNED NOT NULL ,
    `director` VARCHAR( 50 ) NOT NULL ,
    PRIMARY KEY ( `product_dvd_id` ) ,
    INDEX ( `product_id` )
) ENGINE = InnoDB;

ALTER TABLE `product_dvd` ADD FOREIGN KEY ( `product_id` ) 
    REFERENCES `product` (`product_id`) 
    ON DELETE RESTRICT ON UPDATE RESTRICT ;

@ Skliwz,是否可以提供有关如何使用触发器来提供此约束的更多详细信息?

@ boes,听起来不错。在您有孩子的情况下,它如何运作?例如,如果我们添加product_movie并使product_dvd成为product_movie的子项?使product_dvd的检查约束也必须考虑所有子类型,这是一个可维护性的噩梦吗?

6 个答案:

答案 0 :(得分:8)

通过在外键列上定义唯一约束,可以实现1:0-1或1:1的关系,因此只能存在一个组合。通常这将是子表的主键。

如果FK位于引用表的主键或唯一键上,它会将它们约束为父表中存在的值,并且列上的唯一约束将它们限制为唯一性。这意味着子表只能具有与受约束列中的父对应的值,并且每行必须具有唯一值。这样做会强制子表最多只有一行对应于父记录。

答案 1 :(得分:4)

如果您摆脱了 product-dvd-id product-cd-id ,并使用 product-id 作为主要内容对于所有三个表的密钥,您至少可以确保没有两张DVD或没有两张CD使用相同的 product-id 。此外,还会有更少的ID来跟踪。

您可能需要在products表中使用某种 type 列。

答案 2 :(得分:3)

您只需将一个主键的外键添加到另一个主键即可。因为PK必须是唯一的,所以你自动获得一对一的关系。

答案 3 :(得分:3)

要确保产品是CD或DVD,我会添加一个类型列并使其成为主键的一部分。在派生列中,为类型添加检查约束。在示例中,我将cd设置为1,您可以为每个派生表生成dvd = 2等等。

CREATE TABLE IF NOT EXISTS `product` (
`product_id` int(10) unsigned NOT NULL auto_increment,
'product_type' int not null,
`product_name` varchar(50) NOT NULL,
`description` text NOT NULL,
PRIMARY KEY (`product_id`, 'product_type')
) ENGINE = InnoDB;

CREATE TABLE `product_cd` (
`product_id` INT UNSIGNED NOT NULL ,
'product_type' int not null default(1) check ('product_type' = 1)
`artist_name` VARCHAR( 50 ) NOT NULL ,
PRIMARY KEY ( `product_id`, 'product_type' ) ,
) ENGINE = InnoDB;

ALTER TABLE `product_cd` ADD FOREIGN KEY ( `product_id`, 'product_type' ) 
REFERENCES `product` (`product_id`, 'product_type') 
ON DELETE RESTRICT ON UPDATE RESTRICT ;

答案 4 :(得分:1)

如果您使用MySQL 5.x,则可以使用触发器来实现这些约束。

另一个(次优)选项是使用父表中的“type”列静默忽略重复,并能够选择正确的“扩展表”。

答案 5 :(得分:1)

维持一对一关系的唯一有效方法是将外键列设置为 UNIQUE ;这样我们就可以确保只为它创建一条记录。如果您尝试违反此约束,则会出现以下错误(MySQL 5.1):

`1 error(s) saving changes to table testdb`.table4: INSERT INTO testdb.table4 (idtable4, label, table3_idtable3) VALUES (0, 'soso', 0) 1062: Duplicate entry '0' for key 'table3_idtable3_UNIQUE' Rollback complete