Comment concevoir une base de données pour les champs définis par l'utilisateur?


145

Mes exigences sont:

  • Besoin de pouvoir ajouter dynamiquement des champs définis par l'utilisateur de tout type de données
  • Besoin de pouvoir interroger rapidement les UDF
  • Besoin de pouvoir effectuer des calculs sur les UDF en fonction du type de données
  • Besoin de pouvoir trier les UDF en fonction du type de données

Les autres informations:

  • Je recherche principalement la performance
  • Il y a quelques millions d'enregistrements maîtres auxquels des données UDF peuvent être jointes
  • Lors de ma dernière vérification, il y avait plus de 50 millions d'enregistrements UDF dans notre base de données actuelle
  • La plupart du temps, un UDF n'est attaché qu'à quelques milliers de fiches maîtresses, pas toutes
  • Les UDF ne sont pas jointes ou utilisées comme clés. Ce ne sont que des données utilisées pour des requêtes ou des rapports

Options:

  1. Créez une grande table avec StringValue1, StringValue2 ... IntValue1, IntValue2, ... etc. Je déteste cette idée, mais je la considérerai si quelqu'un peut me dire qu'elle est meilleure que d'autres idées et pourquoi.

  2. Créez une table dynamique qui ajoute une nouvelle colonne à la demande si nécessaire. Je n'aime pas non plus cette idée car je pense que les performances seraient lentes à moins que vous n'indexiez chaque colonne.

  3. Créez une table unique contenant UDFName, UDFDataType et Value. Lorsqu'un nouvel UDF est ajouté, générez une vue qui extrait uniquement ces données et les analyse dans le type spécifié. Les éléments qui ne répondent pas aux critères d'analyse retournent NULL.

  4. Créez plusieurs tables UDF, une par type de données. Nous aurions donc des tables pour les UDFStrings, les UDFDates, etc.

  5. XML DataTypes? Je n'ai jamais travaillé avec ces derniers mais je les ai vus mentionnés. Je ne sais pas s'ils me donneraient les résultats que je souhaite, surtout avec la performance.

  6. Autre chose?


