在SQL中,两个表可以互相引用吗?

在此系统中,我们存储产品,产品图像(产品可能有多个图像)以及产品的默认图像。 数据库:

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.IDproducts.DEFAULT_PICTURE_ID -> products_pictures.ID ,所以是一个循环引用。 可以吗?

不,不行。 表格之间的循环引用是混乱的。 看这(十年)文章: SQLdevise:循环参考

一些DBMS可以处理这些,并且特别谨慎,但是MySQL会有问题。


作为你的devise,第一select是使两个FK中的一个可以为空。 这可以让你解决鸡与鸡蛋的问题(我应该先插入哪个表?)。

尽pipe你的代码有问题。 这将允许产品有一个默认的图片,其中的图片将引用另一种产品!

要禁止这样的错误,你的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 

这将要求(product_id, id)上的products_pictures中的上述FK的UNIQUE约束/索引被定义并且正常工作。


另一种方法是从product表中删除Default_Picture_ID列,并在picture表中添加一个IsDefault BIT列。 这个解决scheme的问题是,如何让每个产品只有一张图片和其他所有图片都可以closures。 在SQL Server中(我认为在Postgres中)这可以通过部分索引来完成:

 CREATE UNIQUE INDEX is_DefaultPicture ON products_pictures (Product_ID) WHERE IsDefault = 1 ; 

但MySQL没有这样的function。


第三种方法,即使将FK列定义为NOT NULL也可以使用可延迟的约束。 这在PostgreSQL中工作,我想在Oracle中。 通过@Erwin检查这个问题和答案: SQLAlchemy中的复杂外键约束 ( 所有键列NOT NULL部分)。

MySQL中的约束不能推迟。


第四种方法(我发现最干净的)是删除Default_Picture_ID列并添加另一个表。 FK约束中没有圆形path,所有的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) 

这也需要解决scheme1中的(product_id, id)上的UNIQUE约束/索引。


总而言之,用MySQL你有两个select:

  • 选项1(可为空的FK列)与上述更正一起强制执行正确的完整性

  • 选项4(不可为空的FK列)

这只是一个build议,但如果可能的话,在这个表之间创build一个连接表可能会有助于跟踪

 product_productcat_join ------------------------ ID(PK) ProductID(FK)- product table primary key PictureID(FK) - category table primary key 

你会遇到的唯一问题是当你插入。 你首先插入哪一个?

有了这个,你将不得不做一些事情:

  • 插入带有默认图片的产品
  • 用新创build的产品ID插入图片
  • 更新产品以将默认图片设置为您刚插入的图片。

再次,删除不会很有趣。

约翰你做什么没有什么不好,但使用PK-FK实际上有助于通过消除多余的重复数据来标准化你的数据。 这有一些奇妙的优势

  • 由于消除了相同数据的重复存储位置,提高了数据完整性
  • 减less了locking争用并提高了多用户并发性
  • 较小的文件

这不是循环的参考,那就是pk-fk