eCommerce Bestellungstabelle. Sparen Sie Preise oder verwenden Sie eine Audit- / Verlaufstabelle?


13

Ich entwerfe mein erstes E-Commerce-Schema. Ich habe ein wenig über das Thema gelesen und bin etwas verwirrt über die Beziehung zwischen a order_line_itemund aproduct

A productkann gekauft werden. Es hat verschiedene Details, aber das Wichtigste ist unit_price.

An order_line_itemhat einen Fremdschlüssel zu dem product_idgekauften, dem quantitygekauften und dem unit_pricezu dem Zeitpunkt, an dem der Kunde das Produkt gekauft hat.

Das meiste, was ich gelesen habe, besagt, dass das unit_priceOn order_line_itemexplizit hinzugefügt werden sollte (dh nicht durch das referenziert wird product_id). Sinnvoll, da das Geschäft den Preis in Zukunft ändern könnte, was Bestellberichte, Nachverfolgung, Integrität usw. durcheinander bringen würde.

Was ich nicht verstehe, ist, warum ich den unit_priceWert direkt in das speichere order_line_item.

Wäre es nicht besser, eine Audit- / Verlaufstabelle zu erstellen, die die unit_priceÄnderung von a dokumentiert product?

Beim Erstellen von order_line_itemwird der Fremdschlüssel der product_auditTabelle hinzugefügt, und der Preis kann von dort (als Referenz) abgerufen werden.

Es scheint mir, dass die Verwendung dieses Ansatzes eine Menge Vorteile mit sich bringt (weniger Duplizierung von Daten, Verlauf von Preisänderungen usw.). Warum wird er also nicht häufiger verwendet? Ich habe noch kein Beispiel für ein E-Commerce-Schema gefunden, das diesen Ansatz verwendet. Fehlt mir etwas?

UDPATE: Meine Frage scheint sich auf die sich langsam ändernde Dimension zu beziehen . Ich bin jedoch immer noch verwirrt, da sich Slowly Changing Dimension auf Data Warehouse und OLAPs bezieht. Können langsam wechselnde Dimensionstypen auf meine OLTP-Datenbank (Main Business Transaction Process Database) angewendet werden? Ich frage mich, ob ich viele Konzepte vermische. Würde mich sehr über eine Anleitung freuen.


Eigentlich würde ich beides tun: den Verkaufspreis in der order_line speichern und eine Historie der Produktpreise speichern. Weil beide unterschiedlichen Zwecken dienen. Das Speichern des Verkaufspreises erleichtert und beschleunigt die Abfrage von Bestellungen erheblich . Andererseits möchten Sie möglicherweise alte Preise auch für nicht verkaufte Produkte abrufen.
a_horse_with_no_name

Sicherlich kann die Vereinfachung von Bestellanfragen nicht der einzige Treiber sein, der den Endpreis jedes Mal spart. Immerhin ist es eine relationale Datenbank - Abfragen wären nicht viel schwieriger.
Gaz_Edge

3
Das Speichern des Verkaufspreises in der Bestellposition ist nicht "nicht relational" (und ich betrachte es als normalisiert, da die Verkaufspreise meiner Meinung nach ein direktes Attribut der Bestellposition sind). Bei der Verwendung einer relationalen Datenbank geht es darum, die Grenzen zu kennen. Wenn Sie einen Shop mit 100 Produkten und 5 Bestellungen pro Tag betreiben, ist das Speichern und Abrufen alter Preise kein Problem. Wenn Sie einen Marktplatz mit Millionen von Produkten, Tausenden von Händlern und Hunderten von Bestellungen pro Minute betreiben, wird es ein Problem sein , diese Historie abzufragen .
a_horse_with_no_name

Wo würdest du die Geschichtspreise aufbewahren? Nach dem, was ich lese, brauche ich zwei Datenbanken. Eine für OLTP eine für OLAP. Die Geschichte geht in den OLAP?
Gaz_Edge

Antworten:


10

Wie Sie festgestellt haben, erleichtert das Speichern des Preises in der Bestellung die technische Implementierung. Es gibt jedoch eine Reihe von geschäftlichen Gründen, warum dies von Vorteil sein kann.

Zusätzlich zu Web-Transaktionen unterstützen viele Unternehmen den Vertrieb über andere Kanäle, z.

  • Über das Telefon
  • Handelsvertreter "on the road"
  • An einem physischen Ort (zB Geschäft, Büro)

