Utilisation d'une table de configuration à ligne unique dans la base de données SQL Server. Mauvaise idée?

145

En développant une application de panier, j'ai constaté que je devais enregistrer les paramètres et les configurations en fonction des préférences et des exigences de l'administrateur. Ces informations peuvent être des informations sur l'entreprise, des identifiants de compte d'expédition, des clés API PayPal, des préférences de notification, etc.

Il semble tout à fait inapproprié de créer une table pour stocker une seule ligne dans un système de base de données relationnelle.

Quelle est la manière appropriée de stocker ces informations?

Remarque: mon SGBD est SQL Server 2008 et la couche de programmation est implémentée avec ASP.NET (en C #).

David Murdoch
la source

Réponses:

189

J'ai fait cela de deux manières dans le passé - une table à une seule ligne et une table de paires clé / valeur - et chaque approche présente des avantages et des inconvénients.

Une seule rangée

  • positif: les valeurs sont stockées dans le bon type
  • positif: il est plus facile de traiter dans le code (grâce à ce qui précède)
  • positif: des valeurs par défaut peuvent être attribuées à chaque paramètre individuellement
  • négatif: un changement de schéma est nécessaire pour ajouter un nouveau paramètre
  • négatif: le tableau peut devenir très large s'il y a beaucoup de réglages

Paire clé / valeur

  • positif: l'ajout de nouveaux paramètres ne nécessite pas de changement de schéma
  • positif: le schéma de la table est étroit, avec des lignes supplémentaires utilisées pour les nouveaux paramètres
  • négatif: chaque paramètre a la même valeur par défaut (nul / vide?)
  • négatif: tout doit être stocké sous forme de chaînes (ie. nvarchar)
  • négatif: lorsque vous traitez les paramètres dans le code, vous devez savoir de quel type est un paramètre et le cast

L'option à une seule ligne est de loin la plus simple à utiliser. Cela est dû au fait que vous pouvez stocker chaque paramètre dans son type correct dans la base de données sans avoir à stocker les types de paramètres ainsi que leurs clés de recherche dans le code.

Une chose qui m'intéressait en utilisant cette approche était d'avoir plusieurs lignes dans le tableau des paramètres "spéciaux" d'une seule ligne. J'ai surmonté cela par (dans SQL Server):

  • ajout d'une nouvelle colonne de bits avec une valeur par défaut de 0
  • création d'une contrainte de vérification pour s'assurer que cette colonne a une valeur de 0
  • création d'une contrainte unique sur la colonne de bits

Cela signifie qu'une seule ligne peut exister dans la table car la colonne de bits doit avoir une valeur de 0, mais il ne peut y avoir qu'une seule ligne avec cette valeur en raison de la contrainte d'unicité.

Adrianbanks
la source
5
Nous faisons la chose sur une seule ligne dans notre application LOB. Les valeurs sont toutes du type correct, ce qui rend leur utilisation dans l'application beaucoup plus simple. Notre schéma est versionné avec l'application, donc une modification de la configuration de la configuration est gérée comme toute révision d'application.
DaveE
17
Une seule ligne positive: peut avoir FK défini sur certaines colonnes!
wqw
8
Vous pouvez toujours faire une paire clé / valeur avec un identificateur de type pour déterminer quelle colonne a la valeur dans son type de valeur. Cela vous donne le meilleur des deux mondes et vous pouvez utiliser une procédure stockée pour obtenir la valeur lorsque vous en avez besoin.
Middletone
19
Une chose qui peut vraiment gâcher votre journée après la mise en œuvre de la solution à une seule ligne est lorsque vous êtes plus tard chargé de "garder une trace de la dernière fois que chaque valeur a été modifiée et qui l'a changée ..."
Dave Mateer
6
Autre avantage de la solution à une seule ligne, que j'ai découvert dans un cas: j'avais une application construite pour un client, avec une table à une seule ligne pour les «paramètres». J'ai eu plus tard deux autres clients qui voulaient utiliser la même application, mais voulaient des paramètres différents: tout ce que j'avais à faire était d'ajouter un PK "client_id" à la table afin de maintenir un ensemble de paramètres séparé pour chaque client. (C'est à ce moment-là que vous vous rendez compte que ces "paramètres" ne sont en réalité que des attributs pour une entité de niveau supérieur que vous n'avez pas encore modélisée.)
Jeffrey Kemp
10

Vous devez créer une table avec une colonne pour le type d'information et la valeur d'information (au moins). De cette façon, vous évitez d'avoir à créer de nouvelles colonnes à chaque fois qu'une nouvelle information est ajoutée.

Otávio Décio
la source
1
Simple et soigné. Travaillez simplement avec une liste de paires valeur / clé à partir de là. Vous voudrez peut-être réfléchir un peu aux valeurs par défaut, cela dépend du contexte d'utilisation ...
Paul Kohler
4
Pourquoi créer de nouvelles colonnes pose-t-il un problème? Je sais qu'il y a des situations où les développeurs doivent l'éviter en raison de problèmes politiques liés à la mise à jour des schémas SQL, mais cela n'est pas mentionné dans la question.
finnw
6

Une seule ligne fonctionnera bien; il aura même des types forts:

show_borders    bit
admin_name      varchar(50)
max_users       int

Un inconvénient est qu'il nécessite un changement de schéma ( alter table) pour ajouter un nouveau paramètre. Une alternative est la normalisation, où vous vous retrouvez avec une table comme:

pref_name       varchar(50) primary key
pref_value      varchar(50) 

Cela a des types faibles (tout est un varchar), mais l'ajout d'un nouveau paramètre consiste simplement à ajouter une ligne, ce que vous pouvez faire avec un accès en écriture à la base de données.

Andomar
la source
4

Personnellement, je le stockerais sur une seule ligne si c'est ce qui fonctionne. Overkill pour le stocker dans une table SQL? probablement, mais il n'y a pas de réel mal à le faire.

EJ Brennan
la source
4

Comme vous l'avez deviné, et à l'exception des situations les plus simples, mettre tous les paramètres de configuration sur une seule ligne présente de nombreux inconvénients. C'est une mauvaise idée...

Un moyen pratique de stocker les informations de configuration et / ou de préférence utilisateur est en XML . De nombreux SGBD prennent en charge le type de données XML. La syntaxe XML vous permet de développer le «langage» et la structure décrivant la configuration à mesure que cette configuration évolue. Un avantage de XML est sa prise en charge implicite de la structure hiérarchique, permettant par exemple de stocker de petites listes de paramètres de configuration sans avoir à les nommer avec un suffixe numéroté. Un inconvénient possible du format XML est que la recherche et la modification générale de ces données ne sont pas aussi simples que d'autres approches (rien de compliqué, mais pas aussi simple / naturel)

Si vous souhaitez rester plus proche du modèle relationnel , le modèle Entité-Attribut-Valeur est probablement ce dont vous avez besoin, dans lequel les valeurs individuelles sont stockées dans une table qui ressemble généralement à:

EntityId     (foreign key to the "owner" of this attribute)
AttributeId  (foreign key to the "metadata" table where the attribute is defined)
StringValue  (it is often convenient to have different columns of different types
IntValue      allowing to store the various attributes in a format that befits 
              them)

Dans lequel AttributeId est une clé étrangère vers une table où chaque attribut possible ("paramètre de configuration" dans votre cas) est défini, par exemple

AttributeId  (Primary Key)
Name
AttributeType     (some code  S = string, I = Int etc.)
Required          (some boolean indicating that this is required)
Some_other_fields   (for example to define in which order these attributes get displayed etc...)

Enfin, EntityId vous permet d'identifier une entité qui "possède" ces divers attributs. Dans votre cas, il peut s'agir d'un UserId ou même simplement implicite si vous n'avez qu'une seule configuration à gérer.

En plus de permettre à la liste des paramètres de configuration possibles de s'allonger au fur et à mesure de l'évolution de l'application, le modèle EAV place les "méta-données", c'est-à-dire les données relatives à l'attribut eux-mêmes, dans des tables de données, évitant ainsi tout le codage en dur des noms de colonnes couramment vu lorsque les paramètres de configuration sont stockés sur une seule ligne.

mjv
la source
3
Cela semble excessif pour la plupart des utilisations d'une table de configuration.
JerryOL
Je pense que l'idée générale derrière cette approche est excellente. Mais pourquoi XML? Choisissez simplement un format d'échange de données simple comme JSON ou YAML et vous pourrez profiter des avantages des deux autres variantes.
schlamar
1
L'EAV est relationnel mais n'est pas normalisé. Il existe certainement des cas d'utilisation (par exemple, les systèmes ORM semblent les aimer), mais l'argument selon lequel les métadonnées sont dans la base de données pour EAV n'est pas une raison convaincante de les utiliser. Tous les SGBDR contiennent de toute façon des métadonnées dans les tables système que vous pouvez découvrir, de sorte que les tables à une seule ligne ont également des métadonnées dans la base de données. Les noms de colonnes codés en dur sont également un problème. Si vous utilisez des clés pour les entités et les attributs, vous avez une table de recherche codée en dur ailleurs qui les définit (ou pire, c'est dans votre couche de présentation).
Davos
3

