web-dev-qa-db-fra.com

Comment concevoir une table de produits pour de nombreux types de produits où chaque produit a de nombreux paramètres

Je n'ai pas beaucoup d'expérience dans la conception de tables. Mon objectif est de créer une ou plusieurs tables de produits répondant aux exigences ci-dessous:

  • Prend en charge de nombreux types de produits (TV, téléphone, PC, ...). Chaque type de produit a un ensemble de paramètres différent, comme:

    • Le téléphone aura la couleur, la taille, le poids, le système d'exploitation ...

    • Le PC aura un CPU, un disque dur, une RAM ...

  • L'ensemble des paramètres doit être dynamique. Vous pouvez ajouter ou modifier n'importe quel paramètre que vous aimez.

Comment puis-je répondre à ces exigences sans tableau séparé pour chaque type de produit?

126
StoneHeart

Vous avez au moins ces cinq options pour modéliser la hiérarchie de types que vous décrivez:

  • Single Table Inheritance : une table pour tous les types de produits, avec suffisamment de colonnes pour stocker tous les attributs de tous les types. Cela signifie beaucoup de colonnes, dont la plupart sont NULL sur une ligne donnée.

  • Class Table Inheritance : une table pour les produits, stockant les attributs communs à tous les types de produits. Ensuite, une table par type de produit, stockant les attributs spécifiques à ce type de produit.

  • Concrete Table Inheritance : pas de table pour les attributs de produits communs. Au lieu de cela, une table par type de produit, stockant à la fois les attributs de produit courants et les attributs spécifiques au produit.

  • LOB sérialisé : une table pour les produits, stockant les attributs communs à tous les types de produits. Une colonne supplémentaire stocke un BLOB de données semi-structurées, au format XML, YAML, JSON ou un autre format. Ce BLOB vous permet de stocker les attributs spécifiques à chaque type de produit. Vous pouvez utiliser des motifs de conception sophistiqués pour décrire cela, tels que Façade et Memento. Mais peu importe que vous ayez un blob d'attributs qui ne peut pas être facilement interrogé dans SQL; vous devez récupérer le blob entier dans l'application et le trier là-bas.

  • Entity-Attribute-Value : une table pour les produits et une table qui pivote les attributs en lignes, au lieu de colonnes. L'EAV n'est pas une conception valable en ce qui concerne le paradigme relationnel, mais de nombreuses personnes l'utilisent quand même. C'est le "modèle de propriétés" mentionné par une autre réponse. Voir d'autres questions avec le eav tag sur StackOverflow pour certains des pièges.

J'ai écrit plus à ce sujet dans une présentation, Modélisation extensible des données .


Réflexions supplémentaires sur l'EAV: Bien que beaucoup de gens semblent favoriser l'EAV, je ne le fais pas. Cela semble être la solution la plus flexible, et donc la meilleure. Cependant, gardez à l'esprit l'adage TANSTAAFL . Voici certains des inconvénients de l'EAV:

  • Aucun moyen de rendre une colonne obligatoire (équivalent de NOT NULL).
  • Aucun moyen d'utiliser des types de données SQL pour valider les entrées.
  • Aucun moyen de s'assurer que les noms d'attribut sont orthographiés de manière cohérente.
  • Aucun moyen de mettre une clé étrangère sur les valeurs d'un attribut donné, par exemple pour une table de recherche.
  • La récupération des résultats dans une disposition tabulaire conventionnelle est complexe et coûteuse, car pour obtenir des attributs à partir de plusieurs lignes, vous devez faire JOIN pour chaque attribut.

Le degré de flexibilité que l'EAV vous offre nécessite des sacrifices dans d'autres domaines, ce qui rend probablement votre code aussi complexe (ou pire) qu'il ne l'aurait été pour résoudre le problème d'origine d'une manière plus conventionnelle.

Et dans la plupart des cas, il n'est pas nécessaire d'avoir ce degré de flexibilité. Dans la question du PO sur les types de produits, il est beaucoup plus simple de créer une table par type de produit pour les attributs spécifiques au produit, vous avez donc une structure cohérente appliquée au moins pour les entrées du même type de produit.

Je n'utiliserais l'EAV que si chaque ligne devait avoir potentiellement un ensemble d'attributs distinct. Lorsque vous disposez d'un ensemble fini de types de produits, l'EAV est excessif. L'héritage de table de classe serait mon premier choix.


Mise à jour 2019: plus je vois de gens utiliser JSON comme solution au problème des "nombreux attributs personnalisés", moins j'aime cette solution. Cela rend les requêtes trop complexes, même lorsque vous utilisez des fonctions spéciales fonctions JSON pour les prendre en charge. Il faut beaucoup plus d'espace de stockage pour stocker des documents JSON, par rapport au stockage dans des lignes et des colonnes normales.

Fondamentalement, aucune de ces solutions n'est facile ou efficace dans une base de données relationnelle. L'idée d'avoir des "attributs variables" est fondamentalement contraire à la théorie relationnelle.

