web-dev-qa-db-fra.com

MySQL - Contraintes de clés étrangères conditionnelles

J'ai le tableau comments suivant dans mon application:

comments
--------
id           INT
foreign_id   INT
model        TEXT
comment_text TEXT
...

l'idée de ce tableau est de stocker des commentaires pour diverses parties de mon application - il peut stocker des commentaires pour un article de blog, à savoir:

1|34|blogpost|lorem ipsum...

image de l'utilisateur:

2|12|picture|lorem ipsum...

etc.

maintenant, existe-t-il un moyen de forcer la contrainte FOREIGN KEY sur ces données?

c'est-à-dire quelque chose comme ceci dans le tableau des commentaires:

FOREIGN KEY (`foreign_id`) REFERENCES blogposts (`id`)
-- but only when model='blogpost'
58
grzes

Vous essayez de faire une conception qui s'appelle Associations polymorphes . En d'autres termes, la clé étrangère peut référencer des lignes dans n'importe laquelle de plusieurs tables associées.

Mais une contrainte de clé étrangère doit référencer exactement une table. Vous ne pouvez pas déclarer une clé étrangère qui fait référence à différentes tables en fonction de la valeur dans une autre colonne de votre table Comments. Cela violerait plusieurs règles de conception de bases de données relationnelles.

Une meilleure solution consiste à créer une sorte de "supertable" référencée par les commentaires.

CREATE TABLE Commentable (
  id SERIAL PRIMARY KEY
);

CREATE TABLE Comments (
  comment_id SERIAL PRIMARY KEY,
  foreign_id INT NOT NULL,
  ...
  FOREIGN KEY (foreign_id) REFERENCES Commentable(id)
);

Chacun de vos types de contenu serait considéré comme un sous-type de ce supertable. Ceci est analogue au concept orienté objet d'un interface.

CREATE TABLE BlogPosts (
  blogpost_id INT PRIMARY KEY, -- notice this is not auto-generated
  ...
  FOREIGN KEY (blogpost_id) REFERENCES Commentable(id)
);

CREATE TABLE UserPictures (
  userpicture_id INT PRIMARY KEY, -- notice this is not auto-generated
  ...
  FOREIGN KEY (userpicture_id) REFERENCES Commentable(id)
);

Avant de pouvoir insérer une ligne dans BlogPosts ou UserPictures, vous devez insérer une nouvelle ligne dans Commentable pour générer un nouvel identifiant pseudokey. Ensuite, vous pouvez utiliser cet identifiant généré lorsque vous insérez le contenu dans la table de sous-types respective.

Une fois que vous avez fait tout cela, vous pouvez compter sur des contraintes d'intégrité référentielle.

89
Bill Karwin