Vous n'avez certainement pas à modifier votre schéma lors de l'ajout d'un nouveau paramètre de configuration dans l'approche normalisée, mais vous changez probablement toujours votre code pour traiter la nouvelle valeur.

L'ajout d'une «table alternative» à votre déploiement ne semble pas être un si gros compromis pour la simplicité et la sécurité de type de l'approche à une seule ligne.

Dave Mikesell
la source
2

Une paire clé et valeur est similaire à un .Net App.Config qui peut stocker les paramètres de configuration.

Ainsi, lorsque vous souhaitez récupérer la valeur, vous pouvez faire:

SELECT value FROM configurationTable
WHERE ApplicationGroup = 'myappgroup'
AND keyDescription = 'myKey';
rizalp1
la source
1

Une manière courante de faire ceci est d'avoir une table de «propriétés» semblable à un fichier de propriétés. Ici, vous pouvez stocker toutes les constantes de votre application, ou des choses pas si constantes dont vous avez juste besoin.

Vous pouvez ensuite récupérer les informations de ce tableau selon vos besoins. De même, comme vous constatez que vous avez un autre paramètre à enregistrer, vous pouvez l'ajouter. Voici un exemple:

property_entry_table

[id, scope, refId, propertyName, propertyValue, propertyType] 
1, 0, 1, "COMPANY_INFO", "Acme Tools", "ADMIN"  
2, 0, 1, "SHIPPING_ID", "12333484", "ADMIN"  
3, 0, 1, "PAYPAL_KEY", "2143123412341", "ADMIN"   
4, 0, 1, "PAYPAL_KEY", "123412341234123", "ADMIN"  
5, 0, 1, "NOTIF_PREF", "ON", "ADMIN"  
6, 0, 2, "NOTIF_PREF", "OFF", "ADMIN"   

