Speichern einer Best Practice für Rechnungsadressen in der Auftragstabelle

10

Kann mir jemand helfen, die Antwort dieses Benutzers für eine CustomerLocation- Tabelle zu verstehen ? Ich möchte wirklich eine gute Methode zum Speichern von Adressen in der Auftragstabelle.

Was ich suche, ist, wie ich meine Adressen einrichten kann, damit die Bestellung beim Bearbeiten nicht dadurch beeinflusst wird, dass ein Kunde seine Adresse aktualisiert oder umzieht.

So wie es aussieht, sieht mein Schema ähnlich aus wie:

 Person           |EntityID|
 EntityAddress    |EntityID|AddressID|
 Address          |AddressID|AddressType|AddressLine1|AddressLine2|
 Order            |OrderID|BillingAddressID|
Gemeinschaft
quelle

Antworten:

16

Konzeptionell gesehen sind Bestellung und Adresse in Ihrer Geschäftsumgebung zwar eng miteinander verbundene Ideen, es handelt sich jedoch tatsächlich um zwei separate Entitätstypen mit jeweils eigenen anwendbaren Eigenschaften (oder Attributen) und Einschränkungen.

Daher stimme ich, wie bereits in den Kommentaren erwähnt, @Erik zu , und Sie sollten das logische Layout Ihrer Datenbank unter folgenden Elementen organisieren:

  • ein diskreter Tisch zu halten , Adressinformationsstücke;
  • ein Tisch zu behalten Kunden -spezifische Details;
  • ein Tisch zu umschließen Bestelldatenpunkten; und
  • eine Tabelle mit Fakten zu den Zuordnungen zwischen Kunden und Adressen ;

wie ich weiter unten veranschaulichen werde.

Expository IDEF1X-Diagramm

Ein Bild sagt mehr als tausend Worte, deshalb habe ich das in Abbildung 1 gezeigte IDEF1X-Diagramm erstellt , um einige der Möglichkeiten zu veranschaulichen, die sich aus meinem Vorschlag ergeben:

Abbildung 1 - IDEF1X-Diagramm für das Expository für Kunden, Bestellungen und Adressen

Kunde , Adresse und deren Assoziationen

Wie gezeigt, habe ich eine Assoziation mit einem Viele-zu-Viele-Kardinalitätsverhältnis (M: N) zwischen den Entitätstypen Kunde a und Adresse dargestellt . Dieser Ansatz bietet künftige Flexibilität, da ein Kunde , wie Sie wissen, mehrere Adressen im Laufe der Zeit oder sogar gleichzeitig behalten kann und dieselbe Adresse von mehreren Kunden gemeinsam genutzt werden kann .

Eine bestimmte Adresse kann von mehreren Kunden (1: M) auf verschiedene Arten verwendet werden . Beispielsweise kann es als physisch definiert und / oder für den Versand und / oder die Abrechnung festgelegt werden . Möglicherweise kann dieselbe Adressinstanz gleichzeitig jedem der oben genannten Zwecke dienen, oder sie kann zwei Verwendungszwecke abdecken, während ein anderes Adressvorkommen den verbleibenden abdeckt.

a In einigen Geschäftsumgebungen kann ein Kunde entweder eine Person oder eine Organisation sein (Situation, die eine leicht unterschiedliche Anordnung implizieren würde, wie in dieser Antwort zu einer Supertyp-Subtyp-Struktur beschrieben), aber mit dem Ziel, ein vereinfachtes Beispiel bereitzustellen, habe ich mich entschieden diese Möglichkeit hier nicht einzuschließen. Falls Sie diese Situation in Ihrer Datenbank behandeln müssen, zeigt der Beitrag des vorherigen Links die Methode zur Lösung dieser Anforderung.

Reihenfolge , Adresse , Kundenadresse und Adressrollen

In der Regel sind für eine Bestellung nur zwei Arten von Adressen erforderlich , eine für den Versand und eine für die Abrechnung . Auf diese Weise kann dieselbe Adressinstanz beide Rollen für eine einzelne Bestellung ausfüllen , aber jede Rolle wird durch die jeweilige Eigenschaft dargestellt, dh ShippingAddressId oder BillingAddressId .

Die Bestellung wird mit der Adresse über den assoziativen Entitätstyp CustomerAddress mit Hilfe von zwei AUSLÄNDISCHEN SCHLÜSSELN mit mehreren Eigenschaften verbunden, d. H.

  • ( CustomerNumber , ShippingAddressId ) und ( CustomerNumber , BillingAddressId ),

