web-dev-qa-db-fra.com

Quels sont les modèles de conception pour prendre en charge les champs personnalisés dans une application?

Nous développons une application commerciale. Nos clients demandent un support pour les champs personnalisés. Par exemple, ils veulent ajouter un champ au formulaire Client.

Quels sont les modèles de conception connus pour stocker les valeurs de champ et les métadonnées sur les champs?

Je vois ces options pour l'instant:

Option 1 : Ajouter des colonnes Champ1, Champ2, Champ3, Champ4 de type varchar à ma table Clients.

Option 2 : ajoute une seule colonne de type XML dans la table client et stocke les valeurs des champs personnalisés au format xml.

Option 3 : Ajouter une table CustomerCustomFieldValue avec une colonne de type varchar et stocker les valeurs dans cette colonne. Cette table aurait également un CustomerID, un CustomFieldID.

CustomerID,  CustomFieldID, Value
10001,       1001,          '02/12/2009 8:00 AM'
10001,       1002,          '18.26'
10002,       1001,          '01/12/2009 8:00 AM'
10002,       1002,          '50.26'

CustomFieldID serait un ID d'une autre table appelée CustomField avec les colonnes suivantes: CustomFieldID, FieldName, FieldValueTypeID.

Option 4 : Ajouter une table CustomerCustomFieldValue avec une colonne de chaque type de valeur possible et stocker des valeurs dans la colonne de droite. Semblable à # 3 mais les valeurs de champ sont stockées en utilisant une colonne de type fort.

CustomerID,  CustomFieldID, DateValue,           StringValue,       NumericValue                 
10001,       1001,          02/12/2009 8:00 AM,  null,              null
10001,       1002,          null,                null,              18.26
10002,       1001,          01/12/2009 8:00 AM,  null,              null
10002,       1002,          null,                null,              50.26

Option 5 : Les options 3 et 4 utilisent un tableau spécifique à un seul concept (Client). Nos clients demandent également des champs personnalisés sous d'autres formes. Devrions-nous plutôt avoir un système de stockage personnalisé sur le terrain? Ainsi, au lieu d’avoir plusieurs tables telles que CustomerCustomFieldValue, EmployeeCustomFieldValue, InvoiceCustomFieldValue, nous aurions une seule table nommée CustomFieldValue? Bien que cela me semble plus élégant, cela ne causerait-il pas un goulot d'étranglement au niveau des performances?

Avez-vous utilisé l'une de ces approches? Avez-vous réussi? Quelle approche choisiriez-vous? Connaissez-vous une autre approche que je devrais envisager?

De plus, mes clients souhaitent que le champ personnalisé puisse faire référence à des données contenues dans d'autres tables. Par exemple, un client peut souhaiter ajouter un champ "Méthode de paiement favori" au client. Les méthodes de paiement sont définies ailleurs dans le système. Cela amène le sujet des "clés étrangères" dans l'image. Devrais-je essayer de créer des contraintes pour que les valeurs stockées dans les tables de champs personnalisés soient des valeurs valides?

Merci

=======================

EDIT 07-27-2009:

Merci pour vos réponses. Il semble que la liste des approches est maintenant assez complète. J'ai sélectionné l'option 2 (une seule colonne XML). C'était la plus facile à mettre en œuvre pour le moment. Je vais probablement devoir m'adapter à une approche plus définie, car mes exigences seront de plus en plus complexes et le nombre de champs personnalisés à prendre en charge augmentera.

69
Sylvain