In diesen Fällen kann die Bestellung zu einem bestimmten Zeitpunkt nach dem Abschluss der Transaktion in das System eingegeben werden. Unter diesen Umständen kann es schwierig bis unmöglich sein, den zu verwendenden historischen Preisdatensatz korrekt zu identifizieren. Die Speicherung des Stückpreises direkt auf der Bestellung ist die einzig mögliche Option.

Mehrere Kanäle bringen oft eine andere Herausforderung mit sich - unterschiedliche Preise für dasselbe Produkt. Zuschläge für telefonische Bestellungen sind üblich - und einige Kunden können sich selbst einen Rabatt aushandeln. Möglicherweise können Sie alle möglichen Preise für alle Kanäle in Ihrem Produktschema darstellen, die Einbeziehung in Ihre Auftragstabellen kann jedoch (sehr) komplex werden.

Überall dort, wo diese Verhandlung erlaubt ist, wird es sehr schwierig, den Preisverlauf mit dem vereinbarten Auftragspreis zu verknüpfen (es sei denn, die Vermittler haben sehr enge Verhandlungsgrenzen). Sie müssen den Preis auf der Bestellung selbst speichern.

Auch wenn Sie nur Web-Transaktionen unterstützen und eine relativ einfache Preisstruktur haben, bleibt ein interessantes Problem zu überwinden - wie sollten Preiserhöhungen bei Flugtransaktionen behandelt werden? Besteht das Unternehmen darauf, dass der Kunde Erhöhungen zahlen muss, oder wird der ursprüngliche Preis eingehalten (als das Produkt in den Warenkorb gelegt wurde)? Wenn es das letztere ist, ist die technische Implementierung kompliziert - Sie müssen einen Weg finden, um sicherzustellen, dass Sie die Preisversion in der Sitzung korrekt pflegen.

Schließlich beginnen viele Unternehmen, hochdynamische Preise zu verwenden. Möglicherweise gibt es keinen festen Preis für ein bestimmtes Produkt - er wird zur Laufzeit immer auf der Grundlage von Faktoren wie Tageszeit, Nachfrage nach dem Produkt usw. berechnet. In diesen Fällen darf der Preis erst gar nicht gegen das Produkt gespeichert werden!


3

Ich werde einige praktische Punkte hinzufügen, die ich gesehen habe.

  1. Produkte sind vergänglich.

    Was sie heute bedeuten, ist vielleicht nicht dasselbe wie vor einem Jahr. Derselbe SKU-Code (und daher die product_id) kann sich auf verschiedene Varianten / Arten des Produkts in verschiedenen Phasen beziehen.

    Nicht jeder versteht alle anstehenden Bedenken; Daher kann ein Benutzer die Attribute des Originalprodukts ändern, anstatt aus eigener Unwissenheit ein neues zu erstellen. Häufig kann dies passieren, weil ein Benutzer einen Plan hat (Hey! Ich kann nur 100 Skus haben, also warum nicht die älteren ändern, anstatt den Plan zu aktualisieren). Also, sehen Sie, in vielen Karren , ein Produkt wird niemals für immer dasselbe bedeuten.

  2. Abweichende Preise je nach Bestell- und Versandbedingungen

    Wie der Benutzer @Chris bereits erwähnt hat, können in verschiedenen Szenarien unterschiedliche Preise gelten.

    In den meisten Warenkörben werden mindestens 3 verschiedene Felder gespeichert - der Stückpreis, der Rabattbetrag und der Rabattpreis. In fortgeschritteneren Versionen finden Sie 2 weitere - Stückpreis mit Steuern, reduzierter Preis mit Steuern. Möglicherweise finden Sie in einigen weiteren Feldern eine Beschreibung der Versandart- und zusätzlichen Zahlungsartgebühren. Die Steuersätze können je nach Bundesstaat, Produkt, Land, Versandart usw. variieren, ebenso wie die anderen Kosten. In ähnlicher Weise können die Rabatte je nach Region, Werbeaktionen, Verkaufszeit und so weiter variieren. Daher gibt es Informationen, die nur auf Auftragsebene abgerufen werden können, und diese kombinierten Informationen können nicht allein aus Daten in der Produkttabelle generiert werden.

  3. Trennung von Bedenken

    Viele Carts sind so implementiert, dass verschiedene Teams die Kontrolle über verschiedene Teile von Daten haben können. Jemand, der das Bestellsystem verwaltet, muss nicht immer wissen, welche Produkte auf Lager sind, welche Preise zu unterschiedlichen Zeitpunkten verfügbar waren, welche Alternativen für einen bestimmten Artikel verfügbar sind und so weiter. Wenn produktbezogene Daten zusammen mit den Bestelldaten aufbewahrt werden, wird eine Trennung der Bedenken erreicht. Dies kann auch in Entwicklungsphasen der Fall sein, wenn verschiedene Teams verschiedene Teile des Systems verwalten.

  4. Einfachere Skalierbarkeit über mehrere Systeme hinweg

    Häufig werden das Auftragsverwaltungssystem, die Regel-Engine, die Katalog-Engine und das Inhaltsverwaltungssystem als separate Systeme erstellt / verwaltet. Dies hilft bei der Optimierung für verschiedene Lastzustände und bei der Erzeugung spezieller Informationen für jedes System. Ein System kann daher nicht als Lösegeld eingestuft werden, da Informationen von einem anderen System nicht verfügbar sind.

  5. Schnellere Entwicklung und Laufzeit

    Ich habe hier den Begriff "Entwicklungszeit" verwendet, obwohl die Verwendung von "Debugging-Zeit" geeigneter wäre. Immer wenn eine neue Entwicklung stattfindet, ist es schneller, wenn die benötigten Daten verfügbar sind, ohne dass die eigene Komplexität erhöht wird, da dann vergleichsweise kürzere Debug-Zyklen auftreten.

    Stellen Sie sich vor, Sie wurden gebeten, On-Demand-Berichte für Rabatte zu erstellen, die für einen bestimmten Monat vor einem halben Jahr täglich angeboten werden. Wenn Sie den ursprünglichen Preis haben, den ermäßigten Preis in 1-2 Tabellen zusammen mit der Bestellung, Bestellartikeldetails, ist dies so ziemlich unkompliziert. Wenn Sie jedoch Preise von einem anderen Tisch und dann die entsprechenden Rabatte von einem anderen Tisch abrufen und dann die Details herausfinden müssen, sind sowohl die Entwicklung als auch die Laufzeit höher.

