web-dev-qa-db-fra.com

Conception de base de données pour les paramètres utilisateur

Laquelle des options suivantes, le cas échéant, est considérée comme la meilleure pratique lors de la conception d'une table utilisée pour stocker les paramètres utilisateur?

(OPTION 1)

USER_SETTINGS
-Id
-Code (example "Email_LimitMax")
-Value (example "5")
-UserId

(OPTION 2)

créer un nouveau tableau pour chaque paramètre où, par exemple, les paramètres de notification vous obligeraient à créer:

"USER_ALERT_SETTINGS"
-Id
-UserId
-EmailAdded (i.e true)
-EmailRemoved 
-PasswordChanged
...
...

"USER_EMAIL_SETTINGS"
-Id
-UserId
-EmailLimitMax
....

(OPTION 3)

"USER"
-Name
...
-ConfigXML
41
001

D'autres réponses ont bien décrit les avantages et les inconvénients de vos différentes options.

Je crois que votre option 1 (sac de propriété) est la meilleure conception globale pour la plupart des applications, surtout si vous intégrez certaines protections contre les faiblesses des sacs de propreté.

Voir l'ERD suivant:

Property Bag ERD

Dans l'ERD ci-dessus, le USER_SETTING table est très similaire aux OP. La différence est qu'au lieu des colonnes varchar Code et Value, cette conception a un FK vers une table SETTING qui définit les paramètres autorisés (codes) et deux colonnes mutuellement exclusives pour la valeur. Une option est un champ varchar qui peut prendre n'importe quel type d'entrée utilisateur, l'autre est un FK vers une table de valeurs légales.

La table SETTING possède également un indicateur qui indique si les paramètres utilisateur doivent être définis par le FK ou par une entrée varchar non contrainte. Vous pouvez également ajouter un data_type au SETTING pour indiquer au système comment coder et interpréter le USER_SETTING.unconstrained_value. Si vous le souhaitez, vous pouvez également ajouter le SETTING_GROUP tableau pour aider à organiser les différents paramètres de maintenance par l'utilisateur.

Cette conception vous permet de définir par table les règles relatives à vos paramètres. Ceci est pratique, flexible et facile à entretenir, tout en évitant la gratuité pour tous.


EDIT: Quelques détails supplémentaires, y compris quelques exemples ...

Notez que l'ERD ci-dessus a été augmenté avec plus de détails sur les colonnes (valeurs de plage sur SETTING et colonnes sur ALLOWED_SETTING_VALUE).

Voici quelques exemples d'enregistrements à titre d'illustration.

SETTING:
+----+------------------+-------------+--------------+-----------+-----------+
| id | description      | constrained | data_type    | min_value | max_value |
+----+------------------+-------------+--------------+-----------+-----------+
| 10 | Favourite Colour | true        | alphanumeric | {null}    | {null}    |
| 11 | Item Max Limit   | false       | integer      | 0         | 9001      |
| 12 | Item Min Limit   | false       | integer      | 0         | 9000      |
+----+------------------+-------------+--------------+-----------+-----------+

ALLOWED_SETTING_VALUE:
+-----+------------+--------------+-----------+
| id  | setting_id | item_value   | caption   |
+-----+------------+--------------+-----------+
| 123 | 10         | #0000FF      | Blue      |
| 124 | 10         | #FFFF00      | Yellow    |
| 125 | 10         | #FF00FF      | Pink      |
+-----+------------+--------------+-----------+

USER_SETTING:
+------+---------+------------+--------------------------+---------------------+
| id   | user_id | setting_id | allowed_setting_value_id | unconstrained_value |
+------+---------+------------+--------------------------+---------------------+
| 5678 | 234     | 10         | 124                      | {null}              |
| 7890 | 234     | 11         | {null}                   | 100                 |
| 8901 | 234     | 12         | {null}                   | 1                   |
+------+---------+------------+--------------------------+---------------------+

À partir de ces tableaux, nous pouvons voir que certains des paramètres utilisateur qui peuvent être déterminés sont la couleur préférée, la limite maximale de l'élément et la limite minimale de l'élément. La couleur préférée est une liste de choix alphanumériques. Les limites minimales et maximales des éléments sont des valeurs numériques avec des valeurs de plage autorisées définies. Le SETTING.constrained la colonne détermine si les utilisateurs choisissent parmi les ALLOWED_SETTING_VALUEs ou s'ils doivent saisir un USER_SETTING.unconstrained_value. L'interface graphique qui permet aux utilisateurs de travailler avec leurs paramètres doit comprendre quelle option offrir et comment appliquer à la fois SETTING.data_type et le min_value et max_value limites, si elles existent.