Je suis d’accord avec les affiches ci-dessous pour dire que les options 3, 4 ou 5 sont plus susceptibles d’être appropriées. Cependant, chacune de vos implémentations suggérées présente des avantages et des coûts. Je suggère de choisir un en le faisant correspondre à vos besoins spécifiques. Par exemple:

  1. Option 1 pros: Rapide à mettre en œuvre. Autorise les actions de base de données sur les champs personnalisés (recherche, tri).
    Option 1 contre: Les champs personnalisés sont génériques, donc pas de champs fortement typés. La table de base de données est inefficace, en taille, avec de nombreux champs superflus qui ne seront jamais utilisés. Le nombre de champs personnalisés autorisés doit être anticipé.
  2. Option 2 pros: Rapide à mettre en œuvre. Flexible, permettant le nombre et le type arbitraires de champs personnalisés.
    Option 2 contre: Aucune action de base de données sur les champs personnalisés. Ceci est mieux si tout ce que vous avez à faire est d’afficher les champs personnalisés, ultérieurement, ou d’effectuer des manipulations mineures des données uniquement pour chaque client.
  3. Option 3 pros: à la fois flexible et efficace. Des actions de base de données peuvent être effectuées, mais les données sont quelque peu normalisées pour réduire l'espace perdu. Je suis d'accord avec la suggestion de unknown (google) d'ajouter une colonne supplémentaire qui peut être utilisée pour spécifier le type ou les informations de source . Option 3 contre: Légère augmentation du temps de développement et de la complexité de vos requêtes, mais il n'y a vraiment t trop de inconvénients, ici.
  4. L'option 4 est identique à l'option 3, sauf que vos données saisies peuvent être utilisées au niveau de la base de données. L'ajout d'informations de type à la table de liens dans l'option 3 vous permet d'effectuer davantage d'opérations au niveau de notre application, mais la base de données ne sera pas en mesure d'effectuer de comparaison ni de trier, par exemple. Le choix entre 3 et 4 dépend de cette exigence.
  5. L'option 5 est identique à 3 ou 4, mais avec encore plus de flexibilité pour appliquer la solution à de nombreux tableaux différents. Le coût dans ce cas sera que la taille de cette table deviendra beaucoup plus grande. Si vous effectuez de nombreuses jointures coûteuses pour accéder à vos champs personnalisés, cette solution risque de ne pas bien évoluer. 

P.S. Comme indiqué ci-dessous, le terme "modèle de conception" désigne généralement une programmation orientée objet. Vous recherchez une solution à un problème de conception de base de données, ce qui signifie que la plupart des conseils relatifs aux modèles de conception ne sont pas applicables.

14
Eric Nguyen

En ce qui concerne le code de l'application, je ne suis pas sûr. Je sais que les champs personnalisés bénéficient grandement d’un modèle EAV dans la base de données.

D'après les commentaires ci-dessous, l'erreur la plus grave que vous puissiez commettre avec ce modèle est d'y insérer des clés étrangères. Ne jamais mettre quelque chose comme FriendID ou TypeID dans ce modèle. Utilisez ce modèle avec le modèle relationnel typique et conservez les champs de clé étrangère dans les colonnes de la table comme ils le devraient. 

Une deuxième erreur importante consiste à placer dans ce modèle des données qui doivent être rapportées pour chaque élément. Par exemple, mettre quelque chose comme Nom d'utilisateur dans ce modèle signifie que, chaque fois que vous souhaitez accéder à un utilisateur et que vous devez connaître son nom d'utilisateur, vous vous êtes engagé au mieux pour une jointure ou pour 2n requêtes où n est le nombre d'utilisateurs consultés . Lorsque vous considérez que vous allez généralement avoir besoin de la propriété Nom d'utilisateur pour chaque élément User, il devient évident que cela doit également rester dans les colonnes de la table.

Cependant, si vous utilisez uniquement ce modèle avec des champs utilisateur personnalisés, tout ira bien. Je ne peux pas imaginer de nombreuses situations dans lesquelles un utilisateur entrerait des données relationnelles et où le modèle EAV ne nuit pas trop aux recherches.

Enfin, n'essayez pas de joindre des données à partir de cela et d'obtenir un joli jeu d'enregistrements. Saisissez l’enregistrement original, puis l’ensemble des enregistrements de l’entité. Si vous vous sentez tenté de rejoindre les tables, vous avez probablement commis la deuxième erreur mentionnée ci-dessus.

10
Spencer Ruport

Si vous développez avec un langage orienté objet, nous parlons de modèles d'objet adaptatifs ici. Il existe de nombreux articles sur la manière de les implémenter en o-langues, mais peu d'informations sur la conception du côté magasin de données.

Dans l'entreprise où je travaille, nous avons résolu le problème en utilisant une base de données relationnelle pour stocker les données AOM. Nous avons une table d'entités centrale pour présenter toutes les différentes "entités" du domaine, telles que les personnes, les périphériques réseau, les entreprises, etc. Nous stockons les "champs de formulaire" dans des tables de données typées. Nous avons donc une table pour chaînes, une pour les dates et ainsi de suite. Toutes les tables de données ont une clé étrangère pointant vers la table d'entités. Nous avons également besoin de tables pour présenter le type, c’est-à-dire le type d’attributs (champs de formulaire) que certaines entités peuvent avoir et cette information est utilisée pour interpréter les données des tables de données.

Les avantages de notre solution sont que tout peut être modélisé sans modification du code, y compris les références entre entités, les valeurs multiples, etc. Il est également possible d'ajouter des règles de gestion et des validations aux champs, qui peuvent être réutilisés sous toutes leurs formes. Les inconvénients sont que le modèle de programmation n'est pas très facile à comprendre et que les performances des requêtes seront pires que celles d'une conception de base de données plus typique. Une autre solution que la base de données relationnelle aurait pu être meilleure et plus facile pour AOM.