beide zeigen auf den PRIMARY KEY mit mehreren Eigenschaften von CustomerAddress , der als angezeigt wird

  • ( Kundennummer , Adress-ID )

... was dazu beiträgt , eine Geschäftsregel zu vertreten , dass schreibt vor , dass (a) eine Bestell Instanz ausschließlich mit (b) verknüpft werden muß Adresse zuvor zugeordneten Ereignisse mit dem spezifischen Kunden , die das machte Auftrag , und nie mit (c) einem zufälligen nicht Kunden - verwandte Adresse .

Verlauf für (1) Adresse und für (2) die CustomerAddress- Zuordnung

Wenn Sie die Möglichkeit einer Änderung liefern wollen Adressinformationsstücke, dann müssen Sie den Überblick über alle Datenänderungen zu halten. Auf diese Weise habe ich Address als einen „überprüfbaren“ Entitätstyp dargestellt, der seine eigene AddressHistory verwaltet .

Da die Art einer Verbindung zwischen einem Kunden und einer Adresse auch eine oder mehrere Änderungen erfahren kann, habe ich auch die Möglichkeit dargestellt, eine solche Zuordnung aufgrund des Entitätstyps CustomerAddressHistory als „überprüfbar“ zu behandeln .

In dieser Hinsicht wurden verschiedene Faktoren behandelt, die in den Fragen und Antworten Nr. 1 und Q & A Nr. 2 , - sowohl über das Aktivieren zeitlicher Funktionen in einer Datenbank - sind wirklich relevant.

Illustratives logisches SQL-DDL-Layout

Infolgedessen habe ich in Bezug auf das oben gezeigte und erläuterte Diagramm die folgende Anordnung auf logischer Ebene deklariert (die Sie genau an Ihre Bedürfnisse anpassen können):

-- You should determine which are the most fitting 
-- data types and sizes for all your table columns 
-- depending on your business context characteristics.

-- Also, you should make accurate tests to define the 
-- most convenient INDEX strategies based on the exact 
-- data manipulation tendencies of your business domain.

-- As one would expect, you are free to utilize 
-- your preferred (or required) naming conventions. 

CREATE TABLE Customer (
    CustomerNumber      INT      NOT NULL,
    SpecificAttribute   CHAR(30) NOT NULL,
    ParticularAttribute CHAR(30) NOT NULL,  
    CreatedDateTime     DATETIME NOT NULL,
    -- 
    CONSTRAINT Customer_PK PRIMARY KEY (CustomerNumber)
);

CREATE TABLE Address (
    AddressId           INT      NOT NULL,
    SpecificAttribute   CHAR(30) NOT NULL,
    ParticularAttribute CHAR(30) NOT NULL,  
    CreatedDateTime     DATETIME NOT NULL,  
    -- 
    CONSTRAINT Address_PK PRIMARY KEY (AddressId)
);

CREATE TABLE CustomerAddress (
    CustomerNumber  INT      NOT NULL,  
    AddressId       INT      NOT NULL,
    IsPhysical      BIT      NOT NULL,
    IsShipping      BIT      NOT NULL,  
    IsBilling       BIT      NOT NULL,
    IsActive        BIT      NOT NULL,
    CreatedDateTime DATETIME NOT NULL,  
    -- 
    CONSTRAINT CustomerAddress_PK           PRIMARY KEY (CustomerNumber, AddressId),
    CONSTRAINT CustomerAddressToCustomer_FK FOREIGN KEY (CustomerNumber)
        REFERENCES Customer (CustomerNumber),
    CONSTRAINT CustomerAddressToAddress_FK  FOREIGN KEY (AddressId)
        REFERENCES Address  (AddressId)  
);

CREATE TABLE MyOrder (
    CustomerNumber      INT      NOT NULL,  
    OrderNumber         INT      NOT NULL,
    ShippingAddressId   INT      NOT NULL,
    BillingAddressId    INT      NOT NULL,    
    SpecificAttribute   CHAR(30) NOT NULL,
    ParticularAttribute CHAR(30) NOT NULL,  
    OrderDate           DATE     NOT NULL,
    CreatedDateTime     DATETIME NOT NULL,  
    -- 
    CONSTRAINT Order_PK                  PRIMARY KEY (CustomerNumber, OrderNumber),
    CONSTRAINT OrderToCustomer_FK        FOREIGN KEY (CustomerNumber)
        REFERENCES Customer        (CustomerNumber),
    CONSTRAINT OrderToShippingAddress_FK FOREIGN KEY (CustomerNumber, ShippingAddressId)
        REFERENCES CustomerAddress (CustomerNumber, AddressId),
    CONSTRAINT OrderToBillingAddress_FK  FOREIGN KEY (CustomerNumber, BillingAddressId)
        REFERENCES CustomerAddress (CustomerNumber, AddressId)          
);

