在SQL中,两个表是否可以相互引用? [英] In SQL, is it OK for two tables to refer to each other?

查看:212
本文介绍了在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 ,所以一个循环引用。是否可以?

解决方案

不,不行。表之间的循环引用是混乱的。请参阅(十年前)文章: SQL设计:循环引用



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






第一个选择是,作为您的设计,使两个FK中的一个可以为空。这允许你解决鸡和鸡蛋的问题(该表应该我第一次插入?)。



有一个问题,虽然与您的代码。



为了禁止这样的错误,你的FK约束应该是:

  CONSTRAINT FK_products_1 
FOREIGN KEY(id,default_picture_id)
REFERENCES products_pictures(product_id,id)
ON DELETE RESTRICT --- SET NULL选项将
ON UPDATE RESTRICT ---导致其他问题

这将需要(product_id,id)中的 products_pictures 中的






另一种方法是删除表格中添加 Default_Picture_ID 列,并添加 IsDefault BIT 图片表中的列。这个解决方案的问题是如何允许只有一个图片每个产品有该位,所有其他人使它关闭。在SQL Server(我认为在Postgres)这可以用部分索引:

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

但是MySQL没有这样的功能。






第三种方法是使用deferrable约束,即使两个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)

这还需要 products_pictures 在<$ c $上的 UNIQUE 约束/索引






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




  • 选项1(可空的FK列)


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



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?

解决方案

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

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


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?).

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:

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.


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:

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

But MySQL has no such feature.


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).

Constraints in MySQL cannot be deferrable.


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:

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.


To summarize, with MySQL you have two options:

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

  • option 4 (no nullable FK columns)

这篇关于在SQL中,两个表是否可以相互引用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