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.
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:
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.
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.
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:
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:
CustomFields
a une colonne DataType
.CustomFieldValues
pour vous assurer que la colonne spécifiée par CustomFields.DataType
est non nulle.DataType
..__ séparée.CustomerCustomFieldValue
, mais plutôt des colonnes CustomerID
et CustomFieldValueID
.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.
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:
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.
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.
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.