CREATE TABLE AddressHistory (
    AddressId           INT      NOT NULL,
    AuditedDateTime     DATETIME NOT NULL,
    SpecificAttribute   CHAR(30) NOT NULL,
    ParticularAttribute CHAR(30) NOT NULL,  
    CreatedDateTime     DATETIME NOT NULL,  
    -- 
    CONSTRAINT AddressHistory_PK          PRIMARY KEY (AddressId, AuditedDateTime),
    CONSTRAINT AddressHistoryToAddress_FK FOREIGN KEY (AddressId)
        REFERENCES Address  (AddressId)    
);

CREATE TABLE CustomerAddressHistory (
    CustomerNumber  INT      NOT NULL,  
    AddressId       INT      NOT NULL,
    AuditedDateTime DATETIME NOT NULL,    
    IsPhysical      BIT      NOT NULL,
    IsShipping      BIT      NOT NULL,  
    IsBilling       BIT      NOT NULL,
    IsActive        BIT      NOT NULL,
    CreatedDateTime DATETIME NOT NULL,  
    -- 
    CONSTRAINT CustomerAddressHistory_PK                  PRIMARY KEY (CustomerNumber, AddressId, AuditedDateTime),
    CONSTRAINT CustomerAddressHistoryToCustomerAddress_FK FOREIGN KEY (CustomerNumber, AddressId)
        REFERENCES CustomerAddress (CustomerNumber, AddressId)
);

Wenn Sie einen Blick darauf werfen möchten, habe ich ihn in dieser Datenbank-Geige getestet , die unter SQL Server 2017 ausgeführt wird.

Die HistoryTabellen

Der folgende Auszug aus Ihrer Frage ist sehr wichtig:

Was ich suche, ist, wie ich meine Adressen einrichten kann, damit die Bestellung beim Bearbeiten nicht durch die Tatsache beeinflusst wird, dass ein Kunde seine Adresse aktualisiert oder umzieht.

Die Tabellen AddressHistoryund CustomerAddressHistoryhelfen dabei, sicherzustellen, dass eine Bestellung nicht von Adressänderungen betroffen ist , da alle „vorherigen“ Zeilen in der jeweiligen HistoryTabelle beibehalten werden sollten und bei Bedarf abgefragt werden können. UPDATE- und DELETE-Operationen für diese beiden Tabellen sollten verboten sein (der Versuch, den Verlauf zu ändern, kann sogar negative rechtliche Auswirkungen haben).

Das Intervall umfasst die in eingeschlossenen Werte AddressHistory.CreatedDateTimeund AddressHistory.AuditedDateTimesteht für den gesamten Zeitraum, in dem eine bestimmte "vergangene" AddressZeile als "gegenwärtig", "aktuell" oder "wirksam" eingestuft wurde. Ähnliche Überlegungen gelten für die CustomerAddressHistoryZeilen.

Die CustomerAddress.IsActiveBIT-Spalte (Boolean) soll darauf hinweisen, ob eine bestimmte AddressZeile von einer CustomerZeile "verwendet" werden kann oder nicht. Wenn es beispielsweise auf "falsch" gesetzt ist, wird die Tatsache angezeigt, dass ein Kunde diese Adresse nicht mehr verwendet und sie daher nicht für neue Bestellungen verwendet werden kann .


