Dans ce système, nous stockons des produits, des images de produits (il peut y avoir plusieurs images pour un produit) et une image par défaut pour un produit. La base de données:
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;
comme vous pouvez le voir, products_pictures.PRODUCT_ID -> products.ID
et products.DEFAULT_PICTURE_ID -> products_pictures.ID
, donc une référence de cycle. Est-ce que c'est bon?
Non, ça ne va pas. Les références circulaires entre les tables sont en désordre. Voir cet article (décennal): SQL By Design: la référence circulaire
Certains SGBD peuvent les gérer, avec une attention particulière, mais MySQL aura des problèmes.
Le premier choix est, selon votre conception, de rendre l’un des deux FK nulles. Cela vous permet de résoudre le problème du poulet et des œufs (dans quel tableau dois-je d'abord insérer?).
Il y a un problème avec votre code. Cela permettra à un produit d’avoir une image par défaut, l’image référencant un autre produit!
Pour interdire une telle erreur, votre contrainte FK devrait être:
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
Cela nécessite une contrainte/index UNIQUE
dans la table products_pictures
sur (product_id, id)
pour que le FK ci-dessus soit défini et fonctionne correctement.
Une autre approche consiste à supprimer la colonne Default_Picture_ID
de la table product
et à ajouter une colonne IsDefault BIT
à la table picture
. Le problème avec cette solution est de ne permettre à une seule image par produit d’avoir ce bit et à toutes les autres de l’avoir. Dans SQL-Server (et je pense dans Postgres), cela peut être fait avec un index partiel:
CREATE UNIQUE INDEX is_DefaultPicture
ON products_pictures (Product_ID)
WHERE IsDefault = 1 ;
Mais MySQL n'a pas cette fonctionnalité.
Une troisième approche, qui vous permet même de définir les deux colonnes FK en tant que NOT NULL
, consiste à utiliser des contraintes pouvant être différées. Cela fonctionne dans PostgreSQL et je pense dans Oracle. Vérifiez cette question et la réponse de @Erwin: Contrainte de clé étrangère complexe dans SQLAlchemy (le Toutes les colonnes de clé NOT NULL Part).
Les contraintes dans MySQL ne peuvent pas être différées.
Une quatrième approche (que je trouve la plus propre) consiste à supprimer la colonne Default_Picture_ID
et à ajouter une autre table. Aucun chemin circulaire dans les contraintes FK et toutes les colonnes FK ne sera NOT NULL
avec cette 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)
Cela nécessitera également un UNIQUE
contrainte/index dans la table products_pictures
sur (product_id, id)
comme dans la solution 1.
Pour résumer, avec MySQL, vous avez deux options:
option 1 (une colonne FK nullable) avec la correction ci-dessus pour appliquer correctement l'intégrité
option 4 (pas de colonnes FK nullables)
ceci est juste une suggestion mais si possible créer une table de jointure entre cette table pourrait être utile au suivi
product_productcat_join
------------------------
ID(PK)
ProductID(FK)- product table primary key
PictureID(FK) - category table primary key
Le seul problème que vous allez rencontrer, c'est quand vous faites des insertions. Lequel insérez-vous en premier?
Avec cela, vous devrez faire quelque chose comme:
Encore une fois, supprimer ne sera pas amusant.
John ne fait rien de mal à votre travail, mais utiliser PK-FK vous aide en fait à normaliser vos données en supprimant les données redondantes. Qui a des avantages fantastiques de