web-dev-qa-db-fra.com

Quand dénormaliser une conception de base de données

Je sais que la normalisation a été largement discutée sur Stack Overflow. J'ai lu bon nombre des discussions précédentes. J'ai cependant quelques questions supplémentaires.

Je travaille sur un système hérité avec au moins 100 tables. La base de données a une structure non normalisée, des tables qui contiennent une variété de données disparates et d'autres problèmes. J'ai été chargé d'essayer de l'améliorer. Je ne peux pas simplement recommencer, mais je dois modifier le schéma existant.

Dans le passé, j'ai toujours essayé de concevoir des bases de données normalisées. Maintenant les questions. Un développeur senior a suggéré que dans certains cas, nous ne pouvons pas normaliser:

1) Avec des données temporelles. Par exemple, une facture est créée qui est liée à un produit. Si un client demande une copie de cette facture un an plus tard, nous devons être en mesure de produire une copie exacte de l'original. Que faire si le prix, le nom ou la description du produit ont été mis à jour? Le gars senior a suggéré que le prix et les autres informations sur le produit soient copiés dans le tableau des factures. Je pense que nous devrions peut-être avoir une autre table telle que productPrice qui a un champ de date afin que nous puissions suivre les changements de prix au fil du temps. Nous aurions besoin de la même chose pour la description et le nom du produit, je suppose? Semble compliqué. Qu'est-ce que tu penses?

2) La base de données est un système comptable. Je ne connais pas très bien la comptabilité. À l'heure actuelle, certaines données récapitulatives sont dérivées et stockées dans la base de données. Par exemple, le total des ventes de l'année. Mon associé principal a dit que les comptables aiment vérifier que les choses sont correctes en comparant cette valeur avec des données qui sont réellement calculées à partir des factures, etc. pour leur donner l'assurance que l'application fonctionne correctement.

Il a dit qu'à l'heure actuelle, par exemple, nous pouvons savoir si quelqu'un a supprimé une facture de l'année dernière par erreur parce que les totaux ne seront pas les mêmes. Il a également souligné qu'il pourrait être assez lent de calculer ces totaux à la volée. Bien sûr, j'ai dit que les données ne devaient pas être dupliquées et devaient toujours être calculées en cas de besoin. J'ai suggéré que nous pourrions utiliser SQL Reporting Services ou une autre solution qui générera ces rapports du jour au lendemain et les mettra en cache. De toute façon, il n'est pas convaincu. Des commentaires à ce sujet?

47
Mark Evans

Votre collègue principal est un développeur, pas un modélisateur de données. Il vaut mieux partir de zéro, sans eux. La normalisation n'est compliquée que pour ceux qui ne liront pas de livres et obtiendront leurs "connaissances" auprès des amateurs du wiki. C'est assez juste qu'il vous fasse réfléchir, mais certains des problèmes sont absurdes.

