web-dev-qa-db-fra.com

Pourquoi voudriez-vous stocker une énumération dans DB?

J'ai vu un certain nombre de questions, comme this , demandant des conseils sur la façon de stocker les énumérations dans DB. Mais je me demande pourquoi feriez-vous cela. Supposons donc que j'ai une entité Person avec un champ gender et une énumération Gender. Ensuite, ma table personne a un sexe de colonne.

Outre la raison évidente de l'application de l'exactitude, je ne vois pas pourquoi je créerais une table supplémentaire gender pour mapper ce que j'ai déjà dans mon application. Et je n'aime pas vraiment avoir cette duplication.

71
user3748908

Prenons un autre exemple moins chargé de conceptions et d'attentes. J'ai une énumération ici, et c'est l'ensemble des priorités pour un bug.

Quelle valeur stockez-vous dans la base de données?

Donc, je pourrais stocker 'C', 'H', 'M', et 'L' dans la base de données. Ou 'HIGH' etc. Cela a le problème des données typées en chaîne . Il existe un ensemble connu de valeurs valides, et si vous ne stockez pas cet ensemble dans la base de données, il peut être difficile de travailler avec.

Pourquoi stockez-vous les données dans le code?

Tu as List<String> priorities = {'CRITICAL', 'HIGH', 'MEDIUM', 'LOW'}; ou quelque chose à cet effet dans le code. Cela signifie que vous disposez de plusieurs mappages de ces données au format approprié (vous insérez toutes les majuscules dans la base de données, mais vous les affichez sous la forme Critical). Votre code est désormais également difficile à localiser. Vous avez lié la représentation de base de données de l'idée à une chaîne qui est stockée dans le code.

Partout où vous devez accéder à cette liste, vous devez soit avoir une duplication de code, soit une classe avec un tas de constantes. Aucune de ces options n'est bonne. Il ne faut pas non plus oublier qu'il existe d'autres applications qui peuvent utiliser ces données (qui peuvent être écrites dans d'autres langues - le Java l'application Web a un Crystal Reports système de reporting utilisé et un Perl batch job alimentant en données). Le moteur de reporting aurait besoin de connaître la liste valide des données ( que se passe-t-il s'il n'y a rien marqué dans 'LOW' priorité et vous devez savoir qu'il s'agit d'une priorité valide pour le rapport?), et le travail par lots aurait les informations sur les valeurs valides.

En théorie, vous pourriez dire "nous sommes une boutique en une seule langue - tout est écrit en Java" et avoir un seul .jar qui contient ces informations - mais maintenant cela signifie que vos applications sont étroitement couplées les unes aux autres et que .jar contient les données. Vous devrez libérer la partie de rapport et la partie de mise à jour par lots avec l'application Web chaque fois qu'il y a un changement - et espérer que cette version disparaisse en douceur pour toutes les pièces.

Que se passe-t-il lorsque votre patron veut une autre priorité?

Votre patron est venu aujourd'hui. Il y a une nouvelle priorité - CEO. Maintenant, vous devez aller changer tout le code et faire une recompilation et redéployer.

Avec une approche "enum-in-the-table", vous mettez à jour la liste enum pour avoir une nouvelle priorité. Tout le code qui obtient la liste l'extrait de la base de données.

Les données sont rarement isolées

Avec les priorités, les données sont entrées dans d'autres tables qui peuvent contenir des informations sur les workflows, ou qui peut définir cette priorité ou autre chose.

Pour en revenir un peu au genre mentionné dans la question: le genre a un lien avec les pronoms utilisés: he/his/him et she/hers/her... et vous voulez éviter de coder en dur cela dans le code lui-même. Et puis votre patron passe et vous devez ajouter que vous avez le 'OTHER' sexe (pour faire simple) et vous devez associer ce genre à they/their/them... et votre patron voit ce que Facebook a et ... eh bien, oui.

En vous limitant à un bit de données de type chaîne plutôt qu'à une table d'énumération, vous avez maintenant besoin de répliquer cette chaîne dans un tas d'autres tables pour maintenir cette relation entre les données et ses autres bits.

Qu'en est-il des autres banques de données?