7
Martin Fowler recommande 2 (schéma modifiable par l'utilisateur) ou 5 (LOB XML indexé): martinfowler.com/bliki/UserDefinedField.html
Neil McGuigan

Voir également la question StackOverflow sur les schémas de base de données dynamiques .
FloverOwe

Réponses:


49

Si la performance est la principale préoccupation, j'irais avec # 6 ... une table par UDF (vraiment, c'est une variante de # 2). Cette réponse est spécifiquement adaptée à cette situation et à la description de la distribution des données et des modèles d'accès décrits.

Avantages:

  1. Étant donné que vous indiquez que certains UDF ont des valeurs pour une petite partie de l'ensemble de données global, une table distincte vous offrira les meilleures performances car cette table sera aussi grande que nécessaire pour prendre en charge l'UDF. Il en va de même pour les indices associés.

  2. Vous bénéficiez également d'un gain de vitesse en limitant la quantité de données à traiter pour les agrégations ou autres transformations. Le fractionnement des données en plusieurs tables vous permet d'effectuer une partie de l'agrégation et d'autres analyses statistiques sur les données UDF, puis de joindre ce résultat à la table principale via une clé étrangère pour obtenir les attributs non agrégés.

  3. Vous pouvez utiliser des noms de table / colonne qui reflètent ce que sont réellement les données.

  4. Vous avez un contrôle total pour utiliser les types de données, vérifier les contraintes, les valeurs par défaut, etc. pour définir les domaines de données. Ne sous-estimez pas les performances résultant de la conversion de type de données à la volée. Ces contraintes aident également les optimiseurs de requêtes du SGBDR à développer des plans plus efficaces.

  5. Si jamais vous avez besoin d'utiliser des clés étrangères, l'intégrité référentielle déclarative intégrée est rarement surpassée par l'application de contraintes basées sur les déclencheurs ou au niveau de l'application.

Les inconvénients:

  1. Cela pourrait créer de nombreuses tables. L'application de la séparation des schémas et / ou d'une convention de dénomination atténuerait ce problème.

  2. Il y a plus de code d'application nécessaire pour faire fonctionner la définition et la gestion UDF. Je pense que c'est encore moins de code nécessaire que pour les options d'origine 1, 3 et 4.

Autres considérations:

  1. S'il y a quelque chose dans la nature des données qui aurait du sens pour le regroupement des FDU, cela devrait être encouragé. De cette façon, ces éléments de données peuvent être combinés dans un seul tableau. Par exemple, disons que vous avez des UDF pour la couleur, la taille et le coût. La tendance dans les données est que la plupart des instances de ces données ressemblent à

     'red', 'large', 45.03 

    plutôt que

     NULL, 'medium', NULL

    Dans un tel cas, vous n'encourrez pas de pénalité de vitesse notable en combinant les 3 colonnes dans 1 table car peu de valeurs seraient NULL et vous évitez de créer 2 tables de plus, soit 2 jointures de moins nécessaires lorsque vous devez accéder aux 3 colonnes. .

  2. Si vous rencontrez un mur de performances à partir d'un UDF qui est fortement peuplé et fréquemment utilisé, cela doit être pris en compte pour l'inclusion dans la table principale.

  3. La conception de table logique peut vous amener à un certain point, mais lorsque le nombre d'enregistrements devient vraiment énorme, vous devriez également commencer à regarder quelles options de partitionnement de table sont fournies par votre SGBDR de choix.


1
Listes de contrôle! Blague à l'intérieur entre moi et Phil, j'espère que ce n'est pas contraire aux règles.
GunnerL3510

Merci, je pense que je vais faire une variation de ceci. La plupart de nos données UDF proviennent de champs d'importation non mappés qui doivent rester à des fins de référence uniquement, donc j'aimerais les mettre dans un seul tableau. D'autres UDF sont définis selon les besoins (je ne peux pas les identifier à l'avance. Ils sont généralement créés lorsque nous modifions un processus ou décidons de suivre quelque chose de spécial pendant quelques mois) et sont couramment utilisés dans les requêtes. Je pense que je vais faire un tableau séparé pour chaque unité logique de ces valeurs.
Rachel

Je travaille avec une table qui a daté / versionné des UDF, j'utilise cette méthode, stackoverflow.com/a/123481/328968 , pour obtenir les dernières valeurs.
Peter

22

Je l' ai écrit à propos de ce problème beaucoup . La solution la plus courante est l'anti-modèle Entité-Attribut-Valeur, qui est similaire à ce que vous décrivez dans votre option # 3. Évitez cette conception comme la peste .

Ce que j'utilise pour cette solution lorsque j'ai besoin de champs personnalisés vraiment dynamiques, c'est de les stocker dans un blob de XML, afin que je puisse ajouter de nouveaux champs à tout moment. Mais pour accélérer les choses, créez également des tables supplémentaires pour chaque champ sur lequel vous devez rechercher ou trier (vous n'avez pas une table par champ - juste une table par champ de recherche ). Ceci est parfois appelé une conception d'index inversé.

Vous pouvez lire un article intéressant de 2009 sur cette solution ici: http://backchannel.org/blog/friendfeed-schemaless-mysql

Ou vous pouvez utiliser une base de données orientée document, dans laquelle il est prévu que vous ayez des champs personnalisés par document. Je choisirais Solr .


1
Pouvez-vous expliquer pourquoi je devrais éviter l'option 3? J'ai regardé certains de vos exemples, mais ils ne correspondent vraiment pas à ce que j'essaie de faire. Je veux simplement un endroit pour stocker des données supplémentaires, pas un endroit pour stocker tous les attributs.
Rachel

2
Pour commencer, qui feriez-vous un attribut NOT NULL? Comment rendriez-vous un attribut UNIQUE sans rendre tous les attributs UNIQUES? Cela continue à partir de là. Vous finissez par écrire du code d'application pour fournir des fonctionnalités que le SGBDR fournit déjà pour vous, au point même d'avoir à écrire une sorte de classe de mappage pour simplement insérer un enregistrement d'entité logique et le récupérer.
Bill Karwin

