Je conçois mon premier schéma de commerce électronique. Je lis sur le sujet depuis un petit moment et je suis un peu confus quant à la relation entre un order_line_item
et unproduct
Un product
peut être acheté. Il contient divers détails, mais le plus important est unit_price
.
An order_line_item
possède une clé étrangère pour l' product_id
achat, l' quantity
achat et unit_price
le moment où le client a acheté le produit.
La plupart de ce que j'ai lu dit que le unit_price
sur le order_line_item
devrait être explicitement ajouté (c'est-à-dire non référencé par le product_id
). Cela a du sens, car le magasin pourrait changer le prix à l'avenir, ce qui gâcherait les rapports de commande, le suivi, l'intégrité, etc.
La chose que je ne comprends pas, c'est pourquoi enregistrer directement la unit_price
valeur dans le order_line_item
?
Ne serait-il pas préférable de créer un tableau d'audit / historique qui documente le unit_price
changement d'un product
?
Lorsqu'un order_line_item
est créé, la clé étrangère de la product_audit
table est ajoutée et le prix peut être récupéré (par référence) à partir de là.
Il me semble qu'il y a beaucoup de points positifs à utiliser cette approche (moins de duplication des données, historique des changements de prix, etc.), alors pourquoi n'est-elle pas plus fréquemment utilisée? Je n'ai pas rencontré d'exemple de schéma de commerce électronique utilisant cette approche, ai-je raté quelque chose?
UDPATE: Il semble que ma question concerne la dimension à évolution lente . Je suis toujours confus, car la dimension à évolution lente concerne l'entrepôt de données et les OLAP. Les types de dimension à évolution lente peuvent-ils donc être appliqués à ma base de données de processus de transaction commerciale principale (OLTP)? Je me demande si je mélange beaucoup de concepts, j'apprécierais grandement quelques conseils.
la source
Réponses:
Comme vous l'avez identifié, le stockage du prix sur la commande facilite la mise en œuvre technique. Il existe un certain nombre de raisons commerciales pour lesquelles cela peut être bénéfique.
En plus des transactions Web, de nombreuses entreprises soutiennent les ventes via d'autres canaux, par exemple:
Dans ces cas, la commande peut être entrée dans le système à un moment donné après la transaction. Dans ces circonstances, il peut être difficile, voire impossible, d'identifier correctement le relevé de prix historique à utiliser - le stockage du prix unitaire directement sur la commande est la seule option possible.
Plusieurs canaux posent souvent un autre défi - des prix différents pour le même produit. Les suppléments pour les commandes téléphoniques sont courants - et certains clients peuvent négocier eux-mêmes une remise. Vous pouvez être en mesure de représenter tous les prix possibles pour tous les canaux dans votre schéma de produit, mais l'incorporer dans vos tableaux de commande peut devenir (très) complexe.
Partout où cette négociation est autorisée, il devient très difficile de lier l'historique des prix au prix de la commande convenu (sauf si les agents ont des limites de négociation très étroites). Vous devez enregistrer le prix sur la commande elle-même.
Même si vous ne prenez en charge que les transactions Web et que vous avez une structure de prix relativement simple, il reste un problème intéressant à résoudre - comment gérer les augmentations de prix pour les transactions en vol? L'entreprise insiste-t-elle pour que le client paie des augmentations ou respecte-t-il le prix d'origine (lorsque le produit a été ajouté au panier)? Si c'est ce dernier, l'implémentation technique est compliquée - vous devez trouver un moyen de vous assurer que vous maintenez correctement la version des prix dans la session.
Enfin, de nombreuses entreprises commencent à utiliser une tarification très dynamique. Il peut ne pas y avoir un prix fixe pour un produit donné - il est toujours calculé au moment de l'exécution en fonction de facteurs tels que l'heure de la journée, la demande pour le produit, etc. Dans ces cas, le prix ne peut pas être stocké contre le produit en premier lieu!
la source
J'ajouterai quelques points pratiques que j'ai vus.
Les produits sont transitoires.
Ce qu'ils peuvent signifier aujourd'hui peut ne pas être le même que ce qu'ils signifiaient il y a un an. Le même code sku (et donc le product_id) peut faire référence à différentes variantes / types de produits à différentes étapes.
Tout le monde ne comprend pas toutes les préoccupations; par conséquent, un utilisateur peut modifier les attributs du produit d'origine au lieu d'en créer un nouveau à partir de sa propre ignorance. Souvent, cela peut se produire en raison du plan d'un utilisateur (Hé! Je ne peux avoir que 100 sku, alors pourquoi ne pas continuer à changer les plus anciens au lieu de mettre à jour le plan) Donc, vous voyez, dans beaucoup de chariots , un produit ne signifiera jamais la même chose pour toujours.
Différents prix en fonction des conditions de commande et d'expédition
Comme l'utilisateur @Chris l'a mentionné, différents prix peuvent être applicables dans différents scénarios.
Dans la plupart des chariots, vous trouverez au moins 3 champs différents stockés - le prix unitaire, le montant de la remise et le prix réduit. Dans les plus avancés, vous en trouverez 2 de plus - prix unitaire avec taxe, prix réduit avec taxe. Vous pouvez trouver quelques champs supplémentaires pour décrire les frais de méthode d'expédition et les frais de méthode de paiement supplémentaires. Les pourcentages de taxe peuvent varier en fonction de l'État, du produit, du pays, de la méthode d'expédition, etc., tout comme les autres coûts. De même, les remises peuvent varier en fonction de la géographie, des promotions, du moment de la vente, etc. Par conséquent, il existe des informations qui peuvent être obtenues uniquement au niveau de la commande, et ces informations combinées ne peuvent pas être générées uniquement à partir des données du tableau des produits.
Séparation des préoccupations
Un grand nombre de charrettes sont implémentées de manière à ce que différentes équipes puissent avoir le contrôle sur différentes parties des données. Quelqu'un qui gère le système de commande n'a pas toujours besoin de savoir quels sont tous les produits en stock, quels étaient les prix à différents moments, quelles sont les alternatives pour une référence donnée, etc. La conservation des données relatives au produit avec les données de la commande permet de séparer les préoccupations. Cela pourrait également être vrai aux stades de développement, si différentes équipes gèrent différentes parties du système.
Évolutivité facilitée sur plusieurs systèmes
La plupart du temps, le système de gestion des commandes, le moteur de règles, le moteur de catalogue et le système de gestion de contenu sont tous construits / maintenus comme des systèmes distincts. Cela permet d'optimiser pour diverses conditions de charge et de générer une intelligence spécialisée pour chacun des systèmes. Un système ne peut donc pas être tenu pour rançon en raison de l'indisponibilité des informations d'un autre système.
Développement et temps d'exécution plus rapides
J'ai utilisé le terme «temps de développement» ici, bien que l'utilisation de «temps de débogage» serait plus appropriée. Chaque fois qu'un nouveau développement se produit, il sera plus rapide si les données nécessaires sont disponibles sans ajouter de complexité propre, car alors, il y aura des cycles de débogage comparativement plus petits.
Imaginez que l'on vous demande de générer des rapports à la demande pour les remises offertes au jour le jour pour un mois donné il y a six mois. Si vous avez le prix d'origine, le prix réduit dans 1-2 tableaux avec la commande, les détails de l'article, c'est assez simple. Si toutefois, vous devez aller chercher les prix dans une autre table, puis les remises applicables dans une autre table, puis comprendre les détails, le développement et le temps d'exécution seront plus élevés.
Un bon design doit essayer d'optimiser autant pour l'avenir que pour le présent.
la source
Cela peut finir par coûter plus cher en stockage, mais je préfère héberger tous les détails pertinents de la vente avec la transaction elle-même, de sorte que si, pour une raison quelconque, notre piste d'audit est rompue, ou si un administrateur remplace les sécurités en place, les détails de la vente comme: devise utilisée, prix unitaire, quantité, taxes appliquées et valeur, etc. sont disponibles. Je stocke généralement cela en XML afin qu'il puisse être flexible de vente en vente.
EDIT: Pour développer ce que je disais brièvement ci-dessus, dans mon commentaire de suivi ci-dessous, et ce que @a_horse_with_no_name a évoqué ci-dessus, la redondance dans les données de transaction est non seulement importante, mais elle est également nécessaire à grande échelle.
Je suppose que vous construisez en utilisant la POO et vous devriez donc probablement avoir un objet de transaction et soit un objet produit englobant tout le monde et / ou un objet prix. Dans ma propre expérience personnelle, je préfère être verbeux dans mon histoire, le stockage est relativement bon marché.
Ce que nous avons fait est de créer un historique des objets que vous pouvez faciliter en utilisant un SGBDR existant ou une sorte de magasin de valeurs clés NOSQL (ou encore mieux un SGBDR qui permet des connexions de type NoSQL comme handlersocket ou memcache), et nous stockons l'historique des objets de cette façon, avec chaque détail et changement de prix en un seul endroit facilement et rapidement disponibles. Si vous êtes sérieux, vous pouvez même utiliser les DIFF pour économiser sur le stockage et ne stocker que les modifications, bien qu'il ait ses propres mises en garde. Cela devrait prendre soin de votre historique, et l'avantage des objets sérialisés est que votre système pourra / devrait être capable de les récupérer comme les objets qu'ils ont été stockés. Cela prend soin de l'histoire.
En ce qui concerne ma suggestion, le stockage des détails de la transaction tels que les taxes, les devises, etc. avec la transaction elle-même signifie qu'il n'est pas nécessaire de chercher ailleurs pour ces détails, votre objet de transaction sera au courant de ses propriétés et vos vues peuvent se charger de présenter les données variables comme bon vous semble. Vous bénéficiez d'un accès rapide à l'instantané et bénéficiez en outre d'enregistrements redondants et vérifiables.
Ça vaut le coup, croyez-moi!
la source
SELECT ExtractValue(field_name, '/x/path/');
pour filtrer des choses comme, toutes les transactions dans une devise spécifique, ou toutes les transactions avec une certaine valeur fiscale minimale, ou autre. Des rapports à plus grande échelle peuvent être créés à partir de l'historique des objets. Pour les rapports à plus grande échelle, vous pouvez configurer unelasticsearch
serveur / une instance qui a des rapports de style BigData et qui évolue facilement dans les millions de documents +.Mon vote serait de stocker le prix unitaire sur votre élément de campagne et de suivre l'historique des prix de vos produits dans un tableau séparé. Je justifie cela par une flexibilité accrue.
Même si votre structure de prix est rigide et bien définie et ne permet pas les variations mentionnées par @Chris Saxon ci-dessus, êtes-vous à l'aise que ce sera toujours le cas? Même si vous êtes confiant, pourquoi vous peindre dans un coin? Je pense que ce serait une bonne idée de le stocker sur le détail de l'élément de campagne, car je ne vois pas de raison impérieuse de le séparer.
Quant au stockage de votre historique de prix, il y a une valeur certaine à le stocker séparément car il pourrait y avoir des changements dans le prix d'un article et personne ne l'a acheté. Ce serait certainement une information utile pour savoir si un changement de prix était inefficace. Comme vous l'avez mentionné, il s'agit d'un cas d'utilisation classique d'une dimension à évolution lente de type 2 dans un scénario d'entrepôt de données. En règle générale, tout changement de prix dans votre tableau de produits est capturé et une nouvelle ligne est ajoutée au tableau de dimensions avec le prix mis à jour et un horodatage pour indiquer quand ce changement a eu lieu. La ligne précédente aurait sa date de fin mise à jour pour indiquer que ce n'est plus le prix effectif. Une approche consisterait donc à suivre ce type de changement dans un entrepôt de données.
Cependant, si vous ne voulez pas vous soucier de la conception d'un schéma d'entrepôt de données et d'un processus ETL en même temps que la conception de votre base de données de commerce électronique OLTP, cet historique pourrait certainement être capturé dans notre base de données de commerce électronique. Cela peut être fait comme vous l'avez décrit avec la création d'une table product_audit distincte qui se bloque de la table produit et contient les dates de début et de fin pour quand cette version d'un produit était en vigueur. Cela peut également être fait dans le tableau des produits lui-même en ajoutant des dates de début et de fin au tableau pour indiquer quel produit est actuellement actif. Cependant, en fonction du nombre de produits et du nombre ou des modifications de prix que votre entreprise subit, cela pourrait rendre votre tableau de produits beaucoup plus grand que prévu et pourrait entraîner des problèmes de performances des requêtes plus tard.
Enfin, séparer votre historique de prix du prix unitaire réel sur l'élément de campagne pourrait certainement donner d'autres opportunités analytiques pour voir quand un produit a été vendu à un prix supérieur ou inférieur au prix indiqué à l'époque.
la source
Je suis totalement d'accord avec l'idée principale de garder ensemble les informations relatives à l'ordre (contexte). Juste une note secondaire mineure qu'une telle situation ne se produira que lorsque vous concevez votre application très centrée sur la base de données et que tout tourne autour de la grosse base de données. Si vous changez de point de vue en examinant le domaine problématique sous un angle différent, vous observerez clairement que l'ordre est un instantané capturé d'un événement très spécial dans le cycle de vie de votre application. Lorsque vous gérez des problèmes en fonction du contexte, les problèmes de base de données deviennent secondaires et la complexité que tout le monde craint d'interroger et de créer des rapports sera gérée de manière transparente dans le modèle de domaine.
la source