Mon expérience est plus dans la programmation Web que dans l'administration de base de données, alors corrigez-moi si j'utilise la mauvaise terminologie ici. J'essaie de trouver la meilleure façon de concevoir la base de données pour une application que je vais coder.
La situation: j'ai des rapports dans un tableau et des recommandations dans un autre tableau. Chaque rapport peut contenir de nombreuses recommandations. J'ai également un tableau séparé pour les mots clés (pour implémenter le balisage). Cependant, je veux avoir un seul ensemble de mots clés qui s'applique aux rapports et aux recommandations afin que la recherche sur les mots clés vous donne des rapports et des recommandations en tant que résultats.
Voici la structure avec laquelle j'ai commencé:
Reports
----------
ReportID
ReportName
Recommendations
----------
RecommendationID
RecommendationName
ReportID (foreign key)
Keywords
----------
KeywordID
KeywordName
ObjectKeywords
----------
KeywordID (foreign key)
ReportID (foreign key)
RecommendationID (foreign key)
Instinctivement, j'ai l'impression que ce n'est pas optimal et que je devrais faire hériter mes objets étiquetables d'un parent commun et faire étiqueter ce parent de commentaire, ce qui donnerait la structure suivante:
BaseObjects
----------
ObjectID (primary key)
ObjectType
Reports
----------
ObjectID_Report (foreign key)
ReportName
Recommendations
----------
ObjectID_Recommendation (foreign key)
RecommendationName
ObjectID_Report (foreign key)
Keywords
----------
KeywordID (primary key)
KeywordName
ObjectKeywords
----------
ObjectID (foreign key)
KeywordID (foreign key)
Dois-je aller avec cette deuxième structure? Suis-je en train de manquer des préoccupations importantes ici? De plus, si je choisis le second, que dois-je utiliser comme nom non générique pour remplacer "Object"?
Mise à jour:
J'utilise SQL Server pour ce projet. Il s'agit d'une application interne avec un petit nombre d'utilisateurs non simultanés, donc je ne prévois pas une charge élevée. En termes d'utilisation, les mots clés seront probablement utilisés avec parcimonie. C'est à peu près juste à des fins de rapports statistiques. En ce sens, quelle que soit la solution que j'utilise, cela n'affectera probablement que les développeurs qui devront maintenir ce système en ligne ... mais je me suis dit qu'il était bon de mettre en œuvre de bonnes pratiques chaque fois que je le pouvais. Merci pour toute la perspicacité!
la source
Réponses:
Le problème avec votre premier exemple est la table tri-link. Cela nécessitera-t-il que l'une des clés étrangères du rapport ou des recommandations soit toujours NULL pour que les mots clés ne soient liés que dans un sens ou dans l'autre?
Dans le cas de votre deuxième exemple, la jointure de la base aux tables dérivées peut maintenant nécessiter l'utilisation du sélecteur de type ou de LEFT JOIN selon la façon dont vous le faites.
Compte tenu de cela, pourquoi ne pas simplement le rendre explicite et éliminer tous les NULL et LEFT JOINs?
Dans ce scénario, lorsque vous ajoutez quelque chose d'autre qui doit être balisé, vous ajoutez simplement la table d'entités et la table de liaison.
Ensuite, vos résultats de recherche ressemblent à ceci (voyez qu'il y a toujours une sélection de type en cours et les transformer en génériques au niveau des résultats de l'objet si vous voulez une seule liste de résultats):
Quoi qu'il en soit, quelque part il y aura une sélection de type et une sorte de branchement en cours.
Si vous regardez comment vous le feriez dans votre option 1, c'est similaire, mais avec une instruction CASE ou LEFT JOINs et un COALESCE. Au fur et à mesure que vous développez votre option 2 avec plus de choses liées, vous devez continuer à ajouter plus de JOINTS GAUCHE là où les choses ne sont généralement PAS trouvées (un objet lié ne peut avoir qu'une seule table dérivée qui est valide).
Je ne pense pas qu'il y ait quelque chose de fondamentalement mauvais avec votre option 2, et vous pourriez réellement la faire ressembler à cette proposition en utilisant des vues.
Dans votre option 1, j'ai du mal à comprendre pourquoi vous avez opté pour la table tri-link.
la source
Tout d'abord, notez que la solution idéale dépend dans une certaine mesure du SGBDR que vous utilisez. Je vais alors donner à la fois la réponse standard et la réponse spécifique à PostgreSQL.
Réponse normalisée et standard
La réponse standard est d'avoir deux tables de jointure.
Supposons que nous ayons nos tables:
Cette approche suit toutes les règles de normalisation standard et ne rompt pas les principes de normalisation de base de données traditionnels. Il devrait fonctionner sur n'importe quel SGBDR.
Réponse spécifique à PostgreSQL, conception N1NF
Tout d'abord, un mot sur la raison pour laquelle PostgreSQL est différent. PostgreSQL prend en charge un certain nombre de façons très utiles d'utiliser des index sur des tableaux, notamment en utilisant ce que l'on appelle les index GIN. Ceux-ci peuvent améliorer considérablement les performances s'ils sont utilisés correctement ici. Étant donné que PostgreSQL peut "atteindre" les types de données de cette manière, l'hypothèse de base d'atomicité et de normalisation est quelque peu problématique à appliquer de manière rigide ici. Donc, pour cette raison, ma recommandation serait de briser la règle d'atomicité de la première forme normale et de s'appuyer sur les index GIN pour de meilleures performances.
Une deuxième remarque ici est que bien que cela donne de meilleures performances, cela ajoute quelques maux de tête car vous aurez un travail manuel à faire pour que l'intégrité référentielle fonctionne correctement. Donc, le compromis ici est la performance pour le travail manuel.
Nous devons maintenant ajouter des déclencheurs pour garantir la bonne gestion des mots clés.
Deuxièmement, nous devons décider quoi faire lorsqu'un mot clé est supprimé. Dans l'état actuel des choses, un mot clé supprimé de la table des mots clés ne se répercutera pas en cascade dans les champs des mots clés. C'est peut-être souhaitable et peut-être pas. La chose la plus simple à faire est de restreindre toujours la suppression et de vous attendre à ce que vous gériez manuellement ce cas s'il se produit (utilisez un déclencheur pour plus de sécurité ici). Une autre option peut être de réécrire chaque valeur de mot-clé là où le mot-clé existe pour le supprimer. Encore une fois, un déclencheur serait le moyen de le faire également.
Le grand avantage de cette solution est que vous pouvez indexer des recherches très rapides par mot-clé et que vous pouvez extraire toutes les balises sans jointure. L'inconvénient est que la suppression d'un mot-clé est pénible et ne fonctionnera pas bien même par une bonne journée. Cela peut être acceptable car il s'agit d'un événement rare et pourrait être consigné dans un processus d'arrière-plan, mais c'est un compromis qui mérite d'être compris.
Critiquer votre première solution
Le vrai problème avec votre première solution est que vous n'avez aucune clé possible sur ObjectKeywords. Par conséquent, vous avez un problème où vous ne pouvez pas garantir que chaque mot clé n'est appliqué à chaque objet qu'une seule fois.
Votre deuxième solution est un peu meilleure. Si vous n'aimez pas les autres solutions proposées, je vous suggère d'y aller. Je suggérerais cependant de se débarrasser de keyword_id et de simplement se joindre au texte du mot clé. Cela élimine une jointure sans dénormalisation.
la source
Je suggérerais deux structures distinctes:
De cette façon, vous n'avez pas tous les identifiants d'entité possibles dans la même table (ce qui n'est pas très évolutif et pourrait être déroutant), et vous n'avez pas de table avec un "id d'objet" générique que vous devez lever l'ambiguïté ailleurs en utilisant la
base_object
table, qui fonctionnera, mais je pense que la conception est trop compliquée.la source
BaseObjects
table lors de ma première lecture, et j'ai pensé que je voyais une description d'une table oùobject_id
peut pointer vers un ID dans n'importe quelle table.D'après mon expérience, c'est ce que vous pouvez faire.
Et pour la relation entre les mots clés, les rapports et les recommandations, vous pouvez faire l'une des deux options suivantes: Option A:
Cela permet une relation directe des rapports aux recommandations, aux mots-clés et enfin aux mots-clés. Option B:
L'option A est plus facile à appliquer et à gérer car elle aura les architectures de la base de données pour gérer l'intégrité des données et ne permettra pas l'insertion de données invalides.
L'option B nécessite cependant un peu plus de travail car vous devrez coder l'identification de la relation. Est plus flexible à long terme si, par hasard, à un moment donné dans le futur, vous devez ajouter des mots clés à un autre élément que le rapport ou la recommandation, il vous suffit d'ajouter l'identification et d'utiliser directement le tableau.
la source