De cette façon, vous pouvez stocker les données dont vous disposez et les données que vous aurez l'année prochaine et que vous ne connaissez pas encore :).

Dans cet exemple, votre scope et refId peuvent être utilisés pour tout ce que vous voulez sur le back-end. Donc, si propertyType "ADMIN" a une portée 0 refId 2, vous savez de quelle préférence il s'agit.

Le type de propriété entre en jeu quand, un jour, vous devez également stocker des informations non-administrateur ici.

Notez que vous ne devez pas stocker les données du panier de cette façon, ni les recherches d'ailleurs. Cependant, si les données sont spécifiques au système , vous pouvez certainement utiliser cette méthode.

Par exemple: si vous souhaitez stocker votre DATABASE_VERSION , vous utiliserez une table comme celle-ci. De cette façon, lorsque vous devez mettre à niveau l'application, vous pouvez consulter le tableau des propriétés pour voir quelle version de votre logiciel le client possède.

Le fait est que vous ne voulez pas l'utiliser pour des choses qui concernent le chariot. Gardez votre logique métier dans des tables relationnelles bien définies. Le tableau des propriétés est uniquement destiné aux informations système.

Stéphano
la source
@finnw Je suis tout à fait d'accord pour dire que cette méthode ne doit pas être utilisée pour les recherches, en particulier lorsqu'il existe de nombreux types de recherches. Peut-être ai-je mal compris la question. On aurait dit qu'il avait besoin d'un tableau pour les constantes et les propriétés du système. Dans ce cas, pourquoi avoir 10 tables différentes?
Stephano
note: il a dit "enregistrer les paramètres et les configurations", pas "je dois enregistrer les données de panier relationnel"
Stephano
Mon objection à cela est que vous contournez le typage de SQL et d'autres mécanismes de contrainte pour éviter de mettre à jour le schéma SQL lorsque vous ajoutez de nouveaux attributs. Comme vous dites "des données que vous aurez l'année prochaine et que vous ne connaissez pas encore". Oui, vous aurez de nouvelles données l'année prochaine, mais qu'est-ce qui vous empêchera de créer de nouvelles colonnes SQL (typées), des contraintes CHECK et éventuellement FOREIGN KEY au moment de leur ajout?
finnw
Mon premier instinct est d'ajouter simplement ces données à un fichier plat. Et vous avez raison, ce processus d'utilisation d'une table à la place contournera en effet les mécanismes de contrainte du SGBD. Cependant, je dirais que si vous essayez trop fort de suivre les techniques de base de données appropriées, vous manquez le point. Découvrez la première réponse; voté le plus élevé sur SO: stackoverflow.com/questions/406760/…
Stephano
2
J'irais la paire clé-valeur, le viderais tout dans un dictionnaire au démarrage et votre tri.
Paul Creasey
0

