Joh*_*ith 32 mysql database database-design
在此系统中,我们存储产品,产品图像(产品可能有许多图像),以及产品的默认图像.数据库:
CREATE TABLE `products` (
`ID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`NAME` varchar(255) NOT NULL,
`DESCRIPTION` text NOT NULL,
`ENABLED` tinyint(1) NOT NULL DEFAULT '1',
`DATEADDED` datetime NOT NULL,
`DEFAULT_PICTURE_ID` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`ID`),
KEY `Index_2` (`DATEADDED`),
KEY `FK_products_1` (`DEFAULT_PICTURE_ID`),
CONSTRAINT `FK_products_1` FOREIGN KEY (`DEFAULT_PICTURE_ID`) REFERENCES `products_pictures` (`ID`) ON DELETE SET NULL ON UPDATE SET NULL
) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8;
CREATE TABLE `products_pictures` (
`ID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`IMG_PATH` varchar(255) NOT NULL,
`PRODUCT_ID` int(10) unsigned NOT NULL,
PRIMARY KEY (`ID`),
KEY `FK_products_pictures_1` (`PRODUCT_ID`),
CONSTRAINT `FK_products_pictures_1` FOREIGN KEY (`PRODUCT_ID`) REFERENCES `products` (`ID`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
Run Code Online (Sandbox Code Playgroud)
你可以看到,products_pictures.PRODUCT_ID -> products.ID
并且products.DEFAULT_PICTURE_ID -> products_pictures.ID
,这样一个循环引用.可以吗?
ype*_*eᵀᴹ 45
不,不行.表之间的循环引用很混乱.请参阅这篇(十年前的)文章:SQL By Design:The Circular Reference
有些DBMS可以处理这些问题,并且需要特别小心,但MySQL会遇到问题.
作为您的设计,第一个选择是使两个FK中的一个可以为空.这可以让你解决鸡蛋问题(我应该先将哪个表插入?).
您的代码存在问题.它将允许产品具有默认图片,其中该图片将引用另一个产品!
要禁止此类错误,您的FK约束应为:
CONSTRAINT FK_products_1
FOREIGN KEY (id, default_picture_id)
REFERENCES products_pictures (product_id, id)
ON DELETE RESTRICT --- the SET NULL options would
ON UPDATE RESTRICT --- lead to other issues
Run Code Online (Sandbox Code Playgroud)
这将需要一个UNIQUE
约束/表的索引products_pictures
上(product_id, id)
的被定义上述FK和正常工作.
另一种方法是Default_Picture_ID
从product
表中删除列,并在表中添加一IsDefault BIT
列picture
.这个解决方案的问题是如何只允许每个产品一张图片打开,其他所有产品都关闭.在SQL-Server中(我认为在Postgres中)可以使用部分索引来完成:
CREATE UNIQUE INDEX is_DefaultPicture
ON products_pictures (Product_ID)
WHERE IsDefault = 1 ;
Run Code Online (Sandbox Code Playgroud)
但MySQL没有这样的功能.
第三种方法,它允许您甚至将两个FK列定义为NOT NULL
使用可延迟约束.这适用于PostgreSQL,我想在Oracle中.通过@Erwin检查这个问题和答案:SQLAlchemy中的复杂外键约束(所有键列NOT NULL部分).
MySQL中的约束无法推迟.
第四种方法(我认为最干净)是删除Default_Picture_ID
列并添加另一个表.FK约束中没有圆形路径,所有FK列都将NOT NULL
使用此解决方案:
product_default_picture
----------------------
product_id NOT NULL
default_picture_id NOT NULL
PRIMARY KEY (product_id)
FOREIGN KEY (product_id, default_picture_id)
REFERENCES products_pictures (product_id, id)
Run Code Online (Sandbox Code Playgroud)
这也将需要UNIQUE
在表约束/索引products_pictures
对(product_id, id)
在溶液中1.
总而言之,使用MySQL,您有两种选择:
选项1(可空的FK列),具有上述更正,以正确执行完整性
选项4(没有可空的FK列)