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:
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 History
Tabellen
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 AddressHistory
und CustomerAddressHistory
helfen dabei, sicherzustellen, dass eine Bestellung nicht von Adressänderungen betroffen ist , da alle „vorherigen“ Zeilen in der jeweiligen History
Tabelle 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.CreatedDateTime
und AddressHistory.AuditedDateTime
steht für den gesamten Zeitraum, in dem eine bestimmte "vergangene" Address
Zeile als "gegenwärtig", "aktuell" oder "wirksam" eingestuft wurde. Ähnliche Überlegungen gelten für die CustomerAddressHistory
Zeilen.
Die CustomerAddress.IsActive
BIT-Spalte (Boolean) soll darauf hinweisen, ob eine bestimmte Address
Zeile von einer Customer
Zeile "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 Address
Tisch, 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 Address
Zeile abrufen möchten, AddressHistory
die MAX(AddressHistory.AuditedDateTime)
und die berücksichtigen, die AddressHistory.AddressId
dem jeweiligen Address.AddressId
Wert 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 .
MyOrder.ShippingAddressId
undMyOrder.BillingAddressId
muss aufCustomerAddress.AddressId
(und nicht aufAddress.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.History
Tabelle vorhanden ist, sollte dies für dieAddress
Tabelle 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 inHistory
dieAddress
Tabelle einfügen und dann eine neue Einfügung in die Tabelle vornehmen , oder?Address
Zeile, die bis zur Änderung „vorhanden“ war, in dieAddressHistory
Tabelle eingefügt wird und dass (b ) DieAddress
betreffende Zeile ist UPDATEd mit den neuen Werten. Es wäre vorteilhaft, diesen Prozess als einzelne Arbeitseinheit innerhalb einer Transaktion durchzuführen.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:
MDCCL gab hier auch einen Überblick darüber, wie Sie die aktuelle Adresse für einen Benutzer finden:
quelle