Je ne suis pas sûr qu'une seule ligne soit la meilleure implémentation pour la configuration. Vous feriez peut-être mieux d'avoir une ligne par élément de configuration avec deux colonnes (configName, configValue), même si cela nécessitera de convertir toutes vos valeurs en chaînes et inversement.

Quoi qu'il en soit, il n'y a aucun mal à utiliser une seule ligne pour la configuration globale. Les autres options pour le stocker dans la base de données (variables globales) sont pires. Vous pouvez le contrôler en insérant votre première ligne de configuration, puis en désactivant les insertions sur la table pour empêcher plusieurs lignes.

sidéral
la source
0

Vous pouvez effectuer la paire clé / valeur sans conversions en ajoutant une colonne pour chaque type principal et une colonne vous indiquant dans quelle colonne se trouvent les données.

Donc, votre table ressemblerait à quelque chose comme:

id, column_num, property_name, intValue, floatValue, charValue, dateValue
1, 1, weeks, 51, , ,
2, 2, pi, , 3.14159, , 
3, 4, FiscYearEnd, , , , 1/31/2015
4, 3, CompanyName, , , ACME, 

Il utilise un peu plus de place, mais vous utilisez au maximum quelques dizaines d'attributs. Vous pouvez utiliser une instruction case sur la valeur column_num pour extraire / joindre le champ de droite.

spintool
la source
0

Désolé je viens comme, oui plus tard. Mais de toute façon, ce que je fais est simple et efficace. Je crée simplement une table avec trois () colonnes:

Identifiant - entier (11)

nom - varchar (64)

valeur - texte

Ce que je fais avant de créer une nouvelle colonne de configuration, de la mettre à jour ou de la lire, c'est de sérialiser la "valeur"! De cette façon, je suis sûr du type (Eh bien, php est :))

Par exemple:

b: 0; est pour B OOLEAN ( faux )

b: 1; est pour B OOLEAN ( vrai )

i: 1988; est pour I NT

s: 5: "Kader"; est pour un S TRING de 5 caractères

J'espère que ça aide :)

Kader Bouyakoub
la source
1
Pourquoi ne pas simplement créer une nouvelle colonne pour le type? i:1988semble que vous essayez de regrouper deux informations en une seule colonne.
maksymiuk
@maksymiuk Simplement parce qu'une fois désérialisé, vous obtenez le type exact au lieu d'utiliser une boucle après (if ou switch) ... etc
Kader Bouyakoub
pas besoin de boucles, de commutateurs ou de quoi que ce soit, cela supprimerait en fait l'étape consistant à analyser les informations de chaque ligne, tandis que si vous aviez une colonne supplémentaire pour le type, les informations de type sont déjà disponibles pour le composant qui extrait les informations sans avoir à faire d'autres étapes que la simple requête initiale
maksymiuk
Vous voulez dire en faisant quelque chose comme echo (int) $varpour un entier et d'autres pour d'autres types?
Kader Bouyakoub
0

Avoir une colonne clé comme varchar et une colonne valeur comme JSON. 1est numérique alors que "1"est une chaîne. trueet falsesont tous deux booléens. Vous pouvez également avoir des objets.

kzh
la source