Utilisation de PostgreSQL 11.
J'ai essayé de fouiller dans Stack Overflow et ici et je n'ai pas pu trouver de réponse sur les meilleures pratiques.
Je travaille sur une conception de base de données et suis arrivé à un schéma qui utilise une "table de jointure" générique. Cette table de jointure contient cinq colonnes:
CREATE TABLE many_joins_table (
id PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
object_id int NOT NULL,
object_table joins_object_t NOT NULL,
parent_id int NOT NULL,
parent_table joins_parent_t NOT NULL);
J'utilise cette table pour représenter des relations adjacentes et plusieurs à plusieurs entre les objets de ma base de données. Un tel exemple est les balises.
CREATE TABLE tag (
id PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
name text NOT NULL UNIQUE);
CREATE TABLE comment (
id PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY);
CREATE TYPE joins_object_t AS ENUM ('tag');
CREATE TYPE joins_parent_t AS ENUM ('comment');
Lorsqu'une balise est ajoutée à la table des commentaires, j'insère une nouvelle ligne dans cette table de jointure avec les champs suivants:
INSERT INTO many_joins_table
VALUES(1, 'tag'::joins_object_t, 1, 'comment'::joins_parent_t);
Outre la rigidité des énumérations, adressée avec PostgreSQL 9.1 https://stackoverflow.com/questions/1771543/adding-a-new-value-to-an-existing-enum-type/7834949#7834949 .
Y a-t-il des inconvénients ou des avantages significatifs d'une telle approche? Je crains d'avoir implémenté par erreur un anti-modèle. Existe-t-il des meilleures pratiques que je peux appliquer pour améliorer cette implémentation (indexation ou contraintes)?
Merci!
Remarque: Je suis conscient qu'il existe de meilleures façons d'implémenter des balises, à savoir utiliser des intrarays. J'utilise juste des balises comme exemple car c'est facile à comprendre. https://stackoverflow.com/questions/23508551/integer-array-lookup-using-postgres
Edit: Suppression des UUID car cela peut être une distraction pour la question.
Tout d'abord, évitez les énumérations pour des choses comme celle-ci. Les valeurs d'énumération ne peuvent jamais être supprimées, alors ne les utilisez que si vous êtes sûr que cela ne sera jamais nécessaire, ce qui ne semble pas être le cas ici.
Quoi qu'il en soit, je dirais que votre conception est trop compliquée et manque toujours de la caractéristique cruciale de l'intégrité référentielle.
Utilisez une table de jonction pour chaque paire d'objets pouvant être liés. De cette façon, vous
préciser quels objets peuvent être liés
peut avoir une intégrité référentielle
Avoir de nombreuses tables est une bonne chose pour une base de données. Si vous soutenez que vous avez 1000 tables et que chaque objet peut être lié les uns aux autres, ce serait trop de tables. Mais dans ce cas, vous devriez probablement opter pour un modèle où vous n'avez de toute façon pas de table par type d'objet.
Je pense que vous rendez ce complexe. Tout d'abord, ce blog est un mauvais conseil. Bienvenue sur Internet. N'y faites pas attention.
Utilisez un int PRIMARY KEY GENERATED BY IDENTITY AS DEFAULT
(c'est à dire, IDENTITY COLUMN
jusqu'à ce que vous ayez une raison pas trop
Maintenant, vous avez deux choses ..
Ces deux peuvent être hiérarchiques. C'est la seule chose qu'ils ont en commun. Les tables ne modélisent pas différentes structures de données. Les tables contiennent des données. Et tout le but d'une base de données relationnelle est de modéliser les relations des données. Ceci est totalement perverti lorsque vous modélisez des schémas abstraits qui correspondent à toutes vos données.
Des questions pour vous,
En supposant que vous ayez besoin d'un héritage unique, vous pouvez le faire
CREATE TABLE tag (
tag_id int PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
tag name text,
parent_tag int REFERENCES tag
);
Pour plus d'informations à ce sujet, voir hierarchy , et plus précisément ma réponse ici qui traite des commentaires filetés. C'est bien beau, mais maintenant vous avez besoin d'une requête récursive pour interroger cette table. Vous devez également l'épingler à une question. Une question est-elle étiquetée avec toutes les sous-étiquettes? Est-il étiqueté avec toutes les étiquettes parentales? Une question peut-elle être balisée avec deux balises dans la même hiérarchie?
Souvent, avec les balises, il est plus facile de faire simplement ..
-- case insensitive
CREATE EXTENSION citext;
CREATE OR REPLACE FUNCTION array_lacks_dupes(anyarray)
RETURNS bool
AS $$
SELECT coalesce(hasdupe,nodupe) AS hasdupe
FROM (VALUES (true)) AS t(nodupe)
LEFT OUTER JOIN (
SELECT false
FROM unnest($1) AS e
GROUP BY e
HAVING count(*) > 1
LIMIT 1
) AS g(hasdupe)
ON true
$$ LANGUAGE sql
STRICT IMMUTABLE;
CREATE TABLE question (
question_id int PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
tags citext[] CHECK (array_lacks_dupes(tags))
);
CREATE INDEX ON question USING gin (tags);
Avec le schéma ci-dessus, vous n'avez pas à l'interroger de manière étrange et vous pouvez toujours résoudre la requête sur un index ( vous pouvez même ajouter d'autres choses arbitraires sur cet index et tout faire en une seule recherche )
SELECT * FORM question
WHERE tags @> ARRAY['foo']::citext;
Cela trouvera toutes les questions marquées 'foo'
sur un index! Vous voulez trouver si vous correspondez à plusieurs balises?
WHERE tags @> ARRAY['foo', 'bar']::citext;