2
La réponse courte est "ne mélangez pas les données et les métadonnées". La création de colonnes varchar pour fieldnameou tablenamestocke des identificateurs de métadonnées sous forme de chaînes de données, et c'est le début de nombreux problèmes. Voir aussi en.wikipedia.org/wiki/Inner-platform_effect
Bill Karwin

2
@Thomas: dans la conception d'index inversé, vous pouvez utiliser des solutions de schéma standard pour les types de données et des contraintes telles que UNIQUE et FOREIGN KEY. Ceux-ci ne fonctionnent pas du tout lorsque vous utilisez EAV. Je suis d'accord avec les parts d'index inversé avec EAV le trait d'être non relationnel simplement parce qu'il prend en charge des attributs différents par ligne, mais c'est un point de compromis.
Bill Karwin

2
@thitami, ce que j'ai appris au fil des ans, c'est que n'importe quelle solution peut être la bonne pour votre application. Même EAV pourrait être la solution la moins mauvaise pour une application spécifique. Vous ne pouvez pas choisir une stratégie d'optimisation sans connaître vos requêtes. Chaque type d'optimisation améliore certaines requêtes au détriment d'autres requêtes.
Bill Karwin

10

Je créerais très probablement un tableau de la structure suivante:

  • Nom varchar
  • Type de varchar
  • nombre décimal
  • varchar StringValue
  • date DateValue

Les types exacts de cours dépendent de vos besoins (et bien sûr des dbms que vous utilisez). Vous pouvez également utiliser le champ NumberValue (decimal) pour les int et les booléens. Vous pouvez également avoir besoin d'autres types.

Vous avez besoin d'un lien vers les enregistrements maîtres qui possèdent la valeur. Il est probablement plus simple et plus rapide de créer une table de champs utilisateur pour chaque table maître et d'ajouter une simple clé étrangère. De cette façon, vous pouvez filtrer facilement et rapidement les enregistrements principaux par champs utilisateur.

Vous voudrez peut-être avoir une sorte d'informations de métadonnées. Vous vous retrouvez donc avec ce qui suit:

Tableau UdfMetaData

  • identifiant int
  • Nom varchar
  • Type de varchar

Table MasterUdfValues

  • int Master_FK
  • int MetaData_FK
  • nombre décimal
  • varchar StringValue
  • date DateValue

Quoi que vous fassiez, je ne changerais pas la structure de la table de manière dynamique. C'est un cauchemar de maintenance. Je n'utiliserais pas non plus de structures XML, elles sont beaucoup trop lentes.


J'aime votre stratégie, et peut-être opter pour elle mais en 2017, opterez-vous pour quelque chose de différent? like json
maztt

Dans notre projet, nous avons implémenté nos propres structures de données qui se sérialisent en quelque chose de similaire à json. Il dispose d'une interface typeave pour lire et écrire des données sans cast et avec une excellente intégration du langage de programmation. C'est vraiment très bien. Il a le même problème que tous ces types de «documents» dans les bases de données. Il est difficile de rechercher des valeurs spécifiques et il ne peut pas facilement référencer des données en dehors du «document». Selon l'utilisation, les deux ne sont même pas un problème.
Stefan Steinegger

En plus de cela, ce que j'ai proposé en 2011 est IMHO toujours une solution valable.
Stefan Steinegger

10

Cela ressemble à un problème qui pourrait être mieux résolu par une solution non relationnelle, comme MongoDB ou CouchDB.

Ils permettent tous deux une expansion dynamique du schéma tout en vous permettant de conserver l'intégrité du tuple que vous recherchez.

Je suis d'accord avec Bill Karwin, le modèle EAV n'est pas une approche performante pour vous. L'utilisation de paires nom-valeur dans un système relationnel n'est pas intrinsèquement mauvaise, mais ne fonctionne bien que lorsque la paire nom-valeur crée un tuple complet d'informations. Lorsque vous l'utilisez vous oblige à reconstruire dynamiquement une table au moment de l'exécution, toutes sortes de choses commencent à devenir difficiles. L'interrogation devient un exercice de maintenance du pivot ou vous oblige à pousser la reconstruction de tuple vers le haut dans la couche d'objet.

