Données de configuration: table à une seule rangée par rapport à la table paire paire nom-valeur

64

Supposons que vous écriviez une application pouvant être configurée par l'utilisateur. Pour stocker ces "données de configuration" dans une base de données, deux modèles sont couramment utilisés.

  1. La table à une rangée

      CompanyName  |  StartFullScreen  |  RefreshSeconds  |  ...
    ---------------+-------------------+------------------+--------
      ACME Inc.    |        true       |       20         |  ...
    
  2. La table de paires nom-valeur

      ConfigOption   |   Value
    -----------------+-------------
     CompanyName     | ACME Inc.
     StartFullScreen | true (or 1, or Y, ...)
     RefreshSeconds  | 20
     ...             | ...
    

J'ai vu les deux options à l'état sauvage, et les deux ont des avantages et des inconvénients évidents, par exemple:

  • Les tables à une seule ligne limitent le nombre d'options de configuration que vous pouvez avoir (car le nombre de colonnes dans une ligne est généralement limité). Chaque option de configuration supplémentaire nécessite un changement de schéma de base de données.
  • Dans une table de paires nom-valeur, tout est "typé de manière stricte" (vous devez encoder / décoder vos paramètres Boolean / Date / etc.).
  • (beaucoup plus)

Existe-t-il un consensus au sein de la communauté du développement sur quelle option est préférable?

Heinzi
la source
2
Il n'y a aucune raison pour que l'approche "verticale" ne puisse pas avoir différents types de données. Ajoutez une colonne int, float et text par ligne. Enregistrez / chargez ses valeurs à l'aide de fonctions spécifiques au type, telles que 'SaveConfigInt (' field ', n)'
GrandmasterB
4
Il y a une excellente question StackOverflow qui pose des questions à ce sujet et la réponse principale donne le pour et le contre aux deux approches. stackoverflow.com/questions/2300356/…
Kevin - Rétablir Monica
1
Approche 3: Colonne unique / Ligne unique avec un format d'échange de données simple tel que JSON ou YAML. Combine les avantages des deux approches.
schlamar
Et si vous utilisiez un tableau à une seule ligne avec des données complexes contenant XML / JSON, tel que <config> <Nom de la société> ACME Inc. </ Nom de la société> <StartFullScreen> true </ StartFullScreen> 20 <RefreshSeconds> </ RefreshSeconds> </ config> et valider un objet dans la couche de gestion?
Jean
1
@ John: Bonne idée, si des structures hiérarchiques sont nécessaires. S'ils ne le sont pas, il ne s'agit que de l'option 2 avec une complexité accrue.
Heinzi

Réponses:

15

Personnellement, je préfère les tables à une seule rangée pour la plupart des choses. Bien que ce soit moins flexible, à moins d’attendre un comportement dynamique, il est parfaitement acceptable d’ajouter des colonnes ultérieurement si vous en avez besoin. En un sens, cela équivaut à utiliser un dictionnaire / une carte pour contenir des paires nom-valeur plutôt que d'avoir des membres de classe lors de la programmation. Certes, ce n’est pas une métaphore parfaite, mais de nombreux avantages et inconvénients sont mis en parallèle lorsque vous y réfléchissez.

Alors, utiliseriez-vous un dictionnaire / une carte sur les membres de la classe? Probablement pas, sauf si vous aviez des raisons de penser que la quantité de données à représenter est entièrement adaptable, un peu comme si vous disposiez d'un tableau de paires nom-valeur.

Neil
la source
Que se passe-t-il si les données à stocker sont définies par l'utilisateur? En d'autres termes, imaginez une interface utilisateur dans laquelle l'utilisateur peut créer un "champ" en spécifiant le libellé du champ, le type de données qu'il contiendra, etc. Cela impliquerait l'exécution d'instructions DDL à partir de code. Seriez-vous toujours aller avec l'option 1?
devanalyst
1
@devanalyst Non, si les données pouvaient changer d'un composant à l'autre, cela n'aurait aucun sens d'essayer de créer une table statique pour la représenter. Dans ce cas, il serait préférable d’utiliser la deuxième option.
Neil
12

