web-dev-qa-db-fra.com

Comment concevriez-vous une base de données d'utilisateurs avec des champs personnalisés

Cette question porte sur la façon de concevoir une base de données, il peut s'agir de bases de données relationnelles/nosql, en fonction de la meilleure solution


Étant donné une exigence où vous devrez créer un système qui impliquera une base de données pour suivre "Société" et "Utilisateur". Un seul utilisateur appartient toujours à une seule entreprise

  • Un utilisateur ne peut appartenir qu'à une seule entreprise
  • Une entreprise peut avoir plusieurs utilisateurs

La conception de la table "Entreprise" est assez simple. La société aura les attributs/colonnes suivants: (restons simples)

ID, COMPANY_NAME, CREATED_ON

Premier scénario

Simple et direct, les utilisateurs ont tous le même attribut, donc cela peut être facilement fait dans un style relationnel, table utilisateur:

ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CREATED_ON

Deuxième scénario

Que se passe-t-il si différentes entreprises souhaitent stocker un attribut de profil différent pour leur utilisateur. Chaque entreprise aura un ensemble défini d'attributs qui s'appliqueraient à tous les utilisateurs de cette entreprise.

Par exemple:

  • La société A souhaite stocker: LIKE_MOVIE (booléen), LIKE_MUSIC (booléen)
  • La société B souhaite stocker: FAV_CUISINE (String)
  • La société C souhaite stocker: OWN_DOG (booléen), DOG_COUNT (int)

Approche 1

la manière de force brute est d'avoir un schéma unique pour l'utilisateur et de lui laisser des valeurs nulles lorsqu'il n'appartient pas à l'entreprise:

ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, LIKE_MOVIE, LIKE_MUSIC, FAV_CUISINE, OWN_DOG, DOG_COUNT, CREATED_ON

Ce qui est un peu désagréable car vous vous retrouverez avec beaucoup de NULL et de lignes d'utilisateurs qui ont des colonnes qui ne leur sont pas pertinentes (c'est-à-dire que tous les utilisateurs appartenant à la société A ont des valeurs NULL pour FAV_CUISINE, OWN_DOG, DOG_COUNT)

Approche 2

une deuxième approche, est d'avoir un "champ de forme libre":

ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CUSTOM_1, CUSTOM_2, CUSTOM_3, CREATED_ON

Ce qui serait désagréable en soi puisque vous n'avez aucune idée de ce que sont les champs personnalisés, le type de données ne reflétera pas les valeurs stockées (par exemple, nous stockons la valeur int en tant que VARCHAR).

Approche 3

J'ai examiné le champ JSON PostgreSQL, auquel cas vous aurez:

ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CUSTOM_PROFILE_JSON, CREATED_ON

Dans ce cas, comment pourriez-vous appliquer différents schémas à un utilisateur? Un utilisateur avec la société A aura un schéma qui ressemble à

 {"LIKE_MOVIE":"boolean", "LIKE_MUSIC": "boolean"}

Alors qu'un utilisateur avec la société C aura un schéma différent:

 {"OWN_DOG ":"boolean", "DOG_COUNT": "int"}

Comment dois-je résoudre ce problème? Comment puis-je concevoir la base de données correctement pour permettre ce schéma flexible pour un seul "objet" (utilisateur) en fonction de la relation qu'ils ont (entreprise)?

solution relationnelle? solution nosql?


Edit: J'ai également pensé à une table "CUSTOM_PROFILE" qui stockera essentiellement les attributs utilisateur dans des lignes plutôt que des colonnes.

Il y a 2 problèmes avec cette approche:

1) Les données augmentent par utilisateur se développent sous forme de lignes plutôt que de colonnes - et cela signifie pour obtenir une image complète de l'utilisateur, beaucoup de jointures doivent être fait, plusieurs jointures à la table "profil personnalisé" sur les différents attributs personnalisés

2) La valeur des données est toujours stockée en tant que VARCHAR pour être générique, même si nous savons que les données sont censées être des nombres entiers ou booléens, etc.

21
noobcser

Veuillez considérer cela comme une alternative. Les deux exemples précédents nécessiteront tous les deux que vous apportiez des modifications au schéma à mesure que la portée de l'application augmente. En outre, la solution "custom_column" est difficile à étendre et à maintenir. Finalement, vous vous retrouverez avec Custom_510, puis imaginerez à quel point cette table sera horrible à travailler.

Utilisons d'abord le schéma de votre entreprise.

[Companies] ComnpanyId, COMPANY_NAME, CREATED_ON

Ensuite, nous utiliserons également votre schéma d'utilisateurs pour les attributs requis de niveau supérieur qui seront utilisés/partagés par toutes les entreprises.

[Users] UserId, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CREATED_ON

Ensuite, nous construisons un tableau où nous définirons nos attributs dynamiques spécifiques aux attributs utilisateur personnalisés de chaque entreprise. Voici donc un exemple de valeur de la colonne Attribut qui serait "LikeMusic":

[UserAttributeDefinition] UserAttributeDefinitionId, CompanyId, Attribute

Ensuite, nous définissons une table UserAttributes qui contiendra les valeurs d'attribut utilisateur

[UserAttributes] UserAttributeDefinitionId, UserId, Value

Cela peut être modifié de plusieurs façons pour améliorer les performances. Vous pouvez utiliser plusieurs tables pour UserAttributes en rendant chacune spécifique au type de données stocké dans Value ou simplement le laisser comme VarChar et travailler avec lui comme magasin de valeurs-clés.

Vous pouvez également souhaiter déplacer CompanyId hors de la table UserAttributeDefiniton et dans une table de référence croisée pour une vérification future.

14
P. Roe

Utilisez une base de données NoSQL. Il y aurait des documents d'entreprise et d'utilisateur. Les utilisateurs auraient une partie de leur schéma créée dynamiquement sur la base d'un modèle d'utilisateur (texte pour indiquer les champs/types pour cette entreprise.

\Company\<uniqueidentifier>
    - Name: <Name>
    - CreatedOn: <datetime>
    - UserTemplate: <Text>

\User\<uniqueidentifier>
    - COMPANY_ID: <ID>
    - FIRST_NAME: <Text>
    - LAST_NAME: <Text>
    - EMAIL: <Text>
    - CREATED_ON: <datetime>
    - * Dynamically created fields per company

Voici à quoi cela pourrait ressembler dans quelque chose comme Firebase.com Vous devriez apprendre à le faire dans celui que vous choisissez.

7
JeffO

Si vous allez fréquemment rencontrer des demandes de champs personnalisés, je les modéliserais de manière assez similaire à la base de données. Créez une table contenant les métadonnées de chaque champ personnalisé, CompanyCustomField (à qui il appartient, le type de données, etc.) et une autre table CompanyCustomFieldValues ​​qui contient le CustomerId, FieldId et la valeur. Si vous utilisez quelque chose comme Microsoft SQL Server, la colonne de valeur serait un type de données sql_variant.

Bien sûr, cela n'est pas facile car vous aurez besoin d'une interface qui permet aux administrateurs de définir des champs personnalisés pour chaque client, et d'une autre interface qui utilise réellement ces métadonnées pour créer une interface utilisateur pour collecter les valeurs des champs. Et si vous avez d'autres exigences, telles que le regroupement des champs ou la nécessité de créer un type de champ de liste de sélection, vous devrez l'accompagner de plus de métadonnées/autres tables (par exemple, CompanyCustomFieldPickListOptions).

Ce n'est pas anodin, mais il a l'avantage de ne pas nécessiter de modifications de base de données/de code pour chaque nouveau champ personnalisé. Toutes les autres fonctionnalités des champs personnalisés devront également être codées (par exemple, si vous souhaitez valider regex une valeur de chaîne, ou autoriser uniquement les dates entre certaines plages, ou si vous devez activer un champ personnalisé basé sur une autre valeur de champ personnalisé ).

3
Andy

Une alternative aux autres réponses est d'avoir une table appelée profile_attrib, ou similaire que le schéma est complètement géré par votre application.

Au fur et à mesure que des attributs personnalisés sont ajoutés, vous ALTER TABLE profile_attrib ADD COLUMN like_movie TINYINT(1), vous pouvez interdire leur suppression. Cela minimiserait votre adhésion, tout en offrant une flexibilité.

Je suppose que le compromis est que l'application a maintenant besoin de modifier les privilèges de table sur la base de données, et vous devez être intelligent pour nettoyer les noms de colonne.

1
Chris Seufert

Votre question a de nombreuses solutions potentielles. Une solution consiste à stocker les attributs supplémentaires au format XML. Le XML peut être stocké sous forme de texte ou si vous utilisez une base de données qui prend en charge les types XML comme XML (SQL Server). Le stockage sous forme de texte limite votre capacité d'interrogation (comme la recherche sur un attribut personnalisé), mais si le stockage et la récupération sont tout ce dont vous avez besoin, c'est une bonne solution. Si l'on doit interroger, alors le stockage du XML en tant que type XML serait une meilleure option (bien que cela soit plus spécifique au fournisseur).

Cela vous donnera la possibilité de stocker n'importe quel nombre d'attributs à un client en ajoutant simplement une colonne d'ajout sur la table client. On pourrait stocker les attributs sous forme de hashset ou de dictionnaire, on perdra la sécurité du type car tout sera une chaîne pour commencer, mais si l'on applique une chaîne de format standard pour les dates, les nombres, les booléens, cela fonctionnera bien.

Pour plus d'informations:

https://msdn.Microsoft.com/en-us/library/hh403385.aspx

@ La réponse de WalterMitty est également valable, bien que si l'on a beaucoup de clients avec des attributs différents, on puisse se retrouver avec de nombreuses tables si l'on suit le modèle d'héritage. Cela dépend du nombre d'attributs personnalisés partagés entre les clients.

0
Jon Raynor