Vous ne pouvez pas déterminer si une valeur nulle ou manquante est une entrée valide ou une absence d'entrée sans incorporer des règles de schéma dans votre couche d'objets.

Vous perdez la capacité de gérer efficacement votre schéma. Un varchar de 100 caractères est-il le bon type pour le champ «valeur»? 200 caractères? Devrait-il être nvarchar à la place? Cela peut être un compromis difficile et qui se termine par la nécessité de placer des limites artificielles sur la nature dynamique de votre ensemble. Quelque chose comme "vous ne pouvez avoir que x champs définis par l'utilisateur et chacun ne peut contenir que y caractères.

Avec une solution orientée document, comme MongoDB ou CouchDB, vous gérez tous les attributs associés à un utilisateur dans un seul tuple. Comme les jointures ne sont pas un problème, la vie est heureuse, car aucun de ces deux ne se débrouille bien avec les jointures, malgré le battage médiatique. Vos utilisateurs peuvent définir autant d'attributs qu'ils le souhaitent (ou vous l'autoriserez) à des longueurs qui ne seront pas difficiles à gérer jusqu'à ce que vous atteigniez environ 4 Mo.

Si vous avez des données qui nécessitent une intégrité de niveau ACID, vous pouvez envisager de diviser la solution, les données à haute intégrité vivant dans votre base de données relationnelle et les données dynamiques vivant dans un magasin non relationnel.


6

Même si vous fournissez à un utilisateur l'ajout de colonnes personnalisées, il ne sera pas nécessairement le cas que l'interrogation sur ces colonnes fonctionnera bien. La conception des requêtes comporte de nombreux aspects qui leur permettent de bien fonctionner, le plus important étant la spécification appropriée de ce qui doit être stocké en premier lieu. Ainsi, fondamentalement, voulez-vous permettre aux utilisateurs de créer un schéma sans réfléchir aux spécifications et être en mesure de tirer rapidement des informations de ce schéma? Si tel est le cas, il est peu probable qu'une telle solution évolue bien, surtout si vous souhaitez permettre à l'utilisateur de faire une analyse numérique des données.

Option 1

IMO, cette approche vous donne un schéma sans aucune connaissance de ce que signifie le schéma, qui est une recette pour un désastre et un cauchemar pour les concepteurs de rapports. Par exemple, vous devez avoir les métadonnées pour savoir quelle colonne stocke quelles données. Si ces métadonnées sont faussées, elles risquent de détruire vos données. De plus, il est facile de mettre les mauvaises données dans la mauvaise colonne. ("Quoi? String1 contient le nom des couvents? Je pensais que c'était la drogue préférée de Chalie Sheen.")

Option 3,4,5

OMI, les exigences 2, 3 et 4 éliminent toute variation d'un EAV. Si vous avez besoin d'interroger, de trier ou de faire des calculs sur ces données, un EAV est le rêve de Cthulhu et le cauchemar de votre équipe de développement et de DBA. Les EAV créeront un goulot d'étranglement en termes de performances et ne vous donneront pas l'intégrité des données dont vous avez besoin pour accéder rapidement aux informations que vous souhaitez. Les requêtes se transformeront rapidement en nœuds gordiens de tableau croisé.

Option 2,6

Cela ne laisse vraiment qu'un choix: rassembler les spécifications, puis construire le schéma.

Si le client souhaite obtenir les meilleures performances sur les données qu'il souhaite stocker, il doit passer par le processus de collaboration avec un développeur pour comprendre ses besoins afin de les stocker le plus efficacement possible. Il peut toujours être stocké dans une table séparée du reste des tables avec du code qui crée dynamiquement un formulaire basé sur le schéma de la table. Si vous avez une base de données qui permet des propriétés étendues sur les colonnes, vous pouvez même les utiliser pour aider le générateur de formulaires à utiliser de jolies étiquettes, info-bulles, etc. afin que tout ce qui était nécessaire soit d'ajouter le schéma. Dans tous les cas, pour créer et exécuter des rapports efficacement, les données doivent être stockées correctement. Si les données en question ont beaucoup de valeurs nulles, certaines bases de données ont la capacité de stocker ce type d'informations. Par exemple,

S'il ne s'agissait que d'un sac de données sur lequel aucune analyse, aucun filtrage ou tri ne devait être effectué, je dirais qu'une variante d'un EAV pourrait faire l'affaire. Cependant, compte tenu de vos besoins, la solution la plus efficace sera d'obtenir les spécifications appropriées même si vous stockez ces nouvelles colonnes dans des tables séparées et créez des formulaires de manière dynamique à partir de ces tables.

Colonnes clairsemées


5
  1. Créez plusieurs tables UDF, une par type de données. Nous aurions donc des tables pour les UDFStrings, les UDFDates, etc.

Selon mes recherches, plusieurs tables basées sur le type de données ne vous aideront pas en termes de performances. Surtout si vous avez des données en vrac, comme des enregistrements 20K ou 25K avec plus de 50 UDF. La performance était la pire.

Vous devriez aller avec une seule table avec plusieurs colonnes comme:

varchar Name
varchar Type
decimal NumberValue
varchar StringValue
date DateValue

Cela devrait être correct et voté. La réponse précédente sur 2011 par Phil n'est plus un bon conseil aujourd'hui 2016.
Yap Kai Lun Leon

Puis-je obtenir un exemple simple de la façon de faire un tel processus en SQL.?
Niroj

Désolé pour la réponse tardive, mais vous voulez une structure de base de données pour la même chose. Je ne t'ai pas compris @Niroj. Pouvez-vous s'il vous plaît expliquer en détail ce que vous voulez.
Amit Contractor

4

Il s'agit d'une situation problématique et aucune des solutions ne semble «correcte». Cependant, l'option 1 est probablement la meilleure tant en termes de simplicité qu'en termes de performances.

C'est également la solution utilisée dans certaines applications d'entreprise commerciales.

ÉDITER

une autre option qui est disponible maintenant, mais qui n'existait pas (ou du moins n'était pas mature) lorsque la question a été initialement posée est d'utiliser les champs json dans la base de données.