Hinweis : Auf der anderen Seite habe ich einige Systeme gesehen , in dem jedes Mal, wenn ein neuer Auftrag , die bewirkt wird , Adressinformationen eingegeben wird (einige Male wiederholt) und die Adresse (n) für Vergangenheit Aufträge werden nie gelöscht (daher Die Bestellungen sind von Adressänderungen nicht betroffen .

Diese Vorgehensweise kann entschieden mit erheblichen Redundanzvolumina verbunden sein. Es besteht jedoch die Möglichkeit, dass - abhängig von den genauen Informationsanforderungen Ihrer Geschäftsdomäne - dies funktioniert. Daher möchten Sie möglicherweise auch die Vor- und Nachteile bewerten.


Datenabruf

Die „present“, „aktuelle“ oder „wirksam“ Version einer Adresse Auftreten muss als Zeile in der enthält seinen AddressTisch, aber die Auswahl der vorherigen „Zustände“ eine Adresse aus der AddressHistory(oder von CustomerAddressHistory) Tabelle ist einfach und es kann Seien Sie eine interessante Übung, um Ihre SQL-Codierungsfähigkeiten zu verbessern.

In Bezug auf eine der Situationen, die Sie in den Kommentaren erwähnt haben, müssen Sie, wenn Sie die „vorletzte Version“ einer einzelnen AddressZeile abrufen möchten, AddressHistorydie MAX(AddressHistory.AuditedDateTime)und die berücksichtigen, die AddressHistory.AddressIddem jeweiligen Address.AddressIdWert entsprechen.

In dieser Hinsicht ist es - zumindest beim Erstellen einer relationalen Datenbank - sehr praktisch, zuerst das entsprechende konzeptionelle Schema (basierend auf den geltenden Geschäftsregeln ) zu definieren und anschließend die nachfolgende logische DDL-Anordnung zu deklarieren . Sobald Sie stabile und zuverlässige Versionen dieser grundlegenden Elemente erhalten haben (die sich natürlich im Laufe der Zeit weiterentwickeln können), ist es an der Zeit, die besten Manipulationsmöglichkeiten (über INSERT-, UPDATE-, DELETE- und SELECT-Operationen oder Kombinationen davon) zu analysieren und zu bestimmen zu Daten.

Unterstützung für Wahrnehmung, Ansichten und Anwendungsprogramme der Endbenutzer

Offensichtlich auf der äußeren Ebene der Abstraktion, Adresse wird Information (Endnutzer) wahrgenommen als Teil eines sein Auftrag , und es ist nichts einzuwenden, aber das bedeutet nicht , dass die Modellierer die wesentlichen Teile des entwerfen haben Datenbank in Frage so. Wenn in diesem Punkt beispielsweise eine „vollständige“ Bestellung gedruckt werden muss (sehr machbar), können Sie diese bei Bedarf mit Hilfe einiger JOIN-Operatoren und WHERE-Klauseln (unter Berücksichtigung der betreffenden Gültigkeitsdauer) „reproduzieren“ usw.) können in Ansichten für den zukünftigen Verbrauch festgelegt werden, indem die entsprechende Ergebnismenge an die zugehörigen Anwendungsprogramme gesendet wird, die wiederum die Formatierung nach Bedarf verbessern können.

Natürlich sind die Anwendungsprogramme auch sehr hilfreich, wenn ein Auftrag ausgeführt wird. Beispielsweise kann ein Desktop- / Mobile-App-Fenster oder eine Webseite:

  • nur die Adresse (n) anzeigen , die der betroffene Kunde als "verwendbar" (über CustomerAddress.IsActive) festgelegt hat;
  • Listen Sie alle Adressen auf , die der Kunde für den Abrechnungsservice aktiviert hat (via CustomerAddress.IsBilling). und
  • Gruppieren Sie alle Adressen , die der Kunde für den Versandservice definiert hat (via CustomerAddress.IsShipping).

Erleichterung auf diese Weise aller beteiligten Prozesse auf der GUI (dh der externen Abstraktionsebene eines Computersystems).


Vorgeschlagene Literatur

Sie haben (in jetzt entfernten Kommentaren) einige Hinweise zur Sounddatenbankliteratur angefordert. In Bezug auf theoretisches Material empfehle ich daher dringend, dass Sie alle Arbeiten von Dr. EF Codd lesen , einem Turing- Preisträger und natürlich dem alleinigen Urheber des relationalen Datenmodells (vielleicht jetzt relevanter als je zuvor). Diese Liste enthält einige seiner äußerst einflussreichen Artikel und Artikel.

Zwei wichtige Werke, die nicht in der oben genannten Liste enthalten sind, sind genau sein ACM Turing Award Lecture mit dem Titel Relational Database: Eine praktische Grundlage für Produktivität von 1981 und sein Buch The Relational Model for Database Management: Version 2 , das veröffentlicht wurde in 1990.

In Bezug auf das Konzept ist die integrierte Definition für Informationsmodellierung (IDEF1X) eine ernsthaft empfehlenswerte Technik, die im Dezember 1993 vom US-amerikanischen National Institute of Standards and Technology (NIST) als Standard definiert wurde .

MDCCL
quelle
1
Entschuldigung, ich weiß, dass der Beitrag älter ist, aber warum verweisen Sie in MyOrder auf (z. B. REFERENZEN Adresse (AddressId))? Warum nicht CustomerAddress?
Shadrix
1
Keine Sorge und schöner Fang: In der Tat beides MyOrder.ShippingAddressIdund MyOrder.BillingAddressIdmuss auf CustomerAddress.AddressId(und nicht auf Address.AddressId) verweisen ; Auf diese Weise wird sichergestellt, dass eine Bestellung ausschließlich mit den Adressen verknüpft werden kann, die zuvor mit dem Kunden verbunden waren , der diese Bestellung aufgegeben hat . Das Diagramm schlägt diese Anordnung vor, sodass die DDL genauer ist. Vielen Dank, dass Sie um diese Klarstellung gebeten haben.
MDCCL
2
@Shadrix Ich habe gerade den Beitrag bearbeitet, falls Sie einen Blick darauf werfen möchten.
MDCCL
@MDCCL Wenn Sie sagten, dass kein UPDATE und DELETE für die HistoryTabelle vorhanden ist, sollte dies für die AddressTabelle identisch sein ? Was ist, wenn der Kunde etwas bestellt und dann nur die Postleitzahl oder Stadt nur ein Feld ändert? Wir sollten die vorhandene Adresse in Historydie AddressTabelle einfügen und dann eine neue Einfügung in die Tabelle vornehmen , oder?
Mike Ross
1
OTOH, wenn ein Kunde eine oder mehrere Informationen zu einer bestimmten Adresse ändern möchte , muss er sicherstellen, dass (a) die entsprechende AddressZeile, die bis zur Änderung „vorhanden“ war, in die AddressHistoryTabelle eingefügt wird und dass (b ) Die Addressbetreffende Zeile ist UPDATEd mit den neuen Werten. Es wäre vorteilhaft, diesen Prozess als einzelne Arbeitseinheit innerhalb einer Transaktion durchzuführen.
MDCCL
3

Diese Antwort wurde aus Kommentaren zur Frage zusammengestellt.

Eine Lösung wäre die Verwendung eines FK für die Adresstabelle in der Auftragstabelle. Auf diese Weise können Sie die Adressen anzeigen, die für die Bestellung verwendet wurden, und die Adresse von der aktuellen Adresse des Benutzers entkoppeln.

Damit dies funktioniert, müssten Sie eine neue Adresse einfügen und diese neue Adresse mit der Benutzertabelle verknüpfen. Dies bedeutet, dass Adressen einmal geschrieben werden und die Bearbeitung für den Endbenutzer eine Illusion ist. Sie können den Verlauf aller Adressen, denen ein Benutzer zugeordnet war, effektiv speichern, indem Sie die Zuordnung von der Benutzertabelle in eine Zuordnungstabelle mit einem Zeitstempel verschieben. Dies würde Ihnen einen Verlauf von Änderungen / Adressen geben und unveränderliche Daten in der Adresstabelle beibehalten.

@MDCCL angegeben:

[Sie sollten] Ihre Datenbankstruktur mit einer Tabelle zum Speichern von auftragsbezogenen Daten und einer anderen Tabelle zum Speichern der Adressinformationen organisieren. Und ja, Sie können definitiv eine Tabelle haben, die die Viele-zu-Viele-Beziehung zwischen diesen beiden Entitätstypen darstellt. Wenn ein Benutzer seine Adressattribute ändern kann, müssen Sie solche Änderungen nachverfolgen, daher sollten Sie die entsprechenden aktivieren AddressHistory. Dieser Beitrag bezieht sich auf den letzteren Aspekt.

MDCCL gab hier auch einen Überblick darüber, wie Sie die aktuelle Adresse für einen Benutzer finden:

Um die neueste Version einer Verlaufstabelle abzurufen, müssen Sie MAX(AuditedDateTime)die entsprechende berücksichtigen AddressId. Der erste Schritt ist das Modellieren / Entwerfen Ihrer bestmöglichen konzeptionellen und logischen Anordnungen. Der zweite Schritt besteht darin, die richtigen Wege zu finden, um Ihre Daten einzufügen, zu aktualisieren, zu löschen und auszuwählen.

Erik
quelle