J'irais généralement avec l'option 2 MAIS j'aurais plusieurs colonnes pour appliquer le type de données

ConfigOption   |   textValue    |   DateValue   |   NumericValue

L'option 1 présente l'avantage supplémentaire que vous pouvez très facilement "échanger" des configurations entières en ajoutant une Activecolonne.

Crétins
la source
Si vous souhaitez autoriser la désactivation des configurations (pour l'option 1), faites-en au moins un activatedOnhorodatage afin que vous puissiez savoir quand l' activation a eu lieu. Et si vous choisissez l'option 2 ... que se passera-t-il si vous finissez par stocker des valeurs dans plusieurs colonnes (ou c'est oracle, où (apparemment) null et une chaîne vide sont équivalents)?
Clockwork-Muse
1
@ X-Zero, l'enregistrement de plusieurs configurations est généralement effectué à des fins de test, mais un horodatage ne peut pas nuire. La maintenance de la configuration, appelez pour obtenir la valeur, saura savoir quelle colonne vérifier, si vous le vouliez vraiment, vous pourriez ajouter une colonne pour le type de données .. Mais je pense que c’est fini de tuer ...
Morons
5
un schéma EATV (Entity-Attribute-Type-Value) rompt la troisième forme normale; la colonne Type est uniquement liée indirectement à la clé primaire de la table, par le biais de la colonne Valeur décrite par la colonne Type. De plus, le stockage de type dynamique et l'instanciation ne résolvent pas beaucoup; Si une méthode GetConfigValue () peut renvoyer n'importe quel type, elle doit aussi renvoyer Object (ou recevoir le type attendu) qui doit toujours être évaluée au moment de l'exécution.
KeithS
5
À chaque fois que l'option 1 a été implémentée dans un logiciel que j'ai vu, elle devait être convertie en option 2. L'option 2 est plus facile à gérer dans le temps, cela prend juste un peu plus de temps pour une implémentation correcte la première fois. L'option 1 est rapide et facile à mettre en œuvre, mais la maintenance dans le temps est horrible à moins que votre logiciel ne soit minuscule et sans chance de développement.
Jimmy Hoffa
8

Pour moi, que vous choisissiez une seule ligne ou EAV dépend de la façon dont vous voulez les consommer.

Le pouvoir d'EAV réside dans le fait que de nouvelles données peuvent être ajoutées sans changement de structure. Cela signifie que si vous souhaitez une nouvelle valeur de configuration, il vous suffit de l'ajouter à la table et de l'extraire à l'endroit souhaité dans le code. Il n'est pas nécessaire d'ajouter un nouveau champ au domaine, au schéma, au mappage ou aux requêtes DAL. , etc.