Ein gutes Design sollte versuchen, sowohl für die Zukunft als auch für die Gegenwart zu optimieren.


2

Es kostet möglicherweise mehr Speicherplatz, aber ich ziehe es vor, alle relevanten Details des Verkaufs mit der Transaktion selbst zu verknüpfen, sodass die Details der Transaktion überschrieben werden, wenn aus irgendeinem Grund unser Prüfpfad unterbrochen wird oder ein Administrator die vorhandenen Sicherheitsvorkehrungen außer Kraft setzt Verkauf wie: verwendete Währung, Stückpreis, Menge, angewendete Steuern und welcher Wert sie erreichten, usw. sind alle verfügbar. Ich speichere das im Allgemeinen als XML, damit es von Verkauf zu Verkauf flexibel sein kann.


BEARBEITEN: Um zu erweitern, was ich oben kurz gesagt habe, in meinem nachfolgenden Kommentar und was @a_horse_with_no_name oben angesprochen hat, ist Redundanz in Transaktionsdaten nicht nur wichtig, sondern auch in der Größenordnung notwendig.

Ich gehe davon aus, dass Sie mit OOP aufbauen und daher wahrscheinlich ein Transaktionsobjekt und entweder ein alles umfassendes Produktobjekt und / oder ein Preisobjekt haben sollten. Nach meiner persönlichen Erfahrung bevorzuge ich es, in meiner Geschichte ausführlich zu sein, die Aufbewahrung ist relativ billig.

Wir haben ein Objektprotokoll erstellt, das Sie mithilfe Ihres vorhandenen RDBMS oder einer Variante des NOSQL-Schlüsselwertspeichers (oder besser eines RDBMS, das NoSQL-ähnliche Verbindungen wie Handlersocket oder Memcache ermöglicht) vereinfachen können, und wir speichern das Objektprotokoll Auf diese Weise sind alle Details und Preisänderungen an einem Ort einfach und schnell verfügbar. Wenn Sie es ernst meinen, können Sie sogar DIFFs verwenden, um Speicherplatz zu sparen und die Änderungen nur vorwärts zu speichern, obwohl es seine eigenen Einschränkungen hat. Das sollte sich um Ihren Verlauf kümmern, und der Vorteil serialisierter Objekte besteht darin, dass Ihr System in der Lage sein wird / sollte, sie als die Objekte wiederherzustellen, in denen sie gespeichert wurden. Das kümmert sich um die Geschichte.

