44

je le tableau suivant « commentaires » dans mon application:MySQL - conditionnel étrangères Contraintes clés

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

l'idée de ce tableau est de stocker des commentaires pour les différentes parties de mon application - il peut stocker des commentaires pour blog à savoir:

1|34|blogpost|lorem ipsum... 

image utilisateur:

2|12|picture|lorem ipsum... 

et ainsi de suite.

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

dire quelque chose comme ça dans le tableau des commentaires:

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

Répondre

71

Vous essayez de faire une conception que l'on appelle Associations polymorphes. C'est-à-dire que la clé étrangère peut référencer des lignes dans l'une quelconque des tables lié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 référence des tables différentes en fonction de la valeur d'une autre colonne de votre table Comments. Cela viole plusieurs règles de conception de base de données relationnelle.

Une meilleure solution est de faire une sorte de "supertable" référencé 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'une 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 à Commentable pour générer un nouveau id pseudokey. Vous pouvez ensuite utiliser cet ID généré lorsque vous insérez le contenu dans la table de sous-types correspondante. Une fois que vous faites tout cela, vous pouvez compter sur les contraintes d'intégrité référentielle.

+1

Je suppose que UserPictures contient un champ user_id qui fait référence à la table User. Comment gérez-vous la suppression d'un utilisateur de sorte que la suppression soit répercutée dans la table commentable? J'ai posé cette question ici - http://stackoverflow.com/questions/11497149/how-to-enforce-referential-integrity-on-single-table-inheritance - et serais reconnaissant si vous pouviez expliquer comment vous gérez cette hoquet je suis coincé. –

+13

@MattMcCormick, je ne réponds plus aux questions sur SO, parce que les modérateurs désagréables ont rendu ingrat la participation. –

+2

Oh ok. Merci pour votre participation passée. J'ai lu quelques unes de vos réponses concernant l'héritage d'une table et les associations polymorphes et j'ai aussi regardé les diapositives de votre conversation que vous avez référencées dans l'une d'entre elles. Cela m'a aidé à mieux identifier les situations avec la base de données qui pourraient causer des problèmes plus tard. J'ai ajouté votre livre à ma liste de lecture et je vais probablement le prendre pour mon prochain livre de logiciels à lire. –