Vos numéros:

  1. Vous devez apprécier les différences entre les données réelles en ligne et les données historiques; puis la différence entre les besoins purement historiques et archivistiques. Tous sont corrects pour l'exigence commerciale spécifique, et faux pour tous les autres, il n'y a pas de bien et de mal universel.

    • pourquoi n'y a-t-il pas de copie papier de la facture? Dans la plupart des pays, ce serait une obligation légale et fiscale, quelle est exactement la difficulté de pêcher l'ancienne facture?
    • là où la base de données a l'exigence de stocker les factures clôturées, alors bien sûr, dès que la facture est fermée, vous avez besoin d'une méthode de capture de ces informations.
    • ProductPrice (en fait, je l'appellerais ProductDate) est une bonne idée, mais peut-être pas nécessaire. Mais vous avez raison, vous devez évaluer la devise des données, dans le contexte complet de l'ensemble de la base de données.
    • Je ne vois pas en quoi la copie du prix du produit dans le tableau de facturation aiderait (n'y a-t-il pas de nombreux postes?)
    • dans les bases de données modernes, où la copie de la facture doit être régurgitée, la facture fermée est en outre stockée sous une forme différente, par exemple XML. Un client enregistre les PDF en tant que BLOB. Il n'y a donc pas de problème avec le prix du produit il y a cinq ans. Mais les données de base de la facture sont en ligne et à jour, même pour les factures clôturées; vous ne pouvez tout simplement pas recalculer l'ancienne facture en utilisant les prix actuels.
    • certaines personnes utilisent une table archive_invoice, mais cela a des problèmes car maintenant chaque segment de code ou outil de rapport utilisateur doit regarder à deux endroits (notez que de nos jours certains utilisateurs comprennent mieux les bases de données que la plupart des développeurs)
    • Quoi qu'il en soit, c'est toute la discussion, pour votre compréhension. Aucune des bases de données que j'ai écrites en 30 ans n'a jamais eu ce genre de problème, et toutes sont conformes aux exigences légales et fiscales.
      • La base de données sert à des fins actuelles et d'archivage à partir d'un seul ensemble de tables (pas de tables "d'archivage"
      • Une fois une facture créée, il s'agit d'un document juridique et ne peut être ni modifié ni supprimé (il peut être annulé ou partiellement crédité par une nouvelle facture, avec des valeurs négatives). Ils sont marqués IsIssued/IsPaid/Etc
      • Products ne peut pas être supprimé, ils peuvent être marqués IsObsolete
      • Il existe des tables distinctes pour InvoiceHeader et InvoiceItem
      • InvoiceItem possède des FK à la fois InvoiceHeader et Product
      • pour de nombreuses raisons (pas seulement celles que vous mentionnez), la ligne InvoiceItem contient le NumUnits; ProductPrice; TaxAmount; ExtendedPrice. Bien sûr, cela ressemble à une "dénormalisation", mais ce n'est pas le cas, car les prix, les taux d'imposition, etc., sont susceptibles de changer. Mais plus important encore, l'exigence légale est que nous puissions reproduire l'ancienne facture sur demande.
      • (où il peut être reproduit à partir de fichiers papier, ce n'est pas obligatoire)
      • InvoiceTotalAmount est une colonne dérivée, juste SUM() des InvoiceItems
        .
  2. Ce sont des ordures. Les systèmes comptables et les comptables ne "fonctionnent" pas ainsi.

    • S'il s'agit d'un véritable système comptable, il aura alors JournalEntries, ou "double entry"; c'est ce qu'un compte qualifié doit utiliser (selon la loi).

      • La double entrée ne signifie pas une entrée en double; cela signifie que chaque transaction financière (un montant) doit avoir un compte source et un compte cible auxquels elle est appliquée; il n'y a donc pas de "dénormalisation" ou de duplication. Dans une base de données bancaire, étant donné que les transactions financières se rapportent à des comptes uniques, cela est généralement rendu sous la forme de deux transactions financières distinctes (lignes) dans une transaction Db. Les contraintes de base de données commerciales ordinaires sont utilisées pour garantir qu'il y a deux "côtés" à chaque transaction financière.
        .
    • S'assurer que les factures ne sont pas supprimables est un problème distinct, lié à la sécurité, etc. si quelqu'un est paranoïaque à propos des éléments supprimés de sa base de données,etleur base de données n'a pas été sécurisée par une personne qualifiée, alors ils ont des problèmes plus nombreux et différents qui n'ont rien à voir avec cette question. Obtenez un audit de sécurité et faites tout ce qu'ils vous disent.

    • Il y a quelques personnes sur ce site qui pensent que wiki est un endroit où vous pouvez apprendre quelque chose. Ce n'est pas le cas. C'est un cloaque de "définitions" écrites par des amateurs, et les "définitions" sont constamment modifiées par d'autres amateurs. Aucune définition fixe sur laquelle vous pouvez compter. Alors ne vous inquiétez pas de ce que dit wiki ou de ce que disent les gens, au moment où ils mentionnent wiki, vous savez que leur "connaissance" vient de la lecture et non de la qualification; et ce qu'ils lisent, c'est une fosse d'aisance en constante évolution. Ils discuteront de façon prévisible des "définitions" parce qu'ils n'ont aucune expérience réelle; l'expérimenté va juste continuer son travail

    • Une base de données normalisée est toujours beaucoup plus rapide qu'une base de données non normalisée. Il est donc très important de comprendre ce qu'est la normalisation et la dénormalisation, et ce qu'elle n'est pas. Le processus est considérablement entravé lorsque les gens ont des "définitions" fluides et amateurs, cela conduit simplement à la confusion et à des "discussions" qui font perdre du temps. Lorsque vous avez des définitions fixes, vous pouvez éviter tout cela et simplement continuer le travail.

    • Les tableaux récapitulatifs sont tout à fait normaux, pour économiser du temps et de la puissance de traitement, de recalculer des informations qui ne changent pas, par exemple: les totaux cumulatifs pour chaque année mais cette année; MTD totalise pour chaque mois de cette année mais pas ce mois-ci. "Toujours recalculer" les données est un peu idiot lorsque (a) les informations sont très volumineuses et (b) ne changent pas. Calculer pour le mois en cours uniquement

      • Dans les systèmes bancaires (millions de transactions par jour), chez EndOfDay, nous calculons et stockons également le total quotidien. Celles-ci sont écrasées pour les cinq derniers jours, car les auditeurs apportent des modifications et les entrées de journal contre les transactions financières des 5 derniers jours sont autorisées.
      • les systèmes non bancaires n'ont généralement pas besoin de totaux quotidiens
        .
    • Les tableaux récapitulatifs ne sont pas une "dénormalisation" (sauf aux yeux de ceux qui viennent d'apprendre la "normalisation" de leur "source" fluide magique et en constante évolution; ou en tant que non-praticiens, qui appliquent des règles simples en noir ou blanc à tout). Encore une fois, la définition n'est pas discutée ici; simplement ne s'applique pas aux tableaux récapitulatifs.

    • Les tableaux récapitulatifs n'affectent pas l'intégrité des données (en supposant, bien sûr, que les données dont ils provenaient étaient intégrées).

    • Les tableaux récapitulatifs sont unajoutà la base de données, qui ne sont pas tenus d'avoir les mêmes contraintes que la base de données. Il existe essentiellement des tables de rapports ou des tables d'entrepôt de données, par opposition aux tables de base de données.

    • Il n'y a pas d'anomalies de mise à jour (qui est une définition stricte) liées aux tableaux récapitulatifs. Vous ne pouvez pas modifier ou supprimer une facture de l'année dernière. Les anomalies de mise à jour s'appliquent aux vraies données actuelles dénormalisées ou non normalisées.

44
PerformanceDBA

1) Ceci est une archive. Tout ce qui s'y trouve ne doit jamais être mis à jour. J'irais avec la suggestion du gars senior et je ferais en sorte que ce tableau de facturation soit autonome. Peut-être utiliser un blob pour la facture elle-même qui contient un langage de balisage?

2) Les services de rapports, une table d'entrepôt qui est mise à jour par déclencheur, quelque chose que vous créez par script chaque fois que ... tout irait bien, je pense. Il est en effet idéal d'être normalisé, mais ce n'est pas toujours rapide. J'ai une base de données de santé de bonne taille que je gère, qui est entièrement normalisée ... et puis j'ai une série de tableaux dénormalisés avec des équations enroulées et des champs couramment tirés. Presque tout se déroule à partir de cet ensemble dénormalisé - il est juste plus rapide de les ajouter avec un déclencheur lorsque les fichiers sont chargés que de continuer à tirer de diverses tables chaque fois que je veux consulter un rapport de 100000 enregistrements.

9
Jeff Ferland

Vous soulevez des points valides, mais vous n'êtes pas complètement clair sur la normalisation et ce qu'elle signifie, par exemple dans

1) L'affirmation selon laquelle le fait de conserver les factures telles qu'elles étaient dénormalise les données est totalement et totalement erronée. Prenons le prix par exemple - si vous avez une exigence commerciale qui stipule que vous devez conserver l'historique des prix, alors ne garder que le prix actuel est mauvais et cela casse les exigences. Et cela n'a rien à voir avec la normalisation, il n'est tout simplement pas bien conçu. La dénormalisation consiste à introduire des possibilités d'ambiguïté dans votre modèle (et d'autres artefacts) - et dans ce cas, vous ne modélisez simplement pas correctement votre espace problématique.
Il n'y a rien de mal à modéliser votre base de données pour prendre en charge les données temporelles (ou versionner et/ou séparer les zones de la base de données en archive/temporel et ensemble de travail).

Regarder la normalisation sans regarder la sémantique (en termes d'exigences) n'est pas possible.

De plus, si votre développeur senior ne voit pas la différence, je suppose qu'il n'a pas obtenu son ancienneté dans le développement de SGBDR;)

2) La deuxième partie est en effet la dénormalisation. Cependant, si vous rencontrez un analyste DB principal qui prêche sérieusement la normalisation, vous l'entendrez dire qu'il est parfaitement acceptable de dénormaliser tant que vous le faites consciemment et de vous assurer que les carences en surpoids et les anomalies ne vous mordent pas. Ils vous diront également de normaliser le modèle logique et que dans le modèle physique, vous êtes autorisé à dévier de l'idéal à des fins diverses (performances, maintenance, etc ...). Dans mon livre, le but principal de la normalisation est de ne pas avoir d'anomalies cachées (voir cet article sur 5NF par exemple)

La mise en cache des résultats intermédiaires est autorisée même sur les bases de données normalisées et même par les plus grands évangélistes de la normalisation - vous pouvez le faire au niveau de la couche application (comme une sorte de cache) ou vous pouvez le faire au niveau de la base de données ou vous pouvez avoir un entrepôt de données pour ces fins. Ce sont tous des choix valides et n'ont rien à voir avec la normalisation du modèle logique.

De plus, comme pour votre comptable - vous devriez être en mesure de le convaincre que ce qu'il prétend est pas un bon test et développer un ensemble de tests (peut-être avec lui) qui automatisera le test du système sans intervention des utilisateurs et vous donne une plus grande confiance que votre système est exempt de bogues.

D'un autre côté, je connais des systèmes qui obligent les utilisateurs à saisir des informations en double, telles que le nombre de lignes sur la facture avant ou après la saisie des lignes réelles, pour garantir que la saisie est complète. Ces données sont "dupliquées" et vous n'avez pas à les stocker si vous disposez d'une procédure qui validera l'entrée. Si cette procédure vient plus tard, il est autorisé de stocker les données "dénormalisées" - encore une fois, la sémantique le justifie et vous pouvez regarder le modèle comme normalisé. (il est avantageux d'enrouler la tête autour de ce concept)

EDIT: Le terme "dénormalisé" dans (2) n'est pas correct si vous regardez la définition formelle des formes normales et si vous considérez une conception dénormalisée si il casse l'une des formes normales (pour certaines personnes, c'est évident et il n'y a pas d'autre moyen).

Pourtant, vous voudrez peut-être vous habituer à l'idée que beaucoup de gens et des textes inutiles inutiles utiliseront le terme normalisation pour tout effort qui tente de réduire la redondance dans la base de données (à titre d'exemple, vous trouverez des articles scientifiques, par dont je ne dis pas qu'ils doivent avoir raison, juste comme un avertissement qu'il est courant, cet appel dérive attribue une forme de dénormalisation, voir ici ).

Si vous voulez vous référer à des autorités plus cohérentes et reconnues (encore une fois, pas reconnues par tous), peut-être que les mots du juge en chef peuvent faire une distinction claire:

Une grande partie de la théorie de la conception concerne la réduction de la redondance; la normalisation réduit la redondance au sein des relvars, l'orthogonalité la réduit entre les relvars.

extrait de Base de données en profondeur: théorie relationnelle pour les praticiens

et sur la page suivante

tout comme un échec à normaliser complètement implique une redondance et peut conduire à certaines anomalies, de même un échec à adhérer à l'orthogonalité.

Ainsi, le terme approprié pour la redondance entre les relvars est orthogonalité (fondamentalement, toutes les formes normales parlent de relvar unique, donc si vous regardez strictement la normalisation, cela ne suggérerait aucune amélioration en raison des dépendances entre deux relvars différents).

Quoi qu'il en soit, l'un des autres concepts importants lorsque vous envisagez la conception de bases de données est également une différence entre les modèles de bases de données logiques et physiques. Beaucoup de choses utiles au niveau physique, telles que les tables avec sous-totaux ou index, n'ont pas leur place dans le modèle logique - où vous essayez d'établir et d'étudier les relations entre les concepts que vous essayez de modéliser. Et c'est pourquoi vous pouvez dire qu'ils sont autorisés et qu'ils ne ruinent pas le design.

Les lignes peuvent parfois être un peu floues sur ce qu'est le modèle logique et ce qui est le modèle physique. Un exemple particulièrement intéressant est un tableau avec des sous-totaux. Pour le considérer comme faisant partie de l'implémentation physique et l'ignorer au niveau logique, vous devez:

  • s'assurer que les utilisateurs (et les applications) ne peuvent pas mettre à jour la table des sous-totaux directement d'une manière qui n'est pas cohérente avec leur prédicat (en d'autres termes, avoir un bogue dans la procédure de sous-totalisation)
  • s'assurer que les utilisateurs (et les applications) ne peuvent pas mettre à jour la table dont ils dépendent sans mettre à jour le sous-total (en d'autres termes, certaines applications ne supprimeront pas une ligne de la table détaillée sans mettre à jour le total)

Si vous enfreignez l'une des règles ci-dessus, vous vous retrouverez avec base de données incohérente qui fournira faits incohérents. (Dans un tel cas, si vous souhaitez concevoir formellement une procédure pour résoudre ou examiner les problèmes causés, vous ne le considéreriez pas comme un simple tableau supplémentaire, il existerait au niveau logique; où il ne devrait pas être).

De plus, la normalisation dépend toujours de la sémantique et des règles métier que vous essayez de modéliser. Par exemple, DBAPerformance donne un exemple dans lequel le stockage de TaxAmount dans la table des transactions n'est pas une conception dénormalisée, mais il ne mentionne pas que cela dépend du type de système que vous essayez de modéliser (est-ce évident?); par exemple, si la transaction a un autre attribut appelé TaxRate, elle sera généralement dénormalisée car il existe une dépendance fonctionnelle à un ensemble d'attributs non clés (TaxAmount = Amount * TaxRate => FD: Amount, TaxRate -> TaxAmount) , et l'un d'eux devrait être supprimé ou garanti pour être cohérent.

Évidemment, vous pourriez dire, mais, si le système que vous construisez est pour une société d'audit, alors vous pourriez ne pas avoir de dépendance fonctionnelle - ils pourraient auditer quelqu'un qui utilise des calculs manuels ou a un logiciel défectueux ou doit avoir la capacité d'enregistrer des données incomplètes et le calcul peut être erroné à l'origine et en tant que société d'audit, vous devez enregistrer le fait tel qu'il s'est produit.

Ainsi, la sémantique (prédicats) qui est déterminée par les exigences influencera si l'une des formes normales est rompue - en influençant les dépendances fonctionnelles (en d'autres termes, l'établissement correct des dépendances fonctionnelles est une partie très importante de la modélisation lorsque vous vous efforcez de créer une base de données normalisée).

7
Unreason

Votre développeur senior fait valoir des points extrêmement valables. Je les ai moi-même appris à la dure en entretenant des systèmes qui ne dénormalisent pas les données historiques.

Dans un sens, cela n'ajoute pas vraiment de surcharge à la base de données. Vous créez des tables de facturation à partir de données existantes dans la base de données. Une facture est un instantané dans le temps. La dénormalisation des informations dont vous avez besoin pour produire cette facture peut faciliter votre reporting SO beaucoup plus facile. Lorsque vous devez produire un nouveau rapport et que vous devez le faire rapidement, vous apprécierez la dénormalisation .

En termes de total dans la base de données. Cela m'a sauvé le cul avant d'avoir apporté une modification à une application qui empêchait les chiffres de s'additionner de la même manière (pas aussi difficile que vous ne le pensez). Sur une application en direct, les totaux m'ont donné un endroit précis où retourner pour corriger les écarts. J'ai déjà écrit à ce sujet, vous pouvez le lire ici: http://jlrand.com/?p=95

4
Jonathan Rand

Je suis d'accord avec votre aîné sur (1). Une ligne de table de transaction doit capturer l'état entier au moment de la transaction. Période. Ce que vous proposez n'enregistre pas les données réelles, c'est donc irrecevable. Je suis également d'accord sur (2). Tout ce que l'entreprise souhaite en termes de recoupement, vous devez le mettre en œuvre. La comptabilité est basée sur le recoupement, la double saisie, le roulage des registres, etc. Vous devez le faire. C'est tellement fondamental que vous ne devriez même pas le considérer comme une dénormalisation, tout comme la mise en œuvre des exigences de l'entreprise.

4
user207421

1) Ne nécessite pas de dénormalisation. Il vous suffit de déterminer le niveau de détail de chaque changement dont vous avez besoin et de le conserver avec une clé appropriée.

