Lors du développement d'une application de panier d'achat, 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 n'importe quoi: informations sur la société, identifiants de compte d'expédition, clés de l'API Paypal, préférences de notification, etc.
Il semble hautement 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 cette information?
Remarque: mon SGBD est SQL Server 2008 et la couche de programmation est implémentée avec ASP.NET (en C #).
Dans le passé, j'ai utilisé deux méthodes différentes: une table à une seule ligne et une table clé/paire de valeurs. Chaque approche présente des avantages et des inconvénients.
L'option à une seule ligne est de loin la plus facile à utiliser. En effet, vous pouvez stocker chaque paramètre dans son type correct dans la base de données sans avoir à stocker les types des paramètres ainsi que leurs clés de recherche dans le code.
Une des choses qui me préoccupait avec l’utilisation de cette approche était d’avoir plusieurs lignes dans le tableau «spécial» des paramètres d’une seule ligne. J'ai surmonté cela par (dans SQL Server):
Cela signifie qu'une seule ligne peut exister dans la table car la colonne de bits doit avoir la valeur 0, mais il ne peut y avoir qu'une seule ligne avec cette valeur en raison de la contrainte unique.
Vous devez créer une table avec une colonne pour le type d'information et la valeur d'information (au moins). De cette manière, vous évitez de créer de nouvelles colonnes chaque fois qu'une nouvelle information est ajoutée.
Une seule rangée fonctionnera bien; il aura même des types forts:
show_borders bit
admin_name varchar(50)
max_users int
Un inconvénient est que cela 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 ajouter un nouveau paramètre consiste simplement à ajouter une ligne, ce que vous pouvez faire uniquement avec un accès en écriture à la base de données.
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’ya pas vraiment de mal à le faire.
Comme vous l'avez deviné, et sauf dans les situations les plus simples, placer tous les paramètres de configuration dans une seule ligne présente de nombreux inconvénients. C'est une mauvaise idée...
Un moyen pratique de stocker des informations de configuration et/ou de type de préférence utilisateur est 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. L'un des avantages de XML est son support implicite pour 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, vous avez probablement besoin du modèle Entity-Attribute-Value , les valeurs individuelles étant stockées dans une table ressemblant 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)
AttributeId étant une clé étrangère vers une table dans laquelle chaque attribut possible ("paramètre de configuration" dans votre cas) est défini, avec
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 différents attributs. Dans votre cas, il pourrait s'agir d'un identifiant utilisateur 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 que l'application évolue, le modèle EAV place les "métadonnées", c'est-à-dire les données relatives à l'attribut elles-mêmes, dans des tables de données, évitant ainsi tout codage en dur des noms de colonnes couramment vus lorsque les paramètres de configuration sont stockés sur une seule ligne.
Vous n'avez certainement pas besoin de modifier votre schéma lorsque vous ajoutez un nouveau paramètre de configuration dans l'approche normalisée, mais vous modifiez probablement votre code pour traiter la nouvelle valeur.
Ajouter une "table de modification" à votre déploiement ne semble pas être un énorme compromis pour la simplicité et la sécurité de type de l'approche à une seule ligne.
Une paire clé/valeur est similaire à un .Net App.Config qui peut stocker des paramètres de configuration.
Alors, quand vous voulez récupérer la valeur que vous pouvez faire:
SELECT value FROM configurationTable
WHERE ApplicationGroup = 'myappgroup'
AND keyDescription = 'myKey';
Une méthode courante consiste à avoir une table "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 que vous devez juste avoir autour.
Vous pouvez ensuite récupérer les informations de cette table selon vos besoins. De même, si vous avez d'autres paramètres à enregistrer, vous pouvez les 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 que vous avez et celles que vous aurez l'année prochaine et que vous ne connaissez pas encore :).
Dans cet exemple, votre portée et refId peuvent être utilisés pour tout ce que vous voulez sur le back-end. Donc, si le type de propriété "ADMIN" a une portée 0 réfId 2, vous savez de quelle préférence il s'agit.
Le type de propriété entre en jeu lorsqu'un jour, vous devez également stocker ici des informations non administratives.
Notez que vous ne devriez pas stocker les données du panier de cette façon, ni les recherches d’ailleurs. Cependant, si les données sont Système spécifique, alors vous pouvez certainement utiliser cette méthode.
Par exemple: si vous voulez stocker votre DATABASE_VERSION, vous utiliseriez une table comme celle-ci. Ainsi, lorsque vous aurez besoin de mettre à niveau l'application, vous pourrez consulter le tableau des propriétés pour connaître la version de votre logiciel dont dispose le client.
Le fait est que vous ne voulez pas utiliser ceci pour des choses qui se rapportent au panier. Conservez votre logique métier dans des tables relationnelles bien définies. Le tableau des propriétés est uniquement destiné aux informations système.
Avoir une colonne clé comme varchar et une colonne valeur comme JSON. 1
est numérique alors que "1"
est une chaîne. true
et false
sont tous deux booléens. Vous pouvez aussi avoir des objets.
Vous pouvez effectuer la paire clé/valeur sans conversions en ajoutant une colonne pour chaque type principal et une colonne vous indiquant la colonne dans laquelle 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 tout au plus quelques dizaines d'attributs. Vous pouvez utiliser une instruction case à partir de la valeur column_num pour extraire/joindre le champ de droite.
Je ne suis pas sûr qu'une seule ligne soit la meilleure implémentation pour la configuration. Il serait peut-être préférable d'avoir une ligne par élément de configuration avec deux colonnes (configName, configValue), bien que cela nécessite de transtyper toutes vos valeurs en chaînes et en retour.
Quoi qu'il en soit, il n'y a pas de 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.
Désolé je viens comme, ans plus tard. Mais quoi qu'il en soit, ce que je fais est simple et efficace. Je crée simplement une table avec trois () colonnes:
ID - int (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 lire, est de sérialiser la "valeur"! De cette façon, je suis sûr du type (bon, php est :))
Par exemple:
b: 0; est pourBOOLEAN (false)
b: 1; est pourBOOLEAN (true)
i: 1988; est pourINT
s: 5: "Kader"; est pour unSTRING d'une longueur de 5 caractères
J'espère que ça aide :)