In Bezug auf meinen Vorschlag bedeutet das Speichern der Transaktionsdetails wie Steuern, Währung usw. mit der Transaktion selbst, dass Sie nicht anderswo nach diesen Details suchen müssen. Ihr Transaktionsobjekt kennt seine Eigenschaften und Ihre Ansichten können sich um die Präsentation kümmern die variierenden Daten, wie Sie es für richtig halten. Sie erhalten schnellen Zugriff auf den Schnappschuss und profitieren zusätzlich von redundanten und überprüfbaren Aufzeichnungen.

Es lohnt sich, vertrau mir!


Das macht nicht wirklich Sinn zu sagen, dass Sie es nicht verwenden, weil es gelöscht werden könnte. Sie können alle Daten überschreiben. Jede Strategie sollte Backups behalten
Gaz_Edge

@Gaz_Edge Sie schließen sich nicht gegenseitig aus, Sie können trotzdem einen Audit-Trail verwenden und die Details mit Transaktionen speichern, die Redundanz ist in diesem Fall gerechtfertigt. Überlassen Sie sich niemals mit Daten, die dieses wichtige Thema betreffen, einer einzigen Fehlerquelle. In unserem Fall verwende ich einen zentralen Objektspeicher als Verlaufsmechanismus, anstatt zu versuchen, einen Verlauf pro Objekttyp (in diesem Fall wie eine Transaktion oder ein Produkt) zu erstellen. Ich werde sowohl das gesamte Transaktionsobjekt in der Geschichte haben, als auch alle wichtigen Details mit dem Datensatz selbst. Das ist ganz abgesehen von den Produktobjekten in der Geschichte.
oucil

und was ist, wenn ich Berichte über Bestellungen erstellen möchte? Wie viele Artikel x wurden gekauft? Oder wie viele Bestellungen über y Menge? Speichern als XML bedeutet, dass Sie die Möglichkeit verlieren, die Daten abzufragen, sofern ich mich nicht irre
Gaz_Edge

@Gaz_Edge Nicht wahr, wenn Sie von MySQL sprechen, können Sie SELECT ExtractValue(field_name, '/x/path/');nach Dingen wie, allen Transaktionen in einer bestimmten Währung oder allen Transaktionen mit einem bestimmten Mindeststeuerwert oder was auch immer filtern. Berichte in größerem Maßstab können aus der Objekthistorie erstellt werden. Für Berichte in größerem Maßstab können Sie einen elasticsearchServer / eine Instanz einrichten, der / die Berichte im BigData-Stil erstellt und sich problemlos in mehrere Millionen Dokumente + skalieren lässt.
oucil

@Gaz_Edge Ich sollte auch erwähnen, dass die Berichte, von denen Sie sprechen (Käufe über Wert, Verkäufe von Produkten usw.), allgemeine Berichte sind und dass in diesen Berichten Werte als Spaltenwerte mit dem Transaktionsdatensatz gespeichert werden sollten, um die Verarbeitung zu beschleunigen. Alles, worüber wichtig ist, aber nicht unbedingt berichtet wird, kann in das XML eingehen. Die Snapshot-Daten sind eigentlich nur für zwei Dinge bestimmt: 1. das Problem der sich langsam ändernden Dimension und 2. Validierung und Vergleich, wenn sich ein Kunde beschwert und Sie schnell sehen müssen, wer Recht hat. Es ist nicht für den täglichen Gebrauch.
oucil

1

Meine Stimme wäre, den Stückpreis in Ihrer Werbebuchung zu speichern und den Preisverlauf Ihrer Produkte in einer separaten Tabelle zu verfolgen. Meine Rechtfertigung dafür ist zusätzliche Flexibilität.

Auch wenn Ihre Preisstruktur starr und klar definiert ist und die oben erwähnten Variationen von @Chris Saxon nicht zulässt, können Sie sich sicher sein, dass dies immer so sein wird? Auch wenn Sie zuversichtlich sind, warum malen Sie sich in einer Ecke? Ich halte es für eine gute Idee, diese Informationen in der Werbebuchung zu speichern, da mir kein zwingender Grund einfällt, sie getrennt zu halten.

Was das Speichern Ihres Preisverlaufs angeht, so ist es durchaus sinnvoll, diesen separat zu speichern, da sich der Preis eines Artikels ändern kann und niemand ihn gekauft hat. Das wäre auf jeden Fall eine nützliche Information, wenn eine Preisänderung unwirksam wäre. Wie Sie bereits erwähnt haben, handelt es sich hierbei um einen klassischen Anwendungsfall einer sich langsam ändernden Dimension des Typs 2 in einem Data-Warehouse-Szenario. In der Regel wird jede Preisänderung in Ihrer Produkttabelle erfasst, und der Dimensionstabelle wird eine neue Zeile mit dem aktualisierten Preis und einem Zeitstempel hinzugefügt, der angibt, wann diese Änderung stattgefunden hat. In der vorherigen Zeile wird das Enddatum aktualisiert, um anzuzeigen, dass es sich nicht mehr um den effektiven Preis handelt. Ein Ansatz wäre also, diese Art von Änderung in einem Data Warehouse zu verfolgen.