2) N'a rien à voir avec la dénormalisation. Le stockage des données récapitulatives ne rend pas la base de données dénormalisée. Stocker les résultats dérivés d'attributs non clés dans le même tablea serait un exemple de dénormalisation mais cela ne semble pas être ce dont vous parlez ici.

3
nvogel

Pour une

La facture doit être calculée à partir des ventes et des paiements. Si vous ne disposez pas de données de vente détaillées, y compris le prix/produit/remise/expédition/etc, commencez par là.

Pour 2

Écrire un système comptable dans la base de données à partir de zéro est un gros projet. Assurez-vous que les comptables vous donnent des règles commerciales afin que vous puissiez mesurer la précision de vos systèmes. La dernière chose que vous voulez est l'étape CFO dans la réunion DBA et annoncez que la base de données surcharge le client, pire encore, vous sous-facturez et conduisez l'entreprise à la faillite.

Si vous avez SQL Server, jetez un œil à la base de données Adventure Works. Si vous détestez la SP, regardez Adventure Works et ne le faites pas de cette façon.

1
RC_Cleland

Il semble que si vous envisagez plutôt ou non de créer un entrepôt de données. Vous ne devez jamais dénormaliser votre base de données à des fins de génération de rapports historiques. La création d'une archive et le stockage de vos informations dans votre entrepôt de données permettent à la fois de dénormaliser la plupart des informations et de conserver l'historique de vos données.

0
Lesly Revenge

La normalisation de la base de données supprime les doublons et rend les requêtes SQL pour la mise à jour des données plus efficaces (et apporte quelques autres améliorations).

Mais si la plupart de vos requêtes sont utilisées pour la sélection de données et que les requêtes de sélection se connectent à plusieurs tables à la fois, vous pouvez envisager la dénormalisation de ces tables. Il augmentera la quantité d'espace disque nécessaire pour les données, le temps d'exécution des requêtes de mise à jour SQL mais améliorera certaines requêtes.

0
Alexandr