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

时间:2021-07-30 00:21:34

In this system, we store products, images of products (there can be many image for a product), and a default image for a product. The database:

在这个系统中,我们存储产品、产品映像(一个产品可以有多个映像)和产品的默认映像。数据库:

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;

as you can see, products_pictures.PRODUCT_ID -> products.ID and products.DEFAULT_PICTURE_ID -> products_pictures.ID, so a cycle reference. Is it OK?

如您所见,products_pictures。PRODUCT_ID - >产品。ID和产品。DEFAULT_PICTURE_ID - > products_pictures。ID,循环引用。它是好吗?

5 个解决方案

#1


38  

No, it's not OK. Circular references between tables are messy. See this (decade old) article: SQL By Design: The Circular Reference

不,它不是好的。表之间的循环引用很混乱。参见这个(10年旧)文章:SQL的设计:循环引用。

Some DBMS can handle these, and with special care, but MySQL will have issues.

有些DBMS能够处理这些问题,并且特别小心,但是MySQL会有问题。


The first choice is, as your design, to make one of the two FKs nullable. This allows you to solve the chicken-and-egg problem (which table should I first Insert into?).

第一个选择是,按照您的设计,使两个FKs中的一个成为空。这使您可以解决鸡和蛋的问题(我应该首先插入哪个表?)

There is a problem though with your code. It will allow a product to have a default picture where that picture will be referencing another product!

但是您的代码有一个问题。它将允许一个产品有一个默认图片,图片将引用另一个产品!

To disallow such an error, your FK constraint should be:

为了避免出现这种错误,您的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

This will require a UNIQUE constraint/index in table products_pictures on (product_id, id) for the above FK to be defined and work properly.

这将需要在表products_pictures (product_id, id)中定义唯一的约束/索引,以便定义上述FK并正常工作。


Another approach is to remove the Default_Picture_ID column form the product table and add an IsDefault BIT column in the picture table. The problem with this solution is how to allow only one picture per product to have that bit on and all others to have it off. In SQL-Server (and I think in Postgres) this can be done with a partial index:

另一种方法是删除产品表中的Default_Picture_ID列,并在图片表中添加一个IsDefault位列。这个解决方案的问题是,如何允许每个产品只有一个图片,而所有其他图片都有这个位。

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

But MySQL has no such feature.

但MySQL没有这样的功能。


A third approach, which allows you to even have both FK columns defined as NOT NULL is to use deferrable constraints. This works in PostgreSQL and I think in Oracle. Check this question and the answer by @Erwin: Complex foreign key constraint in SQLAlchemy (the All key columns NOT NULL Part).

第三种方法是使用可延迟的约束,这允许您将两个FK列定义为NOT NULL。这在PostgreSQL中有效,我认为在Oracle中也是如此。检查这个问题和@Erwin: SQLAlchemy中复杂的外键约束(所有键列都不是NULL部分)的答案。

Constraints in MySQL cannot be deferrable.

MySQL中的约束不能延迟。


A fourth approach (which I find cleanest) is to remove the Default_Picture_ID column and add another table. No circular path in the FK constraints and all FK columns will be NOT NULL with this solution:

第四种方法(我觉得最干净)是删除Default_Picture_ID列并添加另一个表。在FK约束条件下,所有FK列的循环路径都不为零。

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)

This will also require a UNIQUE constraint/index in table products_pictures on (product_id, id) as in solution 1.

这还需要与解决方案1一样,在表products_pictures (product_id, id)中使用唯一的约束/索引。


To summarize, with MySQL you have two options:

总之,使用MySQL,您有两个选项:

  • option 1 (a nullable FK column) with the correction above to enforce integrity correctly

    选项1(一个可空的FK列)与上面的校正以正确地执行完整性

  • option 4 (no nullable FK columns)

    选项4(无空FK列)

#2


4  

this is just suggestion but if possible create one join table between this table might be helpfull to tracking

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

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

#3


2  

The only issue you're going to encounter is when you do inserts. Which one do you insert first?

您将遇到的唯一问题是何时进行插入。你先插入哪一个?

With this, you will have to do something like:

有了这个,你将不得不做以下事情:

  • Insert product with null default picture
  • 插入带有空默认图片的产品
  • Insert picture(s) with the newly created product ID
  • 使用新创建的产品ID插入图片
  • Update the product to set the default picture to one that you just inserted.
  • 更新产品,将默认图片设置为您刚刚插入的图片。

Again, deleting will not be fun.

同样,删除也不会很有趣。

#4


0  

John what your doing isnt anything bad but using PK-FK actually helps with normalizing your data by removing redundant repeating data. Which has some fantastic advantages from

John,你所做的并不是什么坏事,但是使用PK-FK可以通过删除冗余重复数据来帮助数据正常化。哪一种有不可思议的优势

  • Improved data integrity owing to the elimination of duplicate storage locations for the same data
  • 由于消除了相同数据的重复存储位置,改进了数据完整性
  • Reduced locking contention and improved multiple-user concurrency
  • 减少锁争用并改进多用户并发性
  • Smaller files
  • 小的文件

