Ce que vous décrivez s'appelle Polymorphes Associations. C'est, la "clé étrangère" colonne contient une valeur d'id qui doit exister dans une série de tableaux. Généralement la cible tables sont liées d'une certaine façon, comme étant des instances de certaines communes de la superclasse de données. Vous auriez également besoin d'une autre colonne sur le côté de la colonne de clé étrangère, de sorte que sur chaque ligne, vous pouvez désigner la table cible est référencé.
CREATE TABLE popular_places (
user_id INT NOT NULL,
place_id INT NOT NULL,
place_type VARCHAR(10) -- either 'states' or 'countries'
-- foreign key is not possible
);
Il n'y a aucun moyen de modèle Polymorphes Associations à l'aide de SQL contraintes. Une contrainte de clé étrangère fait toujours référence à une table cible.
Polymorphes Associations sont pris en charge par des cadres tels que des Rails et de la mise en veille prolongée. Mais ils explicitement dire que vous devez désactiver SQL contraintes pour utiliser cette fonctionnalité. Au lieu de cela, l'application ou le cadre doit faire un travail équivalent pour s'assurer que la référence est satisfait. C'est la valeur de la clé étrangère est présente dans une des tables cible.
Polymorphes Associations sont faibles à l'égard de l'application de cohérence de base de données. L'intégrité des données dépend de tous les clients qui accèdent à la base de données avec la même intégrité référentielle de la logique appliquée, et aussi l'application de la loi doit être exempt de bogues.
Voici quelques solutions de rechange pour faire profiter de la base de données de l'exécution de l'intégrité référentielle:
Créer une table supplémentaire par cible. Par exemple popular_states
et popular_countries
, qui référence states
et countries
respectivement. Chacun de ces "populaire" tables de référence également le profil de l'utilisateur.
CREATE TABLE popular_states (
state_id INT NOT NULL,
user_id INT NOT NULL,
PRIMARY KEY(state_id, user_id),
FOREIGN KEY (state_id) REFERENCES states(state_id),
FOREIGN KEY (user_id) REFERENCES users(user_id),
);
CREATE TABLE popular_countries (
country_id INT NOT NULL,
user_id INT NOT NULL,
PRIMARY KEY(country_id, user_id),
FOREIGN KEY (country_id) REFERENCES countries(country_id),
FOREIGN KEY (user_id) REFERENCES users(user_id),
);
Cela signifie que, pour obtenir toutes d'un utilisateur à un favori populaire endroits où vous devez vous interroger à la fois de ces tables. Mais cela signifie que vous pouvez compter sur la base de données pour appliquer la cohérence.
Créer un places
tableau comme un supertable. Comme Abie mentionne, une deuxième alternative est que vos endroits populaires référence à un tableau comme places
, ce qui est un parent à la fois states
et countries
. C'est à la fois des états et des pays ont aussi une clé étrangère d' places
(vous pouvez même faire cette clé étrangère aussi être la clé primaire de l' states
et countries
).
CREATE TABLE popular_areas (
user_id INT NOT NULL,
place_id INT NOT NULL,
PRIMARY KEY (user_id, place_id),
FOREIGN KEY (place_id) REFERENCES places(place_id)
);
CREATE TABLE states (
state_id INT NOT NULL PRIMARY KEY,
FOREIGN KEY (state_id) REFERENCES places(place_id)
);
CREATE TABLE countries (
country_id INT NOT NULL PRIMARY KEY,
FOREIGN KEY (country_id) REFERENCES places(place_id)
);
L'utilisation de deux colonnes. Au lieu d'une colonne qui pourrait faire référence à l'une des deux tables cible, l'utilisation de deux colonnes. Ces deux colonnes peuvent être NULL
; en fait, un seul d'entre eux doit être non-NULL
.
CREATE TABLE popular_areas (
place_id SERIAL PRIMARY KEY,
user_id INT NOT NULL,
state_id INT,
country_id INT,
CONSTRAINT UNIQUE (user_id, state_id, country_id), -- UNIQUE permits NULLs
CONSTRAINT CHECK (state_id IS NOT NULL OR country_id IS NOT NULL),
FOREIGN KEY (state_id) REFERENCES places(place_id),
FOREIGN KEY (country_id) REFERENCES places(place_id)
);
En termes de la théorie relationnelle, Polymorphe Associations viole Première Forme Normale, car l' popular_place_id
est en effet une colonne avec deux significations: c'est soit un état ou un pays. Vous n'auriez pas de magasin d'une personne, age
et leur phone_number
dans une seule colonne, et pour la même raison, vous ne devez pas stocker à la fois state_id
et country_id
dans une seule colonne. Le fait que ces deux attributs ont des types de données compatibles est fortuite; ils encore signifier différentes entités logiques.
Polymorphes Associations viole aussi la Troisième Forme Normale, parce que le sens de la colonne dépend de la colonne supplémentaire qui les noms de la table à laquelle la clé étrangère référence. En Troisième Forme Normale, un attribut dans une table doit dépendre uniquement de la clé primaire de cette table.
Re commentaire de @SavasVedova:
Je ne suis pas sûr de suivre votre description sans voir les définitions de table ou un exemple de requête, mais il semble que vous simplement avoir plusieurs Filters
tables, chacune contenant une clé étrangère qui fait référence à une centrale d' Products
table.
CREATE TABLE Products (
product_id INT PRIMARY KEY
);
CREATE TABLE FiltersType1 (
filter_id INT PRIMARY KEY,
product_id INT NOT NULL,
FOREIGN KEY (product_id) REFERENCES Products(product_id)
);
CREATE TABLE FiltersType2 (
filter_id INT PRIMARY KEY,
product_id INT NOT NULL,
FOREIGN KEY (product_id) REFERENCES Products(product_id)
);
...and other filter tables...
Rejoignant les produits à un type spécifique de filtre est facile si vous savez de quel type que vous souhaitez rejoindre:
SELECT * FROM Products
INNER JOIN FiltersType2 USING (product_id)
Si vous souhaitez que le type de filtre dynamique, vous devez écrire le code de l'application pour construire la requête SQL. SQL requiert de la table spécifiée et fixé au moment de vous écrire la requête. Vous ne pouvez pas faire la table jointe être choisis de manière dynamique sur la base des valeurs trouvées dans les différents lignes de Products
.
La seule autre option est d'adhérer à toutes les tables de filtrage à l'aide de jointures externes. Ceux qui n'ont pas de correspondance product_id va juste être retournés comme une ligne unique de valeurs null. Mais vous avez encore à coder en dur toutes les tables jointes, et si vous ajoutez de nouvelles tables de filtrage, vous devez mettre à jour votre code.
SELECT * FROM Products
LEFT OUTER JOIN FiltersType1 USING (product_id)
LEFT OUTER JOIN FiltersType2 USING (product_id)
LEFT OUTER JOIN FiltersType3 USING (product_id)
...
Une autre façon de joindre à toutes les tables de filtrage est de le faire en série:
SELECT * FROM Product
INNER JOIN FiltersType1 USING (product_id)
UNION ALL
SELECT * FROM Products
INNER JOIN FiltersType2 USING (product_id)
UNION ALL
SELECT * FROM Products
INNER JOIN FiltersType3 USING (product_id)
...
Mais ce format nécessite encore à vous d'écrire les références à toutes les tables. Il n'y a pas moyen de contourner cela.