Wenn Sie sich jedoch nicht gleichzeitig mit dem Entwerfen Ihrer OLTP-E-Commerce-Datenbank mit dem Entwerfen eines Data-Warehouse-Schemas und eines ETL-Prozesses befassen möchten, kann dieser Verlauf durchaus in unserer E-Commerce-Datenbank erfasst werden. Dies könnte geschehen, wie Sie es beschrieben haben, indem Sie eine separate product_audit-Tabelle erstellen, die von der product-Tabelle abhängt und Start- und Enddaten für die Gültigkeit dieser Version eines Produkts enthält. Sie können dies auch in der Produkttabelle selbst tun, indem Sie der Tabelle Start- und Enddaten hinzufügen, um anzugeben, welches Produkt gerade aktiv ist. Abhängig von der Anzahl der Produkte und der Anzahl oder Preisänderungen, die Ihr Unternehmen durchläuft, kann dies jedoch dazu führen, dass Ihre Produkttabelle viel größer als beabsichtigt ist und später Probleme mit der Abfrageleistung auftreten.

Wenn Sie Ihren Preisverlauf vom tatsächlichen Stückpreis auf der Werbebuchung trennen, können Sie auf jeden Fall andere Analysemöglichkeiten nutzen, um festzustellen, wann ein Produkt zu einem Preis verkauft wurde, der zu diesem Zeitpunkt über oder unter dem angegebenen Preis lag.


Danke für die Antwort. Ich denke, die Strategie, die ich verfolgen werde, ist, das OLTP nach dem Buch zu entwerfen. Eigentlich mag ich die Idee, ein Data Warehouse für die Berichterstellung zu haben. Obwohl es einige zusätzliche Arbeit (das Erstellen eines neuen Schemas) hinzufügt, kann das Schema auf OLAP zugeschnitten werden. Sozusagen ein Szenario vermeiden, in dem ich versuche, einen quadratischen Stift in ein rundes Loch zu stecken.
Gaz_Edge

@ Gaz_Edge: Ich bin in einer ähnlichen Situation, was die Entscheidung für mein neues Projekt angeht. Können Sie uns bitte mitteilen, welchen Designansatz Sie vor einem Jahr gewählt haben? Hat es gut geklappt
Coder Absolute

@CoderAbsolute Meine ursprüngliche Lösung war sehr datenbankorientiert. Meine Anwendung konzentriert sich jetzt auf eine serviceorientierte Architektur. Ich habe jetzt viele kleine entkoppelte Datenbankschemata anstatt eines eng gekoppelten. Die Notwendigkeit, 3N in einem massiven Schema zu verwenden, ist jetzt weg. Also addiere ich jetzt einfach den Stückpreis direkt zur Bestellung. Die Beibehaltung historischer Produktpreisänderungen ist jetzt verschwunden, da dies keine Geschäftsanforderung war. Ich hoffe, das hilft.
Gaz_Edge

0

Ich stimme voll und ganz der Grundidee zu, auftragsbezogene Informationen (Kontext) zusammenzuhalten. Nur eine kleine Randnotiz, dass eine solche Situation nur dann auftritt, wenn Sie Ihre Anwendung sehr datenbankorientiert gestalten und sich alles um die große fette Datenbank dreht. Wenn Sie Ihre Sichtweise ändern, indem Sie die Problemdomäne aus einem anderen Blickwinkel betrachten, werden Sie deutlich feststellen, dass die Reihenfolge eine Momentaufnahme eines ganz besonderen Ereignisses im Lebenszyklus Ihrer Anwendung ist. Wenn Sie Probleme basierend auf dem Kontext behandeln, werden Datenbankprobleme zweitrangig und die Komplexität, vor der jeder Angst hat, Fragen zu stellen und Berichte zu erstellen, wird nahtlos im Domänenmodell behandelt.


Einige kleine Beispiele würden Ihre Antwort viel einfacher verständlich machen. Insbesondere Sätze wie "Bestellung ist eine Momentaufnahme eines ganz besonderen Ereignisses im Lebenszyklus Ihrer Anwendung".
Dezso
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.