#5


-1  

that is not a cyclic ref, that is pk-fk

那不是一个循环ref,那是pk-fk

#1


38  

No, it's not OK. Circular references between tables are messy. See this (decade old) article: SQL By Design: The Circular Reference

不,它不是好的。表之间的循环引用很混乱。参见这个(10年旧)文章:SQL的设计:循环引用。

Some DBMS can handle these, and with special care, but MySQL will have issues.

有些DBMS能够处理这些问题,并且特别小心,但是MySQL会有问题。


The first choice is, as your design, to make one of the two FKs nullable. This allows you to solve the chicken-and-egg problem (which table should I first Insert into?).

第一个选择是,按照您的设计,使两个FKs中的一个成为空。这使您可以解决鸡和蛋的问题(我应该首先插入哪个表?)

There is a problem though with your code. It will allow a product to have a default picture where that picture will be referencing another product!

但是您的代码有一个问题。它将允许一个产品有一个默认图片,图片将引用另一个产品!

To disallow such an error, your FK constraint should be:

为了避免出现这种错误,您的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

This will require a UNIQUE constraint/index in table products_pictures on (product_id, id) for the above FK to be defined and work properly.

这将需要在表products_pictures (product_id, id)中定义唯一的约束/索引,以便定义上述FK并正常工作。


Another approach is to remove the Default_Picture_ID column form the product table and add an IsDefault BIT column in the picture table. The problem with this solution is how to allow only one picture per product to have that bit on and all others to have it off. In SQL-Server (and I think in Postgres) this can be done with a partial index:

另一种方法是删除产品表中的Default_Picture_ID列,并在图片表中添加一个IsDefault位列。这个解决方案的问题是,如何允许每个产品只有一个图片,而所有其他图片都有这个位。

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

But MySQL has no such feature.

但MySQL没有这样的功能。


A third approach, which allows you to even have both FK columns defined as NOT NULL is to use deferrable constraints. This works in PostgreSQL and I think in Oracle. Check this question and the answer by @Erwin: Complex foreign key constraint in SQLAlchemy (the All key columns NOT NULL Part).

第三种方法是使用可延迟的约束,这允许您将两个FK列定义为NOT NULL。这在PostgreSQL中有效,我认为在Oracle中也是如此。检查这个问题和@Erwin: SQLAlchemy中复杂的外键约束(所有键列都不是NULL部分)的答案。

Constraints in MySQL cannot be deferrable.

MySQL中的约束不能延迟。


A fourth approach (which I find cleanest) is to remove the Default_Picture_ID column and add another table. No circular path in the FK constraints and all FK columns will be NOT NULL with this solution:

第四种方法(我觉得最干净)是删除Default_Picture_ID列并添加另一个表。在FK约束条件下,所有FK列的循环路径都不为零。

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)

This will also require a UNIQUE constraint/index in table products_pictures on (product_id, id) as in solution 1.

这还需要与解决方案1一样,在表products_pictures (product_id, id)中使用唯一的约束/索引。


To summarize, with MySQL you have two options:

总之,使用MySQL,您有两个选项:

  • option 1 (a nullable FK column) with the correction above to enforce integrity correctly

    选项1(一个可空的FK列)与上面的校正以正确地执行完整性

  • option 4 (no nullable FK columns)

    选项4(无空FK列)

#2


4  

this is just suggestion but if possible create one join table between this table might be helpfull to tracking

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

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

#3


2  

The only issue you're going to encounter is when you do inserts. Which one do you insert first?

您将遇到的唯一问题是何时进行插入。你先插入哪一个?

With this, you will have to do something like:

有了这个,你将不得不做以下事情:

  • Insert product with null default picture
  • 插入带有空默认图片的产品
  • Insert picture(s) with the newly created product ID
  • 使用新创建的产品ID插入图片
  • Update the product to set the default picture to one that you just inserted.
  • 更新产品,将默认图片设置为您刚刚插入的图片。

Again, deleting will not be fun.

同样,删除也不会很有趣。

#4


0  

John what your doing isnt anything bad but using PK-FK actually helps with normalizing your data by removing redundant repeating data. Which has some fantastic advantages from

John,你所做的并不是什么坏事,但是使用PK-FK可以通过删除冗余重复数据来帮助数据正常化。哪一种有不可思议的优势

  • Improved data integrity owing to the elimination of duplicate storage locations for the same data
  • 由于消除了相同数据的重复存储位置,改进了数据完整性
  • Reduced locking contention and improved multiple-user concurrency
  • 减少锁争用并改进多用户并发性
  • Smaller files
  • 小的文件

#5


-1  

that is not a cyclic ref, that is pk-fk

那不是一个循环ref,那是pk-fk