de nombreuses bases de données relationnelles prennent désormais en charge les champs basés sur json (qui peuvent inclure une liste dynamique de sous-champs) et permettent de les interroger

postgress

mysql


1
Je déteste l'idée de créer éventuellement des centaines de colonnes inutilisées. Cela va à l'encontre de ce que j'ai appris et lu sur la conception de bases de données SQL. À l'heure actuelle, nous avons plus de 1300 valeurs différentes définies par l'utilisateur, même si beaucoup d'entre elles sont simplement des doublons d'éléments existants qui sont nommés différemment.
Rachel

1300 UDF différents pour une seule table? chaque utilisateur a-t-il la possibilité d'ajouter UDF, ou seulement une sorte d'utilisateur avancé?
Ophir Yoktan

Il fait partie du processus d'importation ... il ajoute toutes les données non mappées à un champ défini par l'utilisateur. Étant donné que personne ne prend le temps de mapper des données non mappées sur des champs UDF existants, il en crée simplement de nouveaux et au fil des ans, beaucoup de choses ont été ajoutées.
Rachel

2

J'ai eu de l'expérience ou 1, 3 et 4 et ils finissent tous soit compliqués, sans savoir quelles sont les données, soit vraiment compliqué avec une sorte de catégorisation douce pour diviser les données en types d'enregistrement dynamiques.

