D'une manière générale, est-il considéré comme une mauvaise pratique d'autoriser les champs créés par l'utilisateur dans une base de données pour une webapp?
Par exemple, je crée une webapp d'inventaire de maison pour ma femme, et elle va vouloir définir ses propres champs pour différents articles. J'avais l'intention de lui permettre de créer des catégories d'articles et d'ajouter des "fonctionnalités" à ces catégories. Les fonctionnalités ne seraient que des clés / valeurs stockées sous forme de chaînes. De cette façon, si elle avait une catégorie appelée "CD audio" par exemple, elle pourrait ajouter des fonctionnalités pour des trucs comme "artiste", "pistes", etc. Mais dans une autre catégorie comme "meubles", elle pourrait ajouter des fonctionnalités pour des trucs comme "matériel" "(bois, plastique, etc.). Ensuite, n'importe quel élément peut appartenir à une (ou plusieurs) catégories, en ajoutant ces fonctionnalités à l'élément.
Je peux voir des problèmes où la recherche par ces fonctionnalités nécessite des comparaisons de chaînes, il n'y a pas de validation des données, etc. comme nous allons. Dans mon exemple, c'est une petite base d'utilisateurs (2 d'entre nous) et la quantité d'enregistrements créés serait petite, donc pas trop mal.
D'une manière générale cependant, comment les gens gèrent-ils quelque chose comme ça dans la «vraie vie»?
Réponses:
Lorsque vous commencez à accéder aux «champs définis par l'utilisateur», comme on le trouve souvent dans les suiveurs de bogues, la gestion des ressources client et les outils commerciaux similaires, ils ne sont pas sauvegardés avec une table avec des champs bajillion (s'ils le sont, alors c'est probablement un problème de sa propre).
Au lieu de cela, vous trouvez des conceptions de table de valeur d'attribut d'entité et l'outil d'administration associé pour gérer les attributs valides.
Considérez le tableau suivant:
C'est après avoir ajouté quelques attributs. Au lieu de
attr1
faire semblant, il litartist
outracks
ougenre
ou tout ce que la chose possède. Et au lieu de 5, que se passerait-il si c'était 50. C'est clairement ingérable. Elle nécessite également une mise à jour du modèle et un redéploiement de l'application pour gérer un nouveau champ. Pas idéal.Considérez maintenant la structure de table suivante:
Vous avez votre truc avec ses champs de base. Vous avez deux autres tables. Un avec les attributs. Chaque champ est une ligne du
attr
tableau. Et puis il y a lething_attr
avec une paire de clés étrangères se rapportant à lathing
table et à laattr
table. Et cela a alors un champ de valeur où vous stockez quelle que soit la valeur du champ pour cette entité.Et maintenant, vous avez une structure où la table attr peut être mise à jour au moment de l'exécution et de nouveaux champs peuvent être ajoutés (ou supprimés) à la volée sans impact significatif sur l'application globale.
Les requêtes sont un peu plus complexes et la validation devient aussi plus complexe (soit des procédures stockées géniales ou tout côté client). C'est un compromis dans la conception.
Considérez également la situation où un jour vous devez effectuer une migration et vous revenez à l'application pour constater qu'il existe maintenant une demi-douzaine d'attributs de plus que le schéma que vous avez distribué à l'origine. Cela rend les migrations et les mises à niveau moches où la table de valeur d'attribut d'entité, lorsqu'elle est utilisée correctement, peut être plus propre. (Pas toujours, mais peut l'être.)
Si vous travaillez avec la saveur appropriée de la base de données nosql, vous pourriez probablement le faire (notez que la saveur appropriée du nosql pour cela serait probablement un magasin de valeurs-clés qui est, eh bien, la table EAV pour les relationnelles décrites ci-dessus) sans trop de peine. Cependant, il est livré avec tous les compromis pour nosql qui sont décrits ailleurs en détail.
Si vous travaillez à la place sur une base de données relationnelle - vous devez avoir le schéma. L'ajout dynamique de la colonne signifie que certains sous-ensembles des éléments suivants sont vrais:
select *
, puis faites du code complexe pour découvrir ce que sont réellement les données (voir le ResultSetMetaData de Java ), puis stockez cela dans une carte ( ou un autre type de données - mais pas de beaux champs dans le code). Cela jette alors un peu de sécurité de type et de faute de frappe que vous avez avec l'approche traditionnelle.hasdate
champ pour stocker un horodatage a déjà été définie commehasdate
avec un booléen pour une correspondance réussie ... et votre mise à niveau s'arrête.varchar2
vstext
et similaires. Votre code pour ajouter la colonne fonctionnerait sur MySQL mais pas Postgres ou Oracle ou SQL Server.delete * from students
place, mais vous ne pouvez pas vraiment gâcher la base de données de mauvaises manières). Le nombre de choses qui peuvent mal tourner avec l'accès au schéma à la suite d'un accident ou d'une activité malveillante.Cela se résume vraiment à «ne pas le faire». Si vous le voulez vraiment, optez pour un modèle connu de la structure de la table EAV ou une base de données entièrement dédiée à cette structure. Ne laissez pas les gens créer des champs arbitraires dans une table. Les maux de tête n'en valent pas la peine.
la source
Il est difficile de bien faire cela.
Pour une application unique comme celle que vous planifiez, vous pouvez bien sûr simplement ajouter une colonne pour chaque champ et fournir une interface utilisateur qui rend la définition de champ par des utilisateurs non formés plus sûre que de leur donner une ligne de commande SQL. Ou vous pourriez suivre le modèle redouté Entity-Attribute-Value , qui est une réponse classique, quoique quelque peu effrayante, à ce genre de problème. La construction de l'interface utilisateur pour définir les champs EAV est généralement beaucoup plus complexe que pour les colonnes de base de données, et les requêtes peuvent devenir assez floues, mais pour un grand nombre de champs ( c'est -à- dire , les schémas à matrice très clairsemée), cela peut être le seul moyen d'obtenir le travail fait.
la source
Je suis venu une croix quelque chose de similaire récemment.
J'ai fait 2 tableaux.
Il est tous vos objets. Vous en définissez le nom.
Et un type de cet objet: - pour moi, les types disponibles étaient inventaire, inventaire_item, bureau.
Et la configuration habituelle était n éléments sont un enfant ou un inventaire qui est également un enfant de bureau et j'ai utilisé une table de jointure pour joindre des objets les uns aux autres
Le tableau des paramètres contient chaque nom de champ pour ce type d'objet spécifique et la valeur en valeur.
Exemple de propriétés de bureau
Lieu, téléphone, horaires
Et pour les articles
Etc, toutes ces propriétés sont appliquées par votre modèle et enregistrées dans le tableau des paramètres en tant que lignes distinctes (mais utilisez remplacer et non insérer pour éviter plusieurs lignes pour le même champ)
Donc, quand je veux un bureau, je le charge facilement avec toutes ses relations et ses paramètres dans lesquels les paramètres object_I'd (objets demandés)
Après cela, je fais pivoter toutes les lignes des paramètres et c'est tout.
Et dans le cas où je voulais qu'un paramètre soit spécifique à un article dans un inventaire (non global), je définis object_I'd = je serais dans la table des relations object_objects et je définirai settings.type = relation_setting
J'espère que vous comprenez ce que je veux dire je vais essayer de reformater la réponse quand j'arriverai à un ordinateur portable
la source
Non, ce n'est pas une mauvaise pratique. C'est assez courant. En termes OO, cela s'appelle l'héritage. Vous avez un inventaire de classe de base et deux classes héritées AudioCD et meubles.
Vous devez décider de la façon dont InventoryItem, AudioCD et les meubles sont stockés dans la base de données.
Si easy-query est le plus important pour vous et que db-space / normalisation n'a pas d'importance, vous implémenterez le schéma "table par hiérarchie".
Si l'espace / la normalisation est le plus important pour vous et que les requêtes plus compliquées ne posent aucun problème, vous implémenterez le schéma "table par type".
Pour plus de détails, reportez-vous à la section dotnet table-by-type-vs-table-per-hierarchy-inheritance ou java hibernate héritage .
la source