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

140

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 différent de paramètres, comme:

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

    • Le PC aura CPU, HDD, RAM ...

  • L'ensemble des paramètres doit être dynamique. Vous pouvez ajouter ou modifier n'importe quel paramètre de votre choix.

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

Cœur de pierre
la source

Réponses:

233

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

  • Héritage de table unique : 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.

  • Héritage de table de classe : une table pour les produits, stockant les attributs communs à tous les types de produits. Puis une table par type de produit, stockant les attributs spécifiques à ce type de produit.

  • Héritage de table en béton : pas de tableau pour les attributs produits communs. Au lieu de cela, une table par type de produit, stockant à la fois les attributs de produit communs 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 dans un autre format. Ce BLOB vous permet de stocker les attributs spécifiques à chaque type de produit. Vous pouvez utiliser des modèles de conception sophistiqués pour décrire cela, tels que Façade et Memento. Mais quoi qu'il en soit, vous avez un tas d'attributs qui ne peuvent pas être facilement interrogés 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 fait pivoter les attributs vers des lignes, au lieu de colonnes. EAV n'est pas une conception valide par rapport au 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 les autres questions avec la balise eav 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 de nombreuses personnes semblent favoriser l'EAV, je ne le suis pas. Cela semble être la solution la plus flexible, et donc la meilleure. Cependant, gardez à l'esprit l'adage TANSTAAFL . Voici quelques-uns des inconvénients de l'EAV:

  • Pas moyen de rendre une colonne obligatoire (équivalent de NOT NULL).
  • Aucun moyen d'utiliser les types de données SQL pour valider les entrées.
  • Aucun moyen de garantir que les noms d'attributs 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 mise en page tabulaire conventionnelle est complexe et coûteuse, car pour obtenir des attributs à partir de plusieurs lignes, vous devez le faire JOINpour chaque attribut.

Le degré de flexibilité que vous offre EAV nécessite des sacrifices dans d'autres domaines, rendant 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 un tableau par type de produit pour les attributs spécifiques au produit, de sorte que vous ayez une structure cohérente appliquée au moins pour les entrées du même type de produit.

J'utiliserais EAV uniquement si chaque ligne doit être autorisée à avoir potentiellement un ensemble distinct d'attributs. Lorsque vous avez un ensemble fini de types de produits, EAV est exagéré. L'héritage de table de classe serait mon premier choix.


Mise à jour 2019: plus je vois des 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 JSON spéciales pour les prendre en charge. Le stockage des documents JSON nécessite beaucoup plus d'espace de stockage que le stockage dans des lignes et des colonnes normales.

Fondamentalement, aucune de ces solutions n'est simple ou efficace dans une base de données relationnelle. L'idée même d'avoir des «attributs variables» est fondamentalement en contradiction avec la théorie relationnelle.

En résumé, vous devez choisir l'une des solutions en fonction de celle qui 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 soit «meilleure» car l'une des solutions peut être la meilleure pour une application donnée.

Bill Karwin
la source
11
@HimalayaGarg Option "4.5" est vraiment l'opposé de tout l'intérêt du post de Bill.
user3308043
2
Contrairement à MySQL, SQL Server a une prise en charge étendue de XML, XPath et XQuery. Donc, pour les utilisateurs de SQL Server, la meilleure option serait de stocker des attributs supplémentaires dans une colonne de type XML (option 4). De cette façon, vous N'AVEZ PAS besoin de «récupérer le blob entier dans l'application et de le trier». Vous pouvez même créer des index sur des colonnes XML dans SQL Server.
Delphi.Boy
2
Je préfère le LOB sérialisé pour mon cas. Mais convient-il à l'ORM? J'utilise EF.
Mahmood Jenami
@ user2741577, bien sûr, mais vous devrez probablement écrire du code personnalisé pour décompresser les champs de données non structurées du LOB et les appliquer à chaque champ d'entité de votre objet ORM. Je ne connais pas EF, mais je suppose que vous pouvez créer une classe ORM de base qui fait cela. Vous devez garder une trace des champs provenant de champs concrets de la ligne de base de données et des champs provenant des champs du LOB, afin que vous puissiez reformer un LOB lorsqu'il est temps d'enregistrer l'objet.
Bill Karwin
12

@Cœur de pierre

J'irais ici avec EAV et MVC jusqu'au bout.

@Bill Karvin