À l'aide de cette conception, vous pouvez créer un tableau des paramètres autorisés, y compris suffisamment de métadonnées pour appliquer certaines contraintes rudimentaires/contrôles d'intégrité sur les valeurs sélectionnées (ou entrées) par les utilisateurs.

EDIT: Exemple de requête

Voici quelques exemples de SQL utilisant les données ci-dessus pour répertorier les valeurs de paramètre pour un ID utilisateur donné:

-- DDL and sample data population...
CREATE TABLE SETTING
    (`id` int, `description` varchar(16)
     , `constrained` varchar(5), `data_type` varchar(12)
     , `min_value` varchar(6) NULL , `max_value` varchar(6) NULL)
;

INSERT INTO SETTING
    (`id`, `description`, `constrained`, `data_type`, `min_value`, `max_value`)
VALUES
    (10, 'Favourite Colour', 'true', 'alphanumeric', NULL, NULL),
    (11, 'Item Max Limit', 'false', 'integer', '0', '9001'),
    (12, 'Item Min Limit', 'false', 'integer', '0', '9000')
;

CREATE TABLE ALLOWED_SETTING_VALUE
    (`id` int, `setting_id` int, `item_value` varchar(7)
     , `caption` varchar(6))
;

INSERT INTO ALLOWED_SETTING_VALUE
    (`id`, `setting_id`, `item_value`, `caption`)
VALUES
    (123, 10, '#0000FF', 'Blue'),
    (124, 10, '#FFFF00', 'Yellow'),
    (125, 10, '#FF00FF', 'Pink')
;

CREATE TABLE USER_SETTING
    (`id` int, `user_id` int, `setting_id` int
     , `allowed_setting_value_id` varchar(6) NULL
     , `unconstrained_value` varchar(6) NULL)
;

INSERT INTO USER_SETTING
    (`id`, `user_id`, `setting_id`, `allowed_setting_value_id`, `unconstrained_value`)
VALUES
    (5678, 234, 10, '124', NULL),
    (7890, 234, 11, NULL, '100'),
    (8901, 234, 12, NULL, '1')
;

Et maintenant le DML pour extraire les paramètres d'un utilisateur:

-- Show settings for a given user
select
  US.user_id 
, S1.description 
, S1.data_type 
, case when S1.constrained = 'true'
  then AV.item_value
  else US.unconstrained_value
  end value
, AV.caption
from USER_SETTING US
  inner join SETTING S1
    on US.setting_id = S1.id 
  left outer join ALLOWED_SETTING_VALUE AV
    on US.allowed_setting_value_id = AV.id
where US.user_id = 234

Voir ceci dans SQL Fiddle .

76
Joel Brown

Considérez cet exemple simple.

