Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In SQL, is it OK for two tables to refer to each other?

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?

like image 699
John Smith Avatar asked May 04 '12 09:05

John Smith


People also ask

Can 2 tables reference each other?

The short answer is “Yes”; the right answer is that your schema is not normalized properly. This is self-referencing design is covered in Chris Date's last book (Database Design & Relational Theory: Normal Forms and All That Jazz; O'Reilly 2012; ISBN 978-1-449-32801-6).

Can we refer same table in SQL?

FOREIGN KEY constraints can reference another column in the same table, and is referred to as a self-reference. A FOREIGN KEY constraint specified at the column level can list only one reference column. This column must have the same data type as the column on which the constraint is defined.

How do you reference two tables in SQL?

A foreign key is a key used to link two tables together. This is sometimes also called as a referencing key. A Foreign Key is a column or a combination of columns whose values match a Primary Key in a different table.

Can two SQL tables have foreign keys to each other?

When you create two tables that are related to each other, they are often related by a column in one table referencing the primary key of the other table - that column is called the "foreign key".


1 Answers

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.


Option 1

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.


Option 2

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.


Option 3

This approach, 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.


Option 4

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

like image 100
ypercubeᵀᴹ Avatar answered Oct 03 '22 04:10

ypercubeᵀᴹ