Voici quelques-uns des inconvénients de l'EAV:

  • Pas moyen de rendre une colonne obligatoire (équivalent de NOT NULL).
  • Aucun moyen d'utiliser les types de données SQL pour valider les entrées.
  • Aucun moyen de garantir que les noms d'attributs 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.

Toutes ces choses que vous avez mentionnées ici:

  • la validation des données
  • validation orthographique des noms d'attribut
  • colonnes / champs obligatoires
  • gestion de la destruction des attributs dépendants

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

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

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

Ce problème peut être résolu en effectuant quelques requêtes sur des données partielles et en les traitant sous forme de tableau 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 des données de chaque ligne de ce tableau.

Aller plus loin Si vous souhaitez améliorer les performances des requêtes, vous pouvez sélectionner certaines opérations comme par exemple la création de rapports ou la recherche de texte globale et préparer pour elles des 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 chaque jour moins cher et moins cher.

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 ensuite simplement traiter les données optimisées plus loin dans votre application principale.

Pawel Barcik
la source
you can always use Erlang, C++, Go Language to pre-process the dataQue voulais-tu dire? Au lieu de DB, utilisez Go lang? Pourriez-vous nous en dire plus?
Vert
1
Je suis tout à fait d'accord. EAV est une voie à suivre, surtout si vous avez besoin d'un niveau de flexibilité qui vous permettrait d'ajouter de nouveaux types de produits et de paramètres sans changements de schéma de base de données, je veux dire en direct en production via votre applicaion. J'y suis allé, j'ai fait ça. A travaillé pour moi. À propos des requêtes lentes ... est-ce que quelqu'un ici a déjà entendu parler des caches? ;)
pawel.kalisz
@Green J'ai modifié le dernier paragraphe pour le rendre plus clair, mais il s'agit de transmettre vos données EAV brutes à un processus dans un langage capable de gérer les transformations de données, les recherches dans une structure arborescente ou toute carte de base réduisant les opérations très rapidement et d'une manière efficace de mémoire. Les détails ici dépendraient de ce qui doit être optimisé
Pawel Barcik
6

Si j'utilise le Class Table Inheritancesens:

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

Ce que j'aime le plus parmi les suggestions de Bill Karwin. Je peux prévoir un inconvénient, que je vais essayer d'expliquer comment éviter de devenir un problème.

Quel plan d'urgence dois-je mettre 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 pouvons vendre des chaises, des lampes, des canapés, des téléviseurs, etc. Je mettrais donc l' power_consumptionattribut sur le tv_type_table. Mais ensuite, nous commençons à proposer des systèmes de cinéma maison qui ont également une power_consumptionpropriété. OK, c'est juste un autre produit, je vais donc ajouter ce champ à la stereo_type_tablecar c'est probablement le plus facile à ce stade. Mais au fil du temps, alors que nous commençons à transporter de plus en plus d'électronique, nous nous rendons compte que power_consumptionc'est assez large pour qu'il devrait être 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 chacun type_tabledans le main_product_table. Supprimez ensuite cette colonne de chacun type_table.

Maintenant, si j'utilisais toujours la même GetProductDataclasse pour interagir avec la base de données pour extraire les informations sur le produit; alors si des changements dans le code doivent maintenant être refactorisés, ils ne devraient concerner que cette classe.

JD Isaacks
la source
3

Vous pouvez avoir une table Product et une table ProductAdditionInfo distincte avec 3 colonnes: ID de produit, nom d'informations supplémentaires, valeur d'informations supplémentaires. Si la couleur est utilisée par de nombreux types de produits, mais pas par tous, vous pouvez l'inclure dans 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 je l'ai vu beaucoup utilisée dans la pratique. Il peut être flexible et avoir de bonnes performances.

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

RossFabricant
la source
4
Le modèle de propriétés est simplement Entité-Attribut-Valeur par un autre nom. Il est largement utilisé, mais son stockage dans une base de données relationnelle enfreint les règles de normalisation.
Bill Karwin
2
Pour être honnête, quand j'ai lu la description d'EAV dans @Bills, je n'ai pas bien compris ce qu'il expliquait. Mais quand vous avez dit, 3 columns: product ID, additional info name, additional info valuej'ai compris le concept. Et j'ai déjà fait cela avant, et j'ai rencontré des problèmes. Cependant, je ne me souviens pas pour le moment quels étaient ces problèmes.
JD Isaacks
1
@JDIsaacks Dans ce modèle, un problème courant est que nous ne savons pas combien de JOINs avons-nous besoin pour récupérer tous les attributs.
Omid