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

时间:2020-03-06 14:31:46  来源:igfitidea点击:

如果我在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的检查约束也必须考虑所有子类型是否会成为可维护性的噩梦?

解决方案

如果使用MySQL 5.x,则可以将触发器用于这些类型的约束。

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

我们可以只将外键从一个主键添加到另一个主键。因为PK必须是唯一的,所以我们会自动获得一对一的关系。

如果摆脱了product-dvd-id和product-cd-id,并使用product-id作为所有三个表的主键,则至少可以确保没有两个DVD或者两个CD使用同一产品-ID。另外,可以减少跟踪的ID。

我们可能需要在products表中使用某种类型列。

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

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

为了确保产品是cd或者dvd,我将添加一个type列并将其作为主键的一部分。在派生列中,为该类型添加检查约束。在示例中,我将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 ;