Son défaut est qu’il n’a que la structure la plus simple qui soit, ce qui vous oblige à traiter les données avec pessimisme. Toute utilisation d’une valeur de configuration doit s’attendre à ce que la valeur ne soit pas présente, ou au format approprié, et se comporte en conséquence lorsque cela n’est pas le cas. Une valeur de configuration peut ne pas être analysable en double, en int ou en char. Cela peut être nul. il peut ne pas y avoir de ligne pour la valeur du tout. Pour contourner ce problème, il suffit généralement d’une seule valeur "par défaut" valide pour toutes les valeurs de configuration d’un type particulier de code ( extrêmement rare; la valeur par défaut est tout aussi problématique pour la consommation de code), ou pour Conservez un dictionnaire codé en dur des valeurs par défaut (qui doit changer chaque fois qu'une nouvelle colonne est ajoutée, rendant le principal avantage du stockage EAV peu utile).

Une seule rangée large est à peu près le contraire. Vous le mappez sur une seule instance d'un objet de configuration avec un champ / une propriété pour chaque valeur de configuration existante. Vous savez exactement quel type ces valeurs doivent être au moment de la compilation et vous "échouez vite" dans le DAL si une colonne de configuration n'existe pas ou n'a pas une valeur du type approprié, ce qui vous donne un emplacement pour capturer les exceptions en fonction sur les problèmes de récupération / hydratation de la configuration.

Le principal inconvénient est qu'un changement structurel est nécessaire pour chaque nouvelle valeur; nouvelle colonne de base de données, nouvelle colonne dans la couche DAL (le mappage ou les requêtes / SP SQL), nouvelle colonne de domaine, nécessaires pour tester correctement l'utilisation.

La situation appropriée pour utiliser l'un ou l'autre est la situation dans laquelle les inconvénients sont atténués. Pour moi, la plupart des situations de codage de configuration ont nécessité une implémentation à une seule ligne. Cela est principalement dû au fait que si vous introduisez une toute nouvelle valeur de configuration régissant le comportement d'une partie de votre programme, vous devez déjà modifier le code pour utiliser la nouvelle valeur de configuration. pourquoi ne pas aller sur l'objet config et ajouter la valeur à utiliser?

En bref, un schéma EAV pour stocker la configuration ne résout pas vraiment le problème qu’il est censé résoudre, et la plupart des solutions de contournement aux problèmes qu’il présente viole DRY.

KeithS
la source
3

Spécifiquement pour les valeurs de configuration, je dirais - aller avec la ligne unique. À moins que vous ne réalisiez actuellement le développement, à quelle fréquence ces colonnes vont-elles changer de toute façon?

Il est probablement préférable de sécuriser le type de données des valeurs plutôt que le code pour une extensibilité que vous ne rencontrerez probablement pas dans le temps mort entre les versions importantes (r). En outre, l'ajout ou la suppression d'une seule colonne est la migration la plus simple qui soit. Je ne prévois pas de maux de tête lors de la création d'une nouvelle option de configuration.

De plus, vous avez dit que les "utilisateurs" peuvent configurer ces options sans donner de plafond. S'agit-il de configurations par utilisateur? Si tel est le cas, je soutiendrai encore plus fortement que les options de configuration devraient figurer dans les colonnes - une seule ligne par utilisateur. Cela vous évitera beaucoup de maux de tête plus tard.

Izkata
la source
2

Si vos clients peuvent traiter des fragments JSON (non seulement des tableaux et des dictionnaires, mais également des chaînes, des nombres, des booléens, des valeurs null), vous pouvez avoir un tableau à plusieurs lignes avec le nom de l'option et une valeur de chaîne contenant du JSON. Cela vous permet également de stocker des valeurs structurées, et le code pour les traiter devrait déjà être là.

Si vos clients ne peuvent pas traiter les fragments JSON, procurez-vous de nouveaux clients.

gnasher729
la source
1

Rangée simple Avantages: bien définis. Inconvénients: Changer la configuration peut être une douleur. Migrations de base de données, etc.

Avantages de l'entité: Super flexible, prend en charge l'évolution de votre configuration. Inconvénients: intégrité référentielle? Plus de contrôles dans votre code pour voir si la propriété existe avant que vous puissiez faire quoi que ce soit dessus.

Je prendrais l'approche 2 soutenue par une base de données non relationnelle comme Mongo. S'il y a quelque chose dont vous pouvez être sûr, c'est son changement.

JVXR
la source
1

Utilise les deux!

Triez les options pouvant avoir plusieurs instances et les options génériques.

La table simple rangée (configurations)

  id  |  company_name  |  start_fullscreen  |  refresh_seconds  |  ...
------+----------------+--------------------+-------------------+-------
  4   |  ACME Inc.     |  true              |  20               |  ...

La table de paires nom-valeur (options)

  name             |  value          | update_time  
-------------------+-----------------+--------------
  generic_option_1 |  Option 1 Value | timestamp    
  generic_option_2 |  Option 2 Value | timestamp    
  generic_option_3 |  Option 3 Value | timestamp    
  configuration    |  4              | timestamp    
  ...              |  ...            | ...          

Je pense que c'est plus flexible.

Andrew Luca
la source