在此系统中,我们存储产品,产品图像(产品可能有许多图像),以及产品的默认图像。数据库:
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;
正如您所见,products_pictures.PRODUCT_ID -> products.ID
和products.DEFAULT_PICTURE_ID -> products_pictures.ID
,所以是循环参考。可以吗?
答案 0 :(得分:42)
不,不行。表之间的循环引用很混乱。见这篇(十年前的)文章: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
这需要在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 ;
但MySQL没有这样的功能。
第三种方法,即使将两个FK列定义为NOT NULL
,也可以使用可延迟约束。这适用于PostgreSQL,我想在Oracle中。请查看@Erwin的问题和答案:Complex foreign key constraint in 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)
这也需要在UNIQUE
上的products_pictures
表中(product_id, id)
约束/索引,与解决方案1中一样。
总结一下,使用MySQL有两种选择:
选项1(可以为空的FK列)并进行上述更正以正确执行完整性
选项4(无可空的FK列)
答案 1 :(得分:4)
这只是建议,但如果可能的话,在此表之间创建一个联接表可能有助于跟踪
product_productcat_join
------------------------
ID(PK)
ProductID(FK)- product table primary key
PictureID(FK) - category table primary key
答案 2 :(得分:2)
您将遇到的唯一问题是何时进行插入。 你先插入哪一个?
有了这个,你将不得不做类似的事情:
同样,删除也不会很有趣。
答案 3 :(得分:1)
在另一个表中,您可以仅保留该字段而没有外键约束。 在某些情况下,如果您要使用较小的表进行处理,但要根据处理结果连接到较大的表,则很有用。
例如,如果您添加一个product_location表,其中包含国家,地区,城市,地址以及经度和纬度信息。在某些情况下,您可能希望在地图上的圆圈内显示产品。
答案 4 :(得分:0)
John你做的不是什么坏事,但使用PK-FK实际上有助于通过删除多余的重复数据来规范化数据。
具有一些奇妙的优势答案 5 :(得分:-2)
这不是循环引用,即pk-fk