Peu importe où vous le stockez, le même principe existe.

  • Vous pourriez avoir un fichier, priorities.prop, qui a la liste des priorités. Vous lisez cette liste dans un fichier de propriétés.
  • Vous pourriez avoir une base de données de magasin de documents (comme CouchDB ) qui a une entrée pour enums (puis écrire une fonction de validation en JavaScript ):

    {
       "_id": "c18b0756c3c08d8fceb5bcddd60006f4",
       "_rev": "1-c89f76e36b740e9b899a4bffab44e1c2",
       "priorities": [ "critical", "high", "medium", "low" ],
       "severities": [ "blocker", "bad", "annoying", "cosmetic" ]
    }
    
  • Vous pourriez avoir un fichier XML avec un peu d'un schéma:

    <xs:element name="priority" type="priorityType"/>
    
    <xs:simpleType name="priorityType">
      <xs:restriction base="xs:string">
        <xs:enumeration value="critical"/>
        <xs:enumeration value="high"/>
        <xs:enumeration value="medium"/>
        <xs:enumeration value="low"/>
      </xs:restriction>
    </xs:simpleType>
    

L'idée centrale est la même. Le magasin de données lui-même est l'endroit où la liste des valeurs valides doit être stockée et appliquée. En le plaçant ici, il est plus facile de raisonner sur le code et les données. Vous n'avez pas à vous soucier de vérifier défensivement ce que vous avez à chaque fois (est-ce en majuscule? Ou en bas? Pourquoi y a-t-il un type chritical dans cette colonne? Etc ...) parce que vous savez ce que vous êtes le retour de la banque de données est exactement ce que la banque de données attend de votre part - et vous pouvez interroger la banque de données pour obtenir une liste de valeurs valides.

À emporter

L'ensemble des valeurs valides est les données , pas le code. Vous devez vous efforcer de SEC code - mais le problème de la duplication est que vous dupliquez les données dans le code, plutôt que de respecter sa place en tant que données et de les stocker dans une base de données.

Il facilite l'écriture de plusieurs applications sur le magasin de données et évite d'avoir des instances où vous devrez déployer tout ce qui est étroitement couplé aux données elles-mêmes - parce que vous n'avez pas couplé votre code aux données.

Cela facilite le test des applications car vous n'avez pas à retester l'application entière lorsque la priorité CEO est ajoutée - car vous n'avez pas de code qui se soucie de la valeur réelle de la priorité.

Pouvoir raisonner sur le code et les données indépendamment les uns des autres facilite la recherche et la correction des bogues lors de la maintenance.

77
user40980

Selon vous, laquelle est la plus susceptible de produire des erreurs lors de la lecture de la requête?

select * 
from Person 
where Gender = 1

Ou

select * 
from Person join Gender on Person.Gender = Gender.GenderId
where Gender.Label = "Female" 

Les gens créent des tables d'énumération dans SQL car ils trouvent que ces dernières sont plus lisibles, ce qui réduit le nombre d'erreurs lors de l'écriture et de la maintenance de SQL.

Vous pouvez faire du genre une chaîne directement dans Person, mais vous devrez alors essayer d'appliquer la casse. Vous pouvez également augmenter le hit de stockage pour la table et le temps de requête en raison de la différence entre les chaînes et les entiers selon la puissance de votre base de données pour optimiser les choses.

21
Telastyn

Je ne peux pas croire que les gens ne l'aient pas encore mentionné.

Clés étrangères

En conservant l'énumération dans votre base de données et en ajoutant une clé étrangère sur la table qui contient une valeur d'énumération, vous assurez-vous qu'aucun code n'entre jamais de valeurs incorrectes pour cette colonne. Cela contribue à l'intégrité de vos données et constitue la raison la plus évidente pour l'OMI de disposer de tables pour les énumérations.

13
Benjamin Gruenbaum

Je suis dans le camp qui est d'accord avec toi. Si vous conservez une énumération de genre dans votre code et un tblGender dans votre base de données, vous risquez de rencontrer des problèmes lors de la maintenance. Vous devrez documenter que ces deux entités doivent avoir les mêmes valeurs et donc toutes les modifications que vous apportez à l'une doivent également être apportées à l'autre.

Vous devrez ensuite transmettre les valeurs d'énumération à vos procédures stockées comme suit:

create stored procedure InsertPerson @name varchar, @gender int
    insert into tblPeople (name, gender)
    values (@name, @gender)

Mais réfléchissez à la façon de procéder si vous conserviez ces valeurs dans une table de base de données:

create stored procedure InsertPerson @name varchar, @genderName varchar
    insert into tblPeople (name, gender)
    select @name, fkGender
    from tblGender
    where genderName = @genderName --I hope these are the same

Bien sûr, les bases de données relationnelles sont construites en tenant compte des jointures, mais quelle requête est plus facile à lire?


Voici un autre exemple de requête:

create stored procedure SpGetGenderCounts
    select count(*) as count, gender
    from tblPeople
    group by gender

Comparez cela à ceci:

create stored procedure SpGetGenderCounts
    select count(*) as count, genderName
    from tblPeople
    inner join tblGender on pkGender = fkGender
    group by genderName --assuming no two genders have the same name

Voici encore un autre exemple de requête:

create stored procedure GetAllPeople
    select name, gender
    from tblPeople

Notez que dans cet exemple, vous devrez convertir la cellule de genre dans vos résultats d'un int en une énumération. Ces conversions sont cependant faciles. Comparez cela à ceci:

create stored procedure GetAllPeople
    select name, genderName
    from tblPeople
    inner join tblGender on pkGender = fkGender

Toutes ces requêtes sont plus petites et plus faciles à gérer lorsque vous envisagez de conserver les définitions d'énumération hors de la base de données.

7
user2023861

Je créerais une table de genre pour la raison qu'elle peut être utilisée dans l'analyse de données. Je pouvais rechercher toutes les personnes de sexe masculin ou féminin dans la base de données pour générer un rapport. Plus vous pourrez visualiser vos données, plus il sera facile de découvrir des informations sur les tendances. Évidemment, il s'agit d'une énumération très simple, mais pour les énumérations complexes (comme les pays du monde ou les États), cela facilite la génération de rapports spécialisés.

1
zackery.fix

Lorsque vous disposez d'une énumération de code utilisée pour piloter la logique métier dans le code, vous devez toujours créer une table pour représenter les données dans la base de données pour les nombreuses raisons détaillées ci-dessus/ci-dessous. Voici quelques conseils pour vous assurer que vos valeurs de base de données restent synchronisées avec les valeurs de code:

  1. Ne faites pas du champ ID de la table une colonne d'identité. Inclure l'ID et la description comme champs.

  2. Faites quelque chose de différent dans le tableau pour aider les développeurs à savoir que les valeurs sont semi-statiques/liées à une énumération de code. Dans toutes les autres tables de recherche (généralement lorsque des valeurs peuvent être ajoutées par les utilisateurs), j'ai généralement un LastChangedDateTime et LastChangedBy, mais ne pas les avoir sur les tables liées à l'énumération m'aide à me rappeler qu'elles ne sont modifiables que par les développeurs. Documentez cela.

  3. Créez un code de vérification qui vérifie que chaque valeur de l'énumération se trouve dans la table correspondante et que seules ces valeurs se trouvent dans la table correspondante. Si vous avez des "tests de santé" d'application automatisés qui s'exécutent après la construction, cliquez ici. Sinon, exécutez le code automatiquement au démarrage de l'application chaque fois que l'application s'exécute dans l'EDI.

  4. La création de production fournit des scripts SQL qui font de même, mais depuis l'intérieur de la base de données. S'ils sont créés correctement, ils aideront également aux migrations d'environnement.

1
Paul Schirf

Vous devez d'abord décider si la base de données ne sera jamais utilisée que par une seule application ou s'il existe un potentiel pour que plusieurs applications l'utilisent. Dans certains cas, une base de données n'est rien de plus qu'un format de fichier pour une application (les bases de données SQLite peuvent souvent être utilisées à cet égard). Dans ce cas, la duplication de bits de la définition d'énumération sous forme de table peut souvent être correcte et peut avoir plus de sens.

Cependant, dès que vous souhaitez envisager la possibilité d'avoir plusieurs applications accédant à la base de données, un tableau pour l'énumération a beaucoup de sens (les autres réponses expliquent pourquoi plus en détail). L'autre chose à considérer est que vous ou un autre développeur souhaitiez consulter les données brutes de la base de données. Si c'est le cas, cela peut être considéré comme une autre utilisation de l'application (une seule où la jauge de laboratoire est du SQL brut).

Si vous avez défini l'énumération dans le code (pour un code plus propre et la vérification du temps de compilation) ainsi qu'une table dans la base de données, je recommanderais d'ajouter des tests unitaires pour vérifier que les deux sont synchronisés.

1
Eric Johnson

Cela dépend également de qui accède aux données. Si vous n'avez qu'une seule application, cela pourrait convenir. Si vous ajoutez un entrepôt de données ou un système de rapports. Ils auront besoin de savoir ce que signifie ce code, quelle est la version humaine du code.

Habituellement, la table de types ne serait pas dupliquée en tant qu'énumération dans le code. Vous pouvez charger la table de types dans une liste qui est mise en cache.

Class GenderList

   Public Shared Property UnfilteredList
   Public Shared Property Male = GetItem("M")
   Public Shared Property Female = GetItem("F")

End Class

Souvent, tapez va et vient. Vous auriez besoin d'une date pour l'ajout du nouveau type. Sachez quand un type spécifique a été supprimé. N'affichez-le qu'en cas de besoin. Que se passe-t-il si un client veut "transgenre" en tant que sexe mais que d'autres clients ne le veulent pas? Toutes ces informations sont mieux stockées dans la base de données.

0
the_lotus