J'aborde un projet pour lequel je vais devoir mettre en place une base de données avec mon patron; nous sommes une toute petite start-up, l'environnement de travail est donc profondément personnel.
Il m'avait déjà fourni une des bases de données de l'entreprise et cela allait complètement à l'encontre de ce que l'on m'avait appris (et que je lisais) à l'école pour le SGBDR. Par exemple, il y a des bases de données entières ici composées d'une table (par base de données indépendante). Une de ces tables a plus de 20 colonnes et pour le contexte, voici quelques noms de colonnes d' une table:
lngStoreID | vrStoreName | lngCompanyID | vrCompanyName | lngProductID | vrProductName
Le fait est que là où il devrait avoir des tables individuelles contenant les données de l'entité (nom, taille, date d'achat, etc.), il les place dans une grande table par base de données.
Je souhaite améliorer cette conception, mais je ne suis pas certain de savoir pourquoi un modèle de données correctement normalisé et segmenté améliorerait réellement ce produit. Bien que je connaisse la conception de bases de données de collège et que je comprenne comment le faire, je ne sais pas pourquoi cela améliore réellement les bases de données.
Pourquoi un bon schéma relationnel améliore-t-il une base de données?
la source
He [the boss] had given me one of his databases before and it completely went against what I was taught (and read about) in school for RDBMS
<- Bienvenue dans le monde réel!Réponses:
L'argument de performance est généralement celui qui est le plus intuitif. Vous voulez surtout souligner la difficulté d'ajouter de bons index dans une base de données mal normalisée (remarque: il existe des cas extrêmes où la dénormalisation peut en fait améliorer les performances, mais lorsque vous êtes tous les deux inexpérimentés avec des bases de données relationnelles, vous ne risquez probablement pas facilement voir ces cas).
Un autre argument est l'argument de la taille de stockage. Une table dénormalisée avec beaucoup de redondances nécessitera beaucoup plus de stockage. Cela joue également sur l'aspect performance: plus vous avez de données, plus vos requêtes seront lentes.
Il existe également un argument un peu plus difficile à comprendre, mais qui est en réalité plus important, car vous ne pouvez pas le résoudre en utilisant davantage de matériel. C'est le problème de cohérence des données. Une base de données correctement normalisée veillera à ce qu'un produit portant un identifiant spécifique porte toujours le même nom. Mais dans une base de données dénormalisée, de telles incohérences sont possibles. Il faut donc prendre des précautions particulières pour éviter les incohérences, ce qui prendra du temps à la programmation pour bien fonctionner et causera toujours des bogues qui vous coûteront cher pour la satisfaction du client.
la source
L'utilisation d'un logiciel de gestion de base de données dédié pourrait être considérablement plus simple (désolé, je n'ai pas pu résister).
Si cette base de données ne se préoccupe que « l' exploitation forestière » qui produit a été vendu où, quand et par qui, alors vous pourrait être en mesure d'étendre la définition de « base de données OK » assez loin pour le couvrir. Si ces données sont utilisées pour autre chose, alors elles sont vraiment très pauvres.
Mais ...
Est-ce que l'application / les requêtes utilisant ces données répondent mal / lentement? Si non, alors il n'y a pas de vrai problème à résoudre. Bien sûr, cela a l'air moche, mais si ça marche, vous n'obtiendrez pas de "points" pour suggérer que "pourrait" être mieux.
Si vous pouvez trouver des symptômes définis (c.-à-d. Des problèmes) qui semblent provenir d'une mauvaise modélisation des données, protégez une meilleure solution. Prenez une copie de l'une de ces "bases de données", normalisez les données et voyez si votre solution fonctionne mieux. Si c'est nettement mieux (et je m'attendrais à ce que toute opération de mise à jour de ces données soit considérablement améliorée), adressez-vous à votre patron et montrez-lui l'amélioration.
Il est parfaitement possible de recréer sa "vue à table unique" des données avec .. bien .. Vues.
la source
La réponse est: cela n'améliore pas toujours une base de données. Vous devez savoir que ce que vous avez probablement appris s'appelle la troisième forme normale .
D'autres formes sont valables dans certaines situations, ce qui est essentiel pour répondre à votre question. Votre exemple ressemble à First Normal Form , si cela vous aide à vous sentir mieux dans son état actuel.
Les règles 3NF établissent des relations entre les données qui "améliorent" une base de données:
Empêcher les données non valides d'entrer dans votre système (si une relation est de 1 à 1, une erreur est générée malgré le code écrit dessus). Si vos données sont cohérentes dans la base de données, cela risque moins d'entraîner des incohérences en dehors de votre base de données.
Il fournit un moyen de valider le code (par exemple, une relation plusieurs-à-un est un signal pour restreindre les propriétés / comportements d'un objet). Lors de l'écriture de code pour utiliser la base de données, les programmeurs remarquent parfois que la structure de données est un indicateur du fonctionnement de leur code. Ils peuvent également fournir des informations utiles si la base de données ne correspond pas à leur code. (Cela ressemble plus à un voeu pieux, malheureusement.)
Définissez des règles qui peuvent vous aider de manière significative à réduire les erreurs lors de la création d'une base de données, de sorte que vous ne la construisez pas en fonction d'exigences arbitraires pouvant survenir à tout moment de la vie d'une base de données. Au lieu de cela, vous évaluez systématiquement les informations pour atteindre des objectifs spécifiques.
Les structures de base de données appropriées améliorent les performances en connectant les données de manière à minimiser le stockage de données, à minimiser les appels de stockage à extraire les données, à maximiser les ressources en mémoire et / ou à minimiser le tri / la manipulation de données pour votre ensemble de données particulier, par rapport à la requête que vous êtes. exécuter contre elle. Mais la structure "appropriée" dépend de la quantité de données, de la nature des données, du type de requête, des ressources système, etc. En normalisant, vous risquez d’aggraver les performances (par exemple, si vous chargez toutes les données sous la forme d’une table). une requête). Le traitement des transactions (OLTP) et la veille stratégique (entrepôt de données) sont très différents.
Dans une petite entreprise avec de petits ensembles de données, vous constaterez peut-être qu'il n'y a rien de mal avec la façon dont il est maintenant. Sauf que si vous grandissez, il sera difficile de "réparer" plus tard, car à mesure que la table grossit, les systèmes qui l'utilisent vont probablement ralentir.
Habituellement, vous voudrez mettre l'accent sur les transactions rapides au fur et à mesure que l'entreprise grandit. Toutefois, si vous consacrez plus de temps à ce projet au lieu d’autres éléments dont la société pourrait avoir besoin plus rapidement, vous ne rencontrerez peut-être jamais ce problème car votre société ne se développe jamais vraiment. C'est le "défi de pré-optimisation" - où passer votre temps précieux en ce moment.
Bonne chance!
la source
WHERE
clause. Bien sûr, cela peut toujours mal se passer, mais cela est moins probable dans une situation normalisée puisqu'il suffit de faire correspondre une ligne via la clé primaire.Il y a plusieurs raisons pour lesquelles utiliser une seule "grande table de dieu" est mauvais. Je vais essayer d'illustrer les problèmes avec une base de données exemple constituée. Supposons que vous essayez de modéliser des événements sportifs. Nous dirons que vous voulez modéliser les jeux et les équipes jouant dans ces jeux. Cela pourrait ressembler à une conception avec plusieurs tables (ceci est très simpliste, donc ne vous laissez pas prendre aux endroits où une normalisation supplémentaire pourrait être appliquée):
et une seule base de données de table ressemblerait à ceci
Commençons par créer des index sur ces tables. Si j'avais besoin d'un index sur la ville natale pour une équipe, je pourrais facilement l' ajouter à la
Teams
table ou à laTeamsAndGames
table. N'oubliez pas que chaque fois que vous créez un index, celui-ci doit être stocké sur un disque et mis à jour à mesure que des lignes sont ajoutées à la table. Dans le cas de laTeams
table, c'est assez simple. Je mets dans une nouvelle équipe, la base de données met à jour l'index. Mais qu'en est-il pourTeamsAndGames
? Eh bien, la même chose s'applique à partir duTeams
exemple. J'ajoute une équipe, l'index est mis à jour. Mais cela arrive aussi quand j'ajoute un jeu! Même si ce champ sera nul pour un jeu, l’index doit quand même être mis à jour et stocké sur disque pour ce jeu. Pour un index, cela ne semble pas trop grave. Mais lorsque vous avez besoin de nombreux index pour les multiples entités entassées dans cette table, vous perdez beaucoup d’espace à les stocker et beaucoup de temps de traitement à les mettre à jour pour les éléments où ils ne s’appliquent pas.Deuxièmement, la cohérence des données. Dans le cas de l'utilisation de deux tables distinctes, je peux utiliser des clés étrangères de
Games
table enTeams
table pour définir les équipes qui jouent dans une partie. Et en supposant que les colonnesHomeTeamId
etAwayTeamId
ne soient pas annulées, la base de données garantira que chaque jeu que je mets a 2 équipes et que ces équipes existent dans ma base de données. Mais qu'en est-il du scénario à table unique? Eh bien, comme il y a plusieurs entités dans cette table, ces colonnes devraient pouvoir être annulées (vous pouvez les rendre non annulables et y insérer des données parasites, mais ce n'est qu'une idée horrible). Si ces colonnes ont la valeur NULL, la base de données ne peut plus garantir que, lorsque vous insérez un jeu, elle comporte deux équipes.Mais que faire si vous décidez d'y aller quand même? Vous configurez les clés étrangères de sorte que ces champs renvoient vers une autre entité de la même table. Mais maintenant, la base de données s'assurera simplement que ces entités existent dans la table, et non qu'elles sont du type correct. Vous pouvez très facilement définir
GameHomeTeamId
l'ID d'un autre jeu et la base de données ne se plaindra pas du tout. Si vous avez essayé cela dans le scénario à plusieurs tables, la base de données s’y adapterait.Vous pouvez essayer d'atténuer ces problèmes en disant "bon, nous nous assurerons simplement de ne jamais le faire dans le code". Si vous avez confiance en votre capacité à écrire du code sans bug la première fois et en votre capacité à prendre en compte toutes les combinaisons étranges d'essais qu'un utilisateur peut essayer, continuez. Personnellement, je ne suis pas confiant dans ma capacité à faire l'une ou l'autre de ces choses, je vais donc laisser la base de données me donner un filet de sécurité supplémentaire.
(Cela empire encore si votre conception consiste à copier toutes les données pertinentes entre les lignes au lieu d'utiliser des clés étrangères. Toute incohérence orthographique / autre donnée sera difficile à résoudre. Comment savoir si "Jon" est une faute de frappe de "John "ou si c'était intentionnel (parce que ce sont deux personnes distinctes)?)
Troisièmement, presque chaque colonne doit pouvoir être nullable ou doit être remplie avec des données copiées ou des données vides. Un jeu n'a pas besoin d'un
TeamName
ouTeamHomeCity
. Donc soit chaque jeu a besoin d’une sorte d’espace réservé, soit il doit pouvoir être annulé. Et s’il est annulable, la base de données acceptera avec plaisir une partie sansTeamName
. Il faudra également une équipe sans nom, même si votre logique d’affaires indique que cela ne devrait jamais se produire.Il existe une poignée d'autres raisons pour lesquelles vous souhaitez utiliser des tables séparées (notamment pour préserver la santé mentale des développeurs). Il y a même plusieurs raisons pour lesquelles une table plus grande peut être meilleure (la dénormalisation améliore parfois les performances). Ces scénarios sont rares (et généralement mieux gérés lorsque vous disposez de mesures de performance indiquant que le problème est réellement, pas un index manquant ou autre chose).
Enfin, développez quelque chose qui sera facile à maintenir. Ce n'est pas parce que ça marche que ça va. Essayer de maintenir des tables divines (comme des classes divines) est un cauchemar. Vous vous arrêtez juste pour la douleur plus tard.
la source
Citation du jour: " Théorie et pratique devraient être les mêmes ... en théorie "
Tableau dénormalisé
Votre table hold-it-all, unique en son genre, contient des données redondantes et présente un avantage: elle permet de créer des rapports sur ses lignes très simples à coder et à exécuter rapidement, car aucune jointure n'est nécessaire. Mais ceci à un coût élevé:
IngCompanyID
etvrCompanyName
). La mise à jour des données de base peut nécessiter de mettre à jour beaucoup plus de lignes que dans un schéma normalisé.Table normalisée
Les inconvénients ci-dessus sont des avantages pour le schéma normalisé. Bien sûr, les requêtes pourraient être un peu plus complexes à écrire.
En bref, le schéma normalisé exprime beaucoup mieux la structure et les relations entre vos données. Je serai provocateur et dirai que c'est le même genre de différence qu'entre la discipline requise pour utiliser un ensemble de tiroirs de bureau ordonnés et la facilité d'utilisation d'une corbeille à papier.
la source
Je pense que votre question comporte au moins deux parties:
1. Pourquoi des entités de types différents ne devraient-elles pas être stockées dans la même table?
Les réponses les plus importantes ici sont la lisibilité et la rapidité du code. A
SELECT name FROM companies WHERE id = ?
est simplement beaucoup plus lisible qu’unSELECT companyName FROM masterTable WHERE companyId = ?
et vous êtes moins susceptible d’interroger par inadvertance un non-sens (par exemple,SELECT companyName FROM masterTable WHERE employeeId = ?
cela ne serait pas possible lorsque les entreprises et les employés sont stockés dans des tables différentes). En ce qui concerne la rapidité, les données d'une table de base de données sont extraites en lisant la table complète de manière séquentielle ou en lisant un index. Les deux sont plus rapides si la table / index contient moins de données, et c'est le cas si les données sont stockées dans des tables différentes (et vous ne devez lire que l'une des tables / index).2. Pourquoi les entités d'un même type devraient-elles être divisées en sous-entités stockées dans différentes tables?
Ici, la raison principale est d'éviter les incohérences dans les données. Avec l'approche à table unique, pour un système de gestion des commandes, vous pouvez stocker le nom du client, l'adresse du client et l'ID de produit du produit que le client a commandé en tant qu'entité unique. Si un client commandait plusieurs produits, vous auriez plusieurs instances du nom et de l'adresse du client dans votre base de données. Dans le meilleur des cas, votre base de données contient des données en double, ce qui peut la ralentir un peu. Mais le pire cas est que quelqu'un (ou un code) a commis une erreur lors de la saisie des données, de sorte qu'une entreprise se retrouve avec des adresses différentes dans votre base de données. Cela seul est déjà assez grave. Mais si vous deviez interroger l'adresse d'une entreprise en fonction de son nom (par exemple,
SELECT companyAddress FROM orders WHERE companyName = ? LIMIT 1
) vous obtiendrez de manière arbitraire une des deux adresses et ne vous rendriez même pas compte d’une incohérence. Toutefois, chaque fois que vous exécutez la requête, vous pouvez obtenir une adresse différente, en fonction de la résolution interne de votre requête par le SGBD. Cela cassera probablement votre demande ailleurs et la cause première de cette casse sera très difficile à trouver.Avec l’approche multi-tables, vous vous rendrez compte qu’il existe une dépendance fonctionnelle du nom de la société à l’adresse de la société (si une société ne peut avoir qu’une seule adresse), vous stockez le tuple (companyName, companyAddress) dans une seule table (par exemple:
company
), et le tuple (productId, companyName) dans une autre table (par exempleorder
). UneUNIQUE
contrainte sur lacompany
table peut alors imposer que chaque société ne possède qu'une seule adresse dans votre base de données, de sorte qu'aucune incohérence pour les adresses de société ne puisse jamais se produire.Remarque: dans la pratique, pour des raisons de performances, vous devez probablement générer un identifiant unique (companyId) unique pour chaque entreprise et l'utiliser comme clé étrangère au lieu d'utiliser directement le nom de l'entreprise. Mais l'approche générale reste la même.
la source
TL; DR - Ils conçoivent la base de données en fonction de la façon dont ils ont été enseignés à l’école.
J'aurais pu écrire cette question il y a 10 ans. Il m'a fallu un certain temps pour comprendre pourquoi mes prédécesseurs ont conçu leurs bases de données comme ils l'ont fait. Vous travaillez avec quelqu'un qui soit:
Je ne soupçonne pas que c'est le numéro 1 puisque vous avez réellement des numéros d'identification dans votre tableau, alors je suppose que le numéro 2.
Après ma sortie de l'école, je travaillais pour un magasin qui utilisait un AS / 400 (alias IBM i). J'ai trouvé certaines choses étranges dans la conception de leurs bases de données et j'ai commencé à demander que nous apportions des changements pour suivre la façon dont on m'a appris à concevoir des bases de données. (J'étais stupide à l'époque)
Il a fallu à un ancien programmeur patient pour m'expliquer pourquoi les choses se passaient ainsi. Ils n’avaient pas changé de schéma car cela aurait causé la défaillance de programmes plus anciens que moi. Littéralement, le code source d'un programme avait une date de création de l'année précédant ma naissance. Sur le système sur lequel nous travaillions, leurs programmes devaient implémenter toute la logique et les opérations gérées par le planificateur de requêtes de votre base de données. (Vous pouvez voir cela en exécutant EXPLAIN sur l'une de vos requêtes)
Il maîtrisait les techniques que j'essayais de mettre en œuvre, mais le fonctionnement du système était plus important que les modifications à apporter "parce que cela contrevenait à ce que l'on m'avait enseigné". Chaque nouveau projet que nous avons commencé a fait le meilleur usage du modèle relationnel que nous avons pu. Malheureusement, d’autres programmeurs / consultants de cette époque ont toujours conçu leurs bases de données comme s’ils utilisaient les anciennes contraintes de ce système.
Quelques exemples de ce que j'ai rencontré qui ne correspondaient pas au modèle relationnel:
code1,code2, ..., code20
)Les raisons qui m'ont été données pour ces décisions de conception étaient toutes basées sur les contraintes du système lors de la conception initiale de la base de données.
Dates - On m'a dit qu'il fallait plus de temps de traitement pour utiliser les fonctions de date (quel mois, jour ou jour de semaine) pour traiter une date que pour créer un tableau de chaque date possible avec toutes ces informations.
Colonnes séquentielles du même type - L'environnement de programmation dans lequel elles se trouvaient ont permis à un programme de créer une variable de tableau sur une partie de la ligne. Et c'était un moyen plus facile de réduire le nombre d'opérations de lecture.
Colonnes CHAR NxM Longueur - Il était plus facile de déplacer les valeurs de configuration dans une colonne pour réduire les opérations de lecture de fichier.
Un exemple mal conçu en équivalent C reflétant l'environnement de programmation qu'ils avaient:
D'après ce qu'on m'a dit, certaines de ces pratiques étaient considérées comme une pratique exemplaire à l'époque.
la source