在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.ID
和products.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