Je serais tenté d'essayer XML, vous devriez être en mesure d'appliquer des schémas au contenu du xml pour vérifier le typage des données, etc., ce qui aidera à contenir des ensembles de données UDF différents. Dans les versions plus récentes du serveur SQL, vous pouvez indexer les champs XML, ce qui devrait améliorer les performances. (voir http://blogs.technet.com/b/josebda/archive/2009/03/23/sql-server-2008-xml-indexing.aspx ) par exemple


Honnêtement, je n'ai pas du tout examiné XML. Le principal inconvénient est que je devrais apprendre comment cela fonctionnait et comment l'interroger, et j'ai entendu dire que les performances peuvent être pires que les autres options
Rachel

1
J'éviterais d'utiliser xml pour cela: cela peut faire le travail, et j'ai implémenté quelque chose comme ça dans xml dans le passé, mais les performances sont devenues assez mauvaises à mesure que les structures de données se développaient, et la complexité du code était élevée.
Kell

2

Si vous utilisez SQL Server, ne négligez pas le type sqlvariant. C'est assez rapide et devrait faire votre travail. D'autres bases de données peuvent avoir quelque chose de similaire.

Les types de données XML ne sont pas très bons pour des raisons de performances. Si vous effectuez des calculs sur le serveur, vous devez constamment les désérialiser.

L'option 1 sonne mal et semble grossière, mais la performance peut être votre meilleure option. J'ai déjà créé des tables avec des colonnes nommées Field00-Field99 parce que vous ne pouvez tout simplement pas battre les performances. Vous devrez peut-être également tenir compte de vos performances INSERT, auquel cas c'est également celui qu'il vous faut. Vous pouvez toujours créer des vues sur cette table si vous voulez qu'elle soit soignée!


Merci, je vais jeter un autre regard sur les variantes SQL. Ma plus grande préoccupation est la performance et je ne sais pas comment cela gérerait cela, surtout si nous parlons de plus de 50 mil lignes
Rachel

Je viens de découvrir que sql_varients ne peut pas être utilisé avec la clause LIKE ... c'est un énorme inconvénient pour moi. Bien sûr, si je crée une vue pour chaque UDF, je pourrais la convertir dans le type de données approprié basé sur SQL_VARIANT_PROPERTY (valeur, 'BaseType') ... encore, semble être mauvais pour les performances
Rachel

Vous pouvez utiliser LIKE, mais vous devez d'abord convertir la valeur. LIKE ne fonctionne que sur varchars, vous devez donc convertir votre sql_variant en varchar. Tant que vous savez ce que votre UDF est un varchar (par exemple parce que le type est stocké ailleurs), vous pouvez filtrer toutes vos lignes en varchars puis lancer et exécuter votre requête LIKE: par exemple. select * FROM MyTable où variant_type = 'v' Cast (variant_value as varchar (max)) LIKE 'Blah%' De cette façon, vous ne convertissez pas les entiers et ainsi de suite en chaînes qui vous ralentiraient.
Tim Rogers

J'aurais besoin d'exécuter des tests pour voir comment les performances sont là-dessus, en particulier avec des millions de lignes. Connaissez-vous des articles en ligne sur les performances utilisant sql_varients? Surtout avec le casting et un très grand nombre de disques?
Rachel


1

J'ai réussi cela avec beaucoup de succès dans le passé en utilisant aucune de ces options (option 6? :)).

Je crée un modèle avec lequel les utilisateurs peuvent jouer (stocker au format XML et exposer via un outil de modélisation personnalisé) et à partir des tables et vues générées par le modèle pour joindre les tables de base avec les tables de données définies par l'utilisateur. Ainsi, chaque type aurait une table de base avec des données de base et une table utilisateur avec des champs définis par l'utilisateur.

Prenons un document comme exemple: les champs typiques seraient le nom, le type, la date, l'auteur, etc. Cela irait dans la table principale. Ensuite, les utilisateurs définiraient leurs propres types de documents spéciaux avec leurs propres champs, tels que contract_end_date, renouveler_clause, bla bla bla. Pour ce document défini par l'utilisateur, il y aurait la table des documents principaux, la table xcontract, jointe sur une clé primaire commune (la clé primaire xcontracts est donc également étrangère sur la clé primaire de la table principale). Ensuite, je générerais une vue pour envelopper ces deux tableaux. Les performances lors des requêtes étaient rapides. des règles métier supplémentaires peuvent également être intégrées dans les vues. Cela a très bien fonctionné pour moi.


1

Notre base de données alimente une application SaaS (logiciel de helpdesk) où les utilisateurs disposent de plus de 7k "champs personnalisés". Nous utilisons une approche combinée:

  1. (EntityID, FieldID, Value)table de recherche des données
  2. un champ JSON dans la entitiestable, qui contient toutes les valeurs d'entité, utilisé pour afficher les données. (de cette façon, vous n'avez pas besoin d'un million de JOIN pour obtenir les valeurs des valeurs).

Vous pouvez diviser davantage le n ° 1 pour avoir une "table par type de données" comme cette réponse suggère , de cette façon vous pouvez même indexer vos UDF.

PS Quelques mots pour défendre l'approche «Entité-Attribut-Valeur» que tout le monde continue de dénigrer. Nous avons utilisé le # 1 sans le # 2 pendant des décennies et cela a très bien fonctionné. Parfois, c'est une décision commerciale. Avez-vous le temps de réécrire votre application et de repenser la base de données ou vous pouvez jeter quelques dollars sur les serveurs cloud, qui sont vraiment bon marché de nos jours? À propos, lorsque nous utilisions l'approche n ° 1, notre base de données contenait des millions d'entités, accédées par des centaines de milliers d'utilisateurs, et un serveur de base de données double cœur de 16 Go fonctionnait très bien


Salut @Alex, je suis tombé sur un problème similaire. Si je comprends bien, vous avez: 1) une custom_fieldstable contenant des valeurs telles que 1 => last_concert_year, 2 => band, 3 => musicpuis une custom_fields_valuestable avec les valeurs 001, 1, 1976 002, 1, 1977 003, 2, Iron Maiden003, 3 , Metal J'espère que l'exemple a du sens pour vous et désolé pour le formatage!
thitami

