Concettualmente parlando, sebbene nel tuo ambiente aziendale Ordine e Indirizzo siano idee strettamente associate, sono in effetti due tipi di entità distinti, ognuno con il proprio set di proprietà (o attributi) e vincoli applicabili.
Pertanto, come precedentemente indicato nei commenti, sono d'accordo con @Erik e dovresti organizzare il layout logico del tuo database dichiarando tra gli altri elementi:
- una tabella discreta per conservare le informazioni di Address ;
- una tabella per conservare i dettagli specifici del cliente ;
- una tabella per racchiudere i punti dati dell'Ordine ; e
- una tabella per contenere i fatti relativi alle associazioni tra Cliente (i) e Indirizzo (i) ;
come esemplificerò di seguito.
Diagramma IDEF1X dell'esposizione
Un'immagine vale più di mille parole, quindi ho creato il diagramma IDEF1X mostrato nella Figura 1 per illustrare alcune delle possibilità aperte dal mio suggerimento:
Cliente , indirizzo e loro associazioni
Come dimostrato, ho rappresentato un'associazione con un rapporto di cardinalità molti-a-molti (M: N) tra i tipi di entità Cliente a e Indirizzo ; questo approccio fornirebbe flessibilità futura perché, come sapete, un cliente può mantenere più indirizzi nel tempo, o anche contemporaneamente, e lo stesso indirizzo può essere condiviso da più clienti .
Un particolare indirizzo può essere utilizzato in diversi modi con uno-a-molti (1: M) I clienti ; ad esempio, può essere definito come fisico e / o può essere impostato per la spedizione e / o per la fatturazione . Forse, la stessa istanza di Indirizzo può servire ciascuno dei suddetti scopi contemporaneamente, oppure può coprire due usi mentre una diversa occorrenza di Indirizzo copre quella rimanente.
a In alcuni ambienti aziendali, un cliente può essere una persona o un'organizzazione (situazione che implicherebbe un accordo leggermente distinto, come dettagliato in questa risposta su una struttura di sottotipo di sottotipo) ma con l'obiettivo di fornire un esempio semplificato, ho deciso non includere questa possibilità qui. Nel caso in cui sia necessario coprire tale situazione nel database, il post del collegamento precedente mostra il metodo per risolvere tale requisito.
Ordine , indirizzo , CustomerAddress e indirizzo ruoli
Comunemente, un ordine richiede solo due tipi di indirizzi , uno per la spedizione e uno per la fatturazione . In questo modo, la stessa istanza dell'indirizzo potrebbe riempire entrambi i ruoli per un singolo ordine , ma ogni ruolo è rappresentato dalla rispettiva proprietà, ovvero ShippingAddressId o BillingAddressId .
L'ordine è collegato con l' indirizzo tramite il tipo di entità associativa CustomerAddress con l'aiuto di due CHIAVI ESTERI multi-proprietà, ovvero
- ( CustomerNumber , ShippingAddressId ) e ( CustomerNumber , BillingAddressId ),
entrambi puntano al CHIAVE PRIMARIA multi-proprietà CustomerAddress mostrato come
- ( CustomerNumber , AddressId )
... che aiuta a rappresentare una regola aziendale che stabilisce che (a) un'istanza dell'Ordine deve essere collegata esclusivamente con (b) le occorrenze dell'indirizzo precedentemente associate al Cliente specifico che ha effettuato tale Ordine e mai con (c) un non Cliente casuale - indirizzo correlato .
Cronologia per (1) indirizzo e per (2) l' associazione CustomerAddress
Se si vuole fornire la possibilità di modificare Indirizzo pezzi di informazioni, allora devi tenere traccia di tutte le modifiche dei dati. In questo modo ho descritto Address come un tipo di entità "controllabile" che mantiene la propria AddressHistory .
Poiché la natura di una connessione tra un cliente e un indirizzo può anche subire una o più modifiche, ho anche descritto la possibilità di gestire tale associazione come una "verificabile" in virtù del tipo di entità CustomerAddressHistory .
A tale proposito, vari fattori trattati nelle domande e risposte n. 1 e domande e risposte n. 2 , "entrambi sull'abilitazione delle capacità temporali in un database" sono veramente rilevanti.
Layout logico illustrativo SQL-DDL
Di conseguenza, in termini di diagramma visualizzato e spiegato sopra, ho dichiarato la seguente disposizione a livello logico (che puoi adattare per soddisfare le tue esigenze con precisione):
-- 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)
);
Se vuoi dare un'occhiata, l'ho testato in questo violino db che funziona su SQL Server 2017.
I History
tavoli
Il seguente estratto dalla tua domanda è molto importante:
Quello che sto cercando è come posso impostare i miei indirizzi, quindi quando li modifico, l'ordine non è influenzato dal fatto che un cliente aggiorna il suo indirizzo o si trasferisce.
Le tabelle AddressHistory
e CustomerAddressHistory
aiutano a garantire che un ordine non sia influenzato dalle modifiche dell'indirizzo , poiché tutte le righe "precedenti" devono essere conservate nella rispettiva History
tabella e possono essere interrogate quando necessario. Le operazioni di AGGIORNAMENTO e ELIMINAZIONE su queste due tabelle dovrebbero essere vietate (il tentativo di modificare la cronologia può anche avere implicazioni legali negative).
L' intervallo racchiuso tra i valori racchiusi AddressHistory.CreatedDateTime
e AddressHistory.AuditedDateTime
rappresenta l'intero Periodo durante il quale una determinata Address
riga "passata" è stata considerata "presente", "corrente" o "effettiva". Considerazioni simili si applicano alle CustomerAddressHistory
righe.
La CustomerAddress.IsActive
colonna BIT (booleana) intende indicare se una determinata Address
riga è "utilizzabile" da una Customer
riga o meno; ad esempio, se impostato su "falso", ciò significherebbe che un cliente non utilizza più tale indirizzo e quindi non può essere utilizzato per nuovi ordini .
Nota : D'altra parte, ho visto alcuni sistemi in cui ogni volta che un nuovo Ordine viene effettuato, l' indirizzo informazioni deve essere inserito (a volte più volte), e l' indirizzo (i) utilizzati per ultimi ordini non vengono mai cancellati (da qui gli ordini non sono interessati dalle modifiche dell'indirizzo ).
Questa linea di condotta può comportare decisamente notevoli volumi di ridondanza, ma è possibile che, in base agli esatti requisiti informativi del proprio dominio aziendale, possa funzionare, quindi è possibile valutare anche i suoi pro e contro.
Recupero dei dati
“Presente”, “di” o “efficace” versione di un indirizzo occorrenza deve essere contenuto come una riga nella Address
tabella ma selezionando il “stati” precedenti di un indirizzo DAL AddressHistory
(o da CustomerAddressHistory
tavolo) è facile e può essere un esercizio interessante per migliorare le tue abilità di codifica SQL.
Per quanto riguarda una delle situazioni menzionate nei commenti, se si desidera recuperare la "seconda all'ultima versione" di una singola Address
riga DA AddressHistory
, è necessario prendere in considerazione la MAX(AddressHistory.AuditedDateTime)
e la AddressHistory.AddressId
corrispondente al Address.AddressId
valore specifico a portata di mano.
A questo proposito, almeno quando si costruisce un database relazionale , è abbastanza conveniente prima definire lo schema concettuale corrispondente (basato sulle regole aziendali applicabili ) e successivamente dichiarare la sua successiva disposizione logica DDL. Una volta ottenute versioni stabili e affidabili di questi elementi fondamentali (che, ovviamente, possono evolversi nel tempo), è tempo di analizzare e determinare i modi migliori per manipolare (tramite le operazioni INSERT, UPDATE, DELETE e SELECT o le relative combinazioni) il per quanto riguarda i dati.
Percezione degli utenti finali, visualizzazioni e assistenza dei programmi applicativi
Evidentemente, al esterna livello di astrazione, Indirizzo informazione viene percepita (dagli utenti finali) come parte di un Ordine , e non c'è niente di sbagliato in questo, ma ciò non significa che i modellisti devono progettare le parti significative del database in questione come quello. Su questo punto, se non v'è la necessità di, ad esempio, stampare un “pieno” Order (molto fattibile), si può “riprodurre” on-demand con l'aiuto di alcuni operatori di join e le clausole WHERE (considerando il periodo di validità relativa , ecc.) potrebbe essere corretto nelle viste per il consumo futuro, inviando il set di risultati pertinente ai programmi applicativi correlati che, a loro volta, possono migliorare la sua formattazione, se necessario.
Naturalmente, anche i programmi applicativi saranno molto utili quando viene eseguito un Ordine ; ad esempio, una finestra dell'app desktop / mobile o una pagina Web può:
- visualizzare solo gli indirizzi che il cliente interessato ha definito "utilizzabili" (tramite
CustomerAddress.IsActive
);
- elencare tutti gli indirizzi che il cliente ha abilitato per il servizio di fatturazione (tramite
CustomerAddress.IsBilling
); e
- raggruppare tutti gli Indirizzi che il Cliente ha definito per il servizio di spedizione (via
CustomerAddress.IsShipping
);
facilitando in questo modo tutti i processi coinvolti nella GUI (ovvero il livello esterno di astrazione di un sistema computerizzato).
Lettura consigliata
Hai richiesto (nei commenti ora rimossi) alcuni suggerimenti sulla documentazione del database audio; pertanto, per quanto riguarda il materiale teorico , consiglio vivamente di leggere tutto il lavoro scritto dal Dr. EF Codd , un destinatario del Turing Award e, naturalmente, l' unico ideatore del modello relazionale di dati (forse ora più pertinente che mai). Questo elenco include alcuni dei suoi articoli e articoli straordinariamente influenti.
Due lavori importanti che non sono inclusi nell'elenco di cui sopra sono precisamente la sua conferenza del Premio ACM Turing intitolata Relational Database: A Practical Foundation for Productivity , del 1981, e il suo libro intitolato The Relational Model for Database Management: Version 2 , che è stato pubblicato nel 1990.
Sul fronte del design concettuale , Integrated Definition for Information Modeling (IDEF1X) è una tecnica seriamente raccomandabile che è stata definita come standard nel dicembre 1993 dal National Institute of Standards and Technology (NIST) degli Stati Uniti .