Je travaille sur la mise à jour de la base de données des produits de notre site Web. Il est construit dans MySQL, mais il s'agit plus d'une question générale de modèle de conception de base de données.
Je prévois de passer à un modèle Supertype / Subtype. Notre base de données actuelle / précédente est principalement une table unique qui contient des données sur un seul type de produit. Nous envisageons d'élargir notre offre de produits pour inclure des produits différents.
Ce nouveau projet de conception est comme ceci:
Product product_[type] product_attribute_[name]
---------------- ---------------- ----------------------------
part_number (PK) part_number (FK) attributeId (PK)
UPC specific_attr1 (FK) attribute_name
price specific_attr2 (FK)
... ...
J'ai une question concernant les tableaux d'attributs du produit. L'idée ici est qu'un produit peut avoir une liste d'attributs donnés tels que la couleur: rouge, vert, bleu ou le matériau: plastique, bois, chrome, aluminium, etc.
Cette liste serait stockée dans une table et la clé primaire (PK) pour cet élément d'attribut sera utilisée dans la table de produit spécifique en tant que clé étrangère (FK).
(Le livre de Martin Fowler intitulé Patterns of Enterprise Application Architecture appelle ceci " Foreign Key Mapping ")
Cela permet à une interface de site Web d'extraire la liste des attributs pour un type d'attribut donné et de la recracher dans un menu de sélection déroulant ou un autre élément d'interface utilisateur. Cette liste peut être considérée comme une liste "autorisée" de valeurs d'attribut.
Le nombre de jointures qui finissent par se produire lors de l'extraction d'un produit spécifique me semble excessif. Vous devez joindre chaque table d'attributs de produit au produit pour pouvoir obtenir les champs de cet attribut. Généralement, ce champ peut simplement n'être rien de plus qu'une chaîne (varchar) pour son nom.
Ce modèle de conception finit par créer un grand nombre de tables et vous vous retrouvez avec une table pour chaque attribut. Une idée pour contrecarrer cela serait de créer quelque chose de plus d'une table de «sac de saisie» pour tous les attributs de produit. Quelque chose comme ça:
product_attribute
----------------
attributeId (PK)
name
field_name
De cette façon, votre table pourrait ressembler à ceci:
1 red color
2 blue color
3 chrome material
4 plastic material
5 yellow color
6 x-large size
Cela pourrait aider à réduire le fluage de la table, mais cela ne réduit pas le nombre de jointures et il semble un peu mal de combiner autant de types différents en une seule table. Mais vous pourriez obtenir assez facilement tous les attributs de «couleur» disponibles.
Cependant, il peut y avoir un attribut qui a plus de champs que simplement "nom" comme la valeur RVB d'une couleur. Cela nécessiterait que cet attribut spécifique ait éventuellement une autre table ou un seul champ pour la paire nom: valeur (qui a ses propres inconvénients).
Le dernier modèle de conception auquel je peux penser est de stocker la valeur d'attribut réelle dans la table de produit spécifique et de ne pas avoir de «table d'attribut» du tout. Quelque chose comme ça:
Product product_[type]
---------------- ----------------
part_number (PK) part_number (FK)
UPC specific_attr1
price specific_attr2
... ...
Au lieu d'une clé étrangère vers une autre table, elle contiendrait la valeur réelle telle que:
part_number color material
----------- ----- --------
1234 red plastic
Cela éliminerait les jointures et empêcherait le fluage de la table (peut-être?). Cependant, cela empêche d'avoir une «liste autorisée» d'attributs. Vous pouvez renvoyer toutes les valeurs actuellement saisies pour un champ donné (c'est-à-dire: couleur) mais cela élimine également l'idée d'avoir une «liste autorisée» de valeurs pour un attribut donné.
Pour avoir cette liste, vous devez toujours créer une table d'attributs «grab bag» ou avoir plusieurs tables (fluage de table) pour chaque attribut.
Cela crée le plus gros inconvénient (et pourquoi je n'ai jamais utilisé cette approche) d'avoir maintenant le nom du produit à plusieurs endroits.
Si vous avez la valeur de couleur «rouge» dans la «table d'attributs principale» et que vous la stockez également dans la table «produit_ [type]», une mise à jour de la table «principale» entraînera un problème potentiel d'intégrité des données si l'application ne fonctionne pas. 'ne mettez pas non plus à jour tous les enregistrements avec l'ancienne valeur dans la table "product_type".
Donc, après mes longues explications et analyses de ce scénario, je me rends compte que cela ne peut pas être un scénario inhabituel et il pourrait même y avoir un nom pour ce type de situation.
Existe-t-il des solutions généralement acceptées à ce défi de conception? Le nombre potentiellement important de jointures est-il acceptable si les tables sont relativement petites? Le stockage du nom d'attribut, au lieu d'un PK d'attribut est-il acceptable dans certaines situations? Y a-t-il une autre solution à laquelle je ne pense pas?
Quelques notes sur cette base de données / application de produit:
- Les produits ne sont pas fréquemment mis à jour / ajoutés / supprimés
- Les attributs ne sont pas fréquemment mis à jour / ajoutés / supprimés
- Le tableau est le plus fréquemment interrogé pour lire / renvoyer des informations
- La mise en cache côté serveur est activée pour mettre en cache le résultat d'une requête / résultat donné
- Je prévois de commencer avec un seul type de produit et d'étendre / ajouter d'autres au fil du temps et j'aurai potentiellement plus de 10 types différents
la source
Réponses:
Personnellement, j'utiliserais un modèle similaire au suivant:
Le tableau des produits serait assez basique, les principaux détails de votre produit:
Deuxièmement, la table d'attributs pour stocker chacun des différents attributs.
Enfin, créez la table product_attribute en tant que table JOIN entre chaque produit et ses attributs qui lui sont associés.
Selon la façon dont vous souhaitez utiliser les données, vous examinez deux jointures:
Voir SQL Fiddle avec démo . Cela renvoie des données au format:
Mais si vous souhaitez renvoyer les données dans un
PIVOT
format où vous avez une ligne avec tous les attributs sous forme de colonnes, vous pouvez utiliser desCASE
instructions avec un agrégat:Voir SQL Fiddle avec démo . Les données sont retournées au format:
Comme vous le voyez, les données peuvent être dans un meilleur format pour vous, mais si vous avez un nombre inconnu d'attributs, cela deviendra facilement intenable en raison des noms d'attributs codés en dur, donc dans MySQL vous pouvez utiliser des instructions préparées pour créer des pivots dynamiques . Votre code serait le suivant (voir SQL Fiddle With Demo ):
Cela génère le même résultat que la deuxième version sans avoir besoin de coder en dur quoi que ce soit. Bien qu'il existe de nombreuses façons de modéliser cela, je pense que cette conception de base de données est la plus flexible.
la source
Je développerais la réponse de Taryn et modifierais la table d'attributs pour avoir la colonne fk_attribute_type_id qui sera à la place de la colonne attribute_name et pointe vers une nouvelle table attribute_type.
Vous avez donc des types d'attributs structurés dans une table et vous pouvez les modifier à tout moment en un seul endroit.
À mon avis, il vaut mieux travailler avec le type de "numérotation" (table avec les types possibles) qu'avec le type enum (comme sa colonne dans l'attribut_name (et en plus de cela en fait son pas nom, son type d'attribut)).
la source