Cela revient à dire que vous devez choisir l'une des solutions en fonction de laquelle est la moins mauvaise pour votre application. Par conséquent, vous devez savoir comment vous allez interroger les données avant de choisir une conception de base de données. Il n'y a aucun moyen de choisir une solution qui est "la meilleure", car l'une des solutions pourrait être la meilleure pour une application donnée.

211
Bill Karwin

@Cœur de pierre

J'irais ici avec EAV et MVC tout le chemin.

@Bill Karvin

Voici certains des inconvénients de l'EAV:

No way to make a column mandatory (equivalent of NOT NULL).
No way to use SQL data types to validate entries.
No way to ensure that attribute names are spelled consistently.
No way to put a foreign key on the values of any given attribute, e.g.

pour une table de recherche.

Toutes ces choses que vous avez mentionnées ici:

  • la validation des données
  • validation de l'orthographe des noms d'attribut
  • colonnes/champs obligatoires
  • gérer la destruction des attributs dépendants

à mon avis, n'appartiennent pas du tout à une base de données car aucune des bases de données n'est capable de gérer ces interactions et exigences à un niveau approprié comme le fait un langage de programmation d'une application.

À mon avis, utiliser une base de données de cette façon, c'est comme utiliser une pierre pour marteler un clou. Vous pouvez le faire avec un rocher mais n'êtes-vous pas supposé utiliser un marteau plus précis et spécialement conçu pour ce genre d'activité?

La récupération des résultats dans une disposition tabulaire conventionnelle est complexe et coûteuse, car pour obtenir des attributs à partir de plusieurs lignes, vous devez faire JOIN pour chaque attribut.

Ce problème peut être résolu en effectuant quelques requêtes sur des données partielles et en les transformant en présentation tabulaire avec votre application. Même si vous disposez de 600 Go de données produit, vous pouvez les traiter par lots si vous avez besoin de données de chaque ligne de ce tableau.

Pour aller plus loin Si vous souhaitez améliorer les performances des requêtes, vous pouvez sélectionner certaines opérations comme par exemple rapports ou recherche de texte globale et préparez-les pour les tables d'index qui stockeraient les données requises et seraient régénérées périodiquement, disons toutes les 30 minutes.

Vous n'avez même pas besoin de vous soucier du coût du stockage de données supplémentaire, car il devient de moins en moins cher chaque jour.

Si vous êtes toujours préoccupé par les performances des opérations effectuées par l'application, vous pouvez toujours utiliser Erlang, C++, Go Language pour prétraiter les données et plus tard simplement traiter les données optimisées plus loin dans votre application principale.

13
Pawel Barcik

Si j'utilise Class Table Inheritance sens:

une table pour les produits, stockant les attributs communs à tous les types de produits. Ensuite, une table par type de produit, stockant les attributs spécifiques à ce type de produit. -Bill Karwin

Ce que j'aime le mieux des suggestions de Bill Karwin. Je peux en quelque sorte prévoir un inconvénient, que j'essaierai d'expliquer comment éviter de devenir un problème.

Quel plan d'urgence devrais-je avoir en place lorsqu'un attribut qui n'est commun qu'à 1 type devient alors commun à 2, puis à 3, etc.?

Par exemple: (ce n'est qu'un exemple, pas mon vrai problème)

Si nous vendons des meubles, nous pourrions vendre des chaises, des lampes, des canapés, des téléviseurs, etc. Le type de téléviseur pourrait être le seul type que nous transportons qui a une consommation d'énergie. Je mettrais donc le power_consumption attribut sur le tv_type_table. Mais ensuite, nous commençons à proposer des systèmes de cinéma maison qui ont également un power_consumption propriété. OK c'est juste un autre produit donc j'ajouterai ce champ au stereo_type_table également puisque c'est probablement plus facile à ce stade. Mais au fil du temps, alors que nous commençons à transporter de plus en plus d'électronique, nous réalisons que power_consumption est suffisamment large pour figurer dans le main_product_table. Qu'est-ce que je devrais faire maintenant?

Ajoutez le champ au main_product_table. Écrivez un script pour parcourir l'électronique et mettez la valeur correcte de chaque type_table au main_product_table. Déposez ensuite cette colonne de chaque type_table.

Maintenant, si j'utilisais toujours la même classe GetProductData pour interagir avec la base de données pour extraire les informations sur le produit; alors si des modifications de code doivent maintenant être refactorisées, elles doivent concerner uniquement cette classe.

5
JD Isaacks

Vous pouvez avoir une table Product et une table ProductAdditionInfo distincte avec 3 colonnes: ID produit, nom d'informations supplémentaires, valeur d'informations supplémentaires. Si la couleur est utilisée par de nombreux produits, mais pas par tous, vous pouvez la définir comme une colonne nullable dans la table Product, ou simplement la placer dans ProductAdditionalInfo.

Cette approche n'est pas une technique traditionnelle pour une base de données relationnelle, mais j'ai vu qu'elle était très utilisée en pratique. Il peut être flexible et avoir de bonnes performances.

Steve Yegge appelle cela le modèle Propriétés et a écrit un long article sur son utilisation.

3
RossFabricant