@thitami pas exactement. En suivant votre exemple: j'ai un bandstableau avec une ligne 1,'Iron Maiden'puis custom_fieldsavec des lignes 1,'concert_year' | 2,'music'puis custom_fields_valuesavec des lignes1,1,'1977'|1,2,'metal'
Alex

0

Dans les commentaires, je vous ai vu dire que les champs UDF doivent vider les données importées qui ne sont pas correctement mappées par l'utilisateur.

Une autre option est peut-être de suivre le nombre d'UDF créés par chaque utilisateur et de les forcer à réutiliser des champs en disant qu'ils peuvent utiliser 6 (ou une autre limite également aléatoire) en haut de champs personnalisés.

Lorsque vous êtes confronté à un problème de structuration de base de données comme celui-ci, il est souvent préférable de revenir à la conception de base de l'application (système d'importation dans votre cas) et de lui imposer quelques contraintes supplémentaires.

Maintenant, ce que je ferais, c'est l'option 4 (EDIT) avec l'ajout d'un lien vers les utilisateurs:

general_data_table
id
...


udfs_linked_table
id
general_data_id
udf_id


udfs_table
id
name
type
owner_id --> Use this to filter for the current user and limit their UDFs
string_link_id --> link table for string fields
int_link_id
type_link_id

Assurez-vous maintenant de créer des vues pour optimiser les performances et obtenir les bons index. Ce niveau de normalisation réduit l'encombrement de la base de données, mais votre application plus complexe.


0

Je recommanderais le n ° 4 car ce type de système a été utilisé dans Magento, une plate-forme CMS de commerce électronique hautement accréditée. Utilisez une table unique pour définir vos champs personnalisés à l'aide des colonnes fieldId et label . Ensuite, ayez des tables séparées pour chaque type de données et dans chacune de ces tables ont un index qui indexe par fieldId et les colonnes de valeur de type de données . Ensuite, dans vos requêtes, utilisez quelque chose comme:

SELECT *
FROM FieldValues_Text
WHERE fieldId IN (
    SELECT fieldId FROM Fields WHERE userId=@userId
)
AND value LIKE '%' + @search + '%'

Cela garantira à mon avis les meilleures performances possibles pour les types définis par l'utilisateur.

D'après mon expérience, j'ai travaillé sur plusieurs sites Web Magento qui servent des millions d'utilisateurs par mois, hébergent des milliers de produits avec des attributs de produit personnalisés et la base de données gère facilement la charge de travail, même pour les rapports.

Pour la création de rapports, vous pouvez PIVOTconvertir les valeurs de libellé de votre table Fields en noms de colonne, puis faire pivoter les résultats de votre requête de chaque table de type de données dans ces colonnes pivotées.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.