Construire un bon AOM avec un magasin de données en état de marche représente beaucoup de travail et je ne le recommanderais pas si vous n'avez pas de développeurs hautement qualifiés. Peut-être qu'un jour, il y aura une solution de système d'exploitation pour ce type d'exigences.

Les champs personnalisés ont déjà été discutés dans SO:

4
Kaitsu

L'option 4 ou 5 serait mon choix. Si vos données sont importantes, je n'irais pas jeter vos informations de type avec Option 3. (Vous pouvez essayer de mettre en œuvre la vérification de type complète vous-même, mais c'est un gros travail et le moteur de base de données le fait déjà pour vous.)

Quelques idées:

  • Assurez-vous que votre CustomFields a une colonne DataType .
    • Utilisez une contrainte de vérification basée sur UDF sur CustomFieldValues pour vous assurer que la colonne spécifiée par CustomFields.DataType est non nulle.
    • Vous voudrez également une contrainte de vérification standard pour vous assurer que vous avez exactement une valeur non nulle.
  • En ce qui concerne les clés étrangères, je les modéliserais comme une DataType..__ séparée.
    • Chaque référence croisée potentielle nécessiterait sa propre colonne. C'est bien, car cela maintient l'intégrité référentielle.
    • De toute façon, vous devrez prendre en charge ces relations dans le code d'application. Par conséquent, le fait qu'elles soient codées en dur dans la base de données ne limite pas réellement les fonctionnalités.
    • Cela ira également bien avec votre ORM, si vous en utilisez un.
  • Pour l'option 5, utilisez des tables intermédiaires pour modéliser les relations .
    • Vous auriez toujours une CustomerCustomFieldValue, mais plutôt des colonnes CustomerID et CustomFieldValueID.
  • Réfléchissez longuement à vos contraintes à chaque étape du processus. Ce sont des choses délicates, et un faux pas peut causer un chaos total sur la ligne.

J'utilise ceci dans une application en cours de développement. Il n'y a pas encore eu de problèmes, mais les designs EAV me font encore peur. Fait attention.

En passant, XML peut également être un bon choix. Je ne sais pas trop à ce sujet d’expérience directe, mais c’était l’une des options que j’avais envisagée lors du démarrage de la conception des données et c’était très prometteur.

3
WCWedin

Quelque chose comme Option 3 est la voie à suivre et j'ai déjà utilisé cette méthode. Créez une seule table pour définir des propriétés supplémentaires et leurs valeurs correspondantes. Ce serait une relation 1-N entre votre table Customer et CustomerCustomField (respectivement). Votre deuxième question concernant la définition de relations avec des propriétés personnalisées serait une chose à laquelle réfléchir. La première chose qui me vient à l’esprit est d’ajouter un champ DataSource, qui contiendrait la table à laquelle la valeur de la propriété est liée. Donc, essentiellement, votre CustomerCustomField ressemblerait à ceci:

  1. N ° de client
  2. Propriété
  3. Valeur
  4. ValueDataSource (nullable)

Cela devrait vous permettre de vous lier à une structure de données spécifique ou simplement de spécifier des valeurs non liées. Vous pouvez continuer à normaliser ce modèle, mais quelque chose comme cela pourrait fonctionner et devrait être assez facile à gérer dans le code. 

3
Sergey

Je travaille actuellement sur un projet avec le même problème et j'ai choisi d'utiliser l'option 3, mais j'ai ajouté un champ FieldType et un champ ListSource au cas où FieldType = "list". Le champ ListSource peut être une requête, une vue SQL, un nom de fonction ou quelque chose qui donne une liste d'options pour la liste. Le plus gros problème lorsque j'essaie de stocker des champs comme celui-ci dans ma situation est que cette liste de champs peut être modifiée et que les utilisateurs sont autorisés à modifier les données ultérieurement. Alors que faire si la liste des champs a changé et qu'ils vont à éditer. Ma solution à ce scénario consistait à autoriser la modification uniquement si la liste n'avait pas changé et à afficher les données en lecture seule, le cas échéant.

0
jbair

si ces champs "extra" sont accessoires et ne vous intéressent pas, je choisis généralement l'option 2 (mais comme JSON, c'est mieux que XML) S'il doit y avoir des recherches sur les champs personnalisés, l'option 3 n'est pas difficile à faire et l'optimiseur SQL peut généralement obtenir des performances raisonnables.

0
Javier