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:
+ -------------- +
| chose |
| -------------- |
| id |
| type |
| desc |
| attr1 |
| attr2 |
| attr3 |
| attr4 |
| attr5 |
+ -------------- +
C'est après avoir ajouté quelques attributs. Au lieu de attr1
faire semblant, il lit artist
ou tracks
ou genre
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:
+ -------------- + + --------------- + + ------------- +
| chose | | thing_attr | | attr |
| -------------- | | --------------- | | ------------- |
| id | <--- + | thing_id (fk) | +> | id |
| type | | attr_id (fk) | + - + | nom |
| desc | | valeur | | |
+ -------------- + + --------------- + + ------------- +
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 le thing_attr
avec une paire de clés étrangères se rapportant à la thing
table et à la attr
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.)
Existe-t-il des inconvénients à simplement modifier le schéma lors de l'exécution? Si l'utilisateur pense qu'une chose a besoin d'un nouvel attribut, il suffit d'ajouter dynamiquement une colonne à la table?
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:
- Vous faites de la programmation de métadonnées. Au lieu de pouvoir mapper proprement cette colonne à ce champ avec un ORM sympa, vous faites probablement des choses comme
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.
- Vous avez probablement abandonné l'ORM. Cela signifie que vous écrivez SQL brut pour tout le code au lieu de laisser le système faire le travail pour vous.
- Vous avez renoncé à faire des mises à niveau propres. Que se passe-t-il lorsque le client ajoute un champ avec un nom que votre prochaine version utilise également? Dans le site de matchmaking, la mise à niveau qui souhaite ajouter un
hasdate
champ pour stocker un horodatage a déjà été définie comme hasdate
avec un booléen pour une correspondance réussie ... et votre mise à niveau s'arrête.
- Vous êtes confiant que le client ne casse pas le système en utilisant un mot réservé qui casse aussi vos requêtes ... quelque part.
- Vous vous êtes lié à une seule marque de base de données. Le DDL de différentes bases de données est différent. Les types de bases de données en sont l'exemple le plus simple.
varchar2
vs text
et similaires. Votre code pour ajouter la colonne fonctionnerait sur MySQL mais pas Postgres ou Oracle ou SQL Server.
- Est - ce que vous faites confiance au client d'ajouter réellement les données bien ? Bien sûr, l'EAV est loin d'être idéal, mais maintenant vous avez des noms de table obscurs horribles que vous, le développeur, n'avez pas ajoutés, avec le mauvais type d'index (le cas échéant), sans aucune contrainte ajoutée dans le code là où il le faut être et ainsi de suite.
- Vous avez accordé des privilèges de modification de schéma à l'utilisateur exécutant l'application. Little Bobby Drop Tables n'est pas possible lorsque vous êtes limité à SQL plutôt qu'à DDL (bien sûr, vous pouvez le faire à la
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.