Si vous avez 2 tableaux, serTable (contient les détails de l'utilisateur) et SettingsTable (contient les détails des paramètres). Créez ensuite un nouveau tableau serSettings pour associer UserTable et SettingsTable comme indiqué ci-dessous

user settings data base design

J'espère que vous aurez trouvé la bonne solution à partir de cet exemple.

8
Sajith

L'option 1 (comme indiqué, "sac de propriété") est facile à mettre en œuvre - très peu d'analyse initiale. Mais il a un tas d'inconvénients.

  1. Si vous souhaitez restreindre les valeurs valides pour UserSettings.Code, vous avez besoin d'une table auxiliaire pour la liste des balises valides. Vous n'avez donc (a) aucune validation sur UserSettings.Code - votre code d'application peut vider n'importe quelle valeur, manquer la chance d'attraper des bogues, ou vous devez ajouter une maintenance sur la nouvelle liste de balises valides.

  2. UserSettings.Value a probablement un type de données de chaîne pour prendre en charge toutes les différentes valeurs qui peuvent y entrer. Vous avez donc perdu le vrai type de données - entier, booléen, flottant, etc., et la vérification du type de données qui serait effectuée par le RDMBS lors de l'insertion de valeurs incorrectes. Encore une fois, vous vous êtes acheté un problème potentiel d'assurance qualité. Même pour les valeurs de chaîne, vous avez perdu la possibilité de limiter la longueur de la colonne.

  3. Vous ne pouvez pas définir une valeur DEFAULT sur la colonne en fonction du code. Donc, si vous vouliez que EmailLimitMax soit réglé par défaut sur 5, vous ne pouvez pas le faire.

  4. De même, vous ne pouvez pas placer de contrainte CHECK dans la colonne Valeurs pour empêcher les valeurs non valides.

  5. L'approche du sac de propriétés perd la validation du code SQL. Dans l'approche de la colonne nommée, une requête qui dit "sélectionnez Blah dans UserSettings où UserID = x" obtiendra une erreur SQL si Blah n'existe pas. Si le SELECT est dans une procédure stockée ou une vue, vous obtiendrez l'erreur lorsque vous appliquerez le proc/view - bien avant l'heure de mise en production du code. Dans l'approche du sac de propriété, vous obtenez simplement NULL. Vous avez donc perdu une autre fonctionnalité QA automatique fournie par la base de données et introduit un éventuel bogue non détecté.

  6. Comme indiqué, une requête pour trouver un ID utilisateur où des conditions s'appliquent sur plusieurs balises devient plus difficile à écrire - elle nécessite une jointure dans la table pour chaque condition testée.

  7. Malheureusement, le Property Bag est une invitation pour les développeurs d'applications à simplement insérer un nouveau code dans le sac de propriétés sans analyse de la façon dont il sera utilisé dans le reste de l'application. Pour une grande application, cela devient une source de propriétés "cachées" car elles ne sont pas formellement modélisées. C'est comme faire votre modèle d'objet avec une valeur de balise pure au lieu d'attributs nommés: il fournit une valve d'échappement, mais vous manquez toute l'aide que le compilateur vous donnerait sur des attributs nommés fortement typés. Ou comme faire du XML de production sans validation de schéma.

  8. L'approche par nom de colonne est auto-documentée. La liste des colonnes du tableau indique à tout développeur quels sont les paramètres utilisateur possibles.

J'ai utilisé des sacs de propriété; mais seulement comme une valve d'échappement et je l'ai souvent regretté. Je n'ai jamais dit "bon sang, j'aurais aimé que cette colonne explicite soit un sac de propriété".

8
Tom Wilson

Chaque option a sa place, et le choix dépend de votre situation particulière. Je compare les avantages et les inconvénients de chaque option ci-dessous:

Option 1: Avantages:

  • Peut gérer de nombreuses options
  • De nouvelles options peuvent facilement être ajoutées
  • Une interface générique peut être développée pour gérer les options

Option 1: Contre

  • Lorsqu'une nouvelle option est ajoutée, il est plus complexe de mettre à jour tous les comptes d'utilisateurs avec la nouvelle option
  • Les noms des options peuvent devenir incontrôlables
  • La validation des valeurs des options autorisées est plus complexe, des métadonnées supplémentaires sont nécessaires pour cela

Option 2: Avantages

  • La validation de chaque option est plus facile que l'option 1 car chaque option est une colonne individuelle

Option 2: Contre

  • Une mise à jour de la base de données est requise pour chaque nouvelle option
  • Avec de nombreuses options, les tables de base de données pourraient devenir plus difficiles à utiliser

Il est difficile d'évaluer "le meilleur" car cela dépend du type de requêtes que vous souhaitez exécuter.

L'option 1 (communément appelée "sac de propriétés", "paires de valeurs de nom" ou "valeur d'attribut d'entité" ou EAV) facilite le stockage de données dont vous ne connaissez pas le schéma à l'avance. Cependant, il est difficile, voire parfois impossible, d'exécuter des requêtes relationnelles courantes. Par exemple, imaginez exécuter l'équivalent de

select count(*) 
from USER_ALERT_SETTINGS 
where EmailAdded = 1 
and Email_LimitMax > 5

Cela deviendrait rapidement très compliqué, en particulier parce que votre moteur de base de données peut ne pas comparer les champs varchar d'une manière numériquement significative (donc "> 5" peut ne pas fonctionner comme vous l'attendez).

Je travaillerais sur les requêtes que vous souhaitez exécuter et verrais quelle conception prend le mieux en charge ces requêtes. Si tout ce que vous avez à faire est de vérifier les limites pour un utilisateur individuel, le sac de propriété est parfait. Si vous devez signaler à tous les utilisateurs, ce n'est probablement pas le cas.

Il en va de même pour JSON ou XML - c'est correct pour stocker des enregistrements individuels, mais rend plus difficile l'interrogation ou la génération de rapports sur tous les utilisateurs. Par exemple, imaginez que vous recherchez les paramètres de configuration pour l'adresse e-mail "[email protected]" - cela nécessiterait une recherche dans tous les documents XML pour trouver le nœud "adresse e-mail".

3
Neville Kuyt