2010-08-20 18 views
15

Prefazione: L'altro giorno stavo pensando a una nuova struttura di database per una nuova applicazione e mi sono reso conto che avevamo bisogno di un modo per archiviare i dati storici in modo efficiente. Volevo qualcun altro a dare un'occhiata e vedere se ci sono problemi con questa struttura. Mi rendo conto che questo metodo di memorizzazione dei dati potrebbe benissimo essere stato inventato prima (ne sono quasi certo) ma non ho idea se abbia un nome e alcune ricerche su google che ho provato non abbiano dato alcun risultato.Struttura del database per la memorizzazione dei dati storici

Problema: Diciamo che avete una tabella per gli ordini e gli ordini sono relativi a una tabella clienti per il cliente che ha effettuato l'ordine. In un normale struttura del database si potrebbe aspettare qualcosa di simile:

orders 
------ 
orderID 
customerID 


customers 
--------- 
customerID 
address 
address2 
city 
state 
zip 

piuttosto semplice, orderID ha una chiave esterna di customerID che è la chiave primaria della tabella cliente. Ma se dovessimo andare ed eseguire un report sulla tabella degli ordini, entreremo nella tabella dei clienti nella tabella degli ordini, che restituirà il record corrente per quell'ID cliente. Cosa succede se al momento dell'ordine, l'indirizzo del cliente era diverso ed è stato successivamente modificato. Ora il nostro ordine non riflette più la storia di quell'indirizzo del cliente, al momento dell'ordine. Fondamentalmente, cambiando il record del cliente, abbiamo appena cambiato tutta la cronologia per quel cliente.

Ora ci sono diversi modi per aggirare questo, uno dei quali sarebbe quello di copiare il record quando è stato creato un ordine. Quello che mi è venuto in mente è quello che penso sarebbe un modo più semplice per farlo, che è forse un po 'più elegante, e ha il vantaggio aggiuntivo di registrare ogni volta che viene apportata una modifica.

Che cosa succede se ho fatto una struttura come questa, invece:

orders 
------ 
orderID 
customerID 
customerHistoryID 


customers 
--------- 
customerID 
customerHistoryID 


customerHistory 
-------- 
customerHistoryID 
customerID 
address 
address2 
city 
state 
zip 
updatedBy 
updatedOn 

ti prego di perdonare la formattazione, ma penso che si può vedere l'idea. Fondamentalmente, l'idea è che ogni volta che un cliente viene modificato, inserito o aggiornato, il customerHistoryID viene incrementato e la tabella dei clienti viene aggiornata con l'ultimo customerHistoryID. La tabella degli ordini ora non punta solo al customerID (che consente di vedere tutte le revisioni del record del cliente), ma anche al customerHistoryID, che indica una specifica revisione del record. Ora l'ordine riflette lo stato dei dati al momento della creazione dell'ordine.

Aggiungendo una colonna aggiornata e aggiornata alla tabella customerHistory, è anche possibile visualizzare un "registro di controllo" dei dati, in modo da poter vedere chi ha apportato le modifiche e quando.

Uno svantaggio potenziale potrebbe essere le eliminazioni, ma non mi preoccupo molto di questo per questo bisogno visto che nulla dovrebbe mai essere cancellato. Ma anche in questo caso, lo stesso effetto si può ottenere usando un activeFlag o qualcosa del genere a seconda del dominio dei dati.

Il mio pensiero è che tutti i tavoli utilizzino questa struttura. Ogni volta che vengono recuperati dati storici, questi verranno uniti alla tabella della cronologia utilizzando customerHistoryID per mostrare lo stato dei dati per quel particolare ordine.

Il recupero di un elenco di clienti è semplice, basta unirsi alla tabella clienti sul clienteHistoryID.

Qualcuno può riscontrare problemi con questo approccio, sia dal punto di vista del design, sia in termini di prestazioni, perché questo è negativo. Ricorda, non importa cosa faccio Devo assicurarmi che i dati storici siano conservati in modo che i successivi aggiornamenti dei record non cambino la cronologia. C'è un modo migliore? È un'idea nota che ha un nome o una documentazione su di esso?

Grazie per qualsiasi aiuto.

Aggiornamento: Questo è un esempio molto semplice di ciò che avrò davvero. La mia vera applicazione avrà "ordini" con diverse chiavi esterne ad altre tabelle. Informazioni sulla posizione di origine/destinazione, informazioni sui clienti, informazioni sulla struttura, informazioni sugli utenti, ecc. È stato suggerito un paio di volte di poter copiare le informazioni nel record dell'ordine in quel punto, e l'ho visto fare in questo modo molte volte, ma ciò comporterebbe un record con centinaia di colonne, che in questo caso non è realmente fattibile.

+0

Quindi, in pratica quello che stai dicendo è questo: "Ho troppe colonne nella tabella ordine Perciò mi piacerebbe. per inserire l'indirizzo dell'ordine nella tabella clienti. Per supportarlo, vorrei compromettere i dati del cliente con uno schema di tracciamento cronologico complesso. " Mi sembra una cattiva idea. –

+1

No ... niente affatto. Quello che sto dicendo è che devo essere in grado di tenere traccia degli indirizzi, quando cambiano, ed essere in grado di legare un ordine a uno stato specifico (revisione) di un indirizzo. Gli ordini potrebbero non essere l'unica tabella che lega ad un indirizzo, per non parlare vogliamo sapere quando e chi ha cambiato un indirizzo. –

+0

BTW non presume mai che nulla verrà eliminato. Pianificare le eliminazioni che si verificheranno inevitabilmente o creare un trigger che non consenta le eliminazioni. – HLGEM

risposta

10

Quando ho riscontrato problemi di questo tipo, un'alternativa è rendere l'ordine nella tabella della cronologia. Le sue funzioni lo stesso, ma è un po 'più facile da seguire

orders 
------ 
orderID 
customerID 
address 
City 
state 
zip 



customers 
--------- 
customerID 
address 
City 
state 
zip 

EDIT: se il numero di colonne arriva a elevato per i vostri gusti è possibile separare fuori come più vi piace.

Se si utilizza l'altra opzione e si utilizzano le tabelle della cronologia, è consigliabile prendere in considerazione l'utilizzo dei dati bitemporal poiché potrebbe essere necessario risolvere la possibilità di correggere i dati storici. Ad esempio, il cliente ha cambiato il suo indirizzo corrente da A a B, ma devi anche correggere l'indirizzo su un ordine esistente che è attualmente soddisfatto.

Inoltre, se si utilizza MS SQL Server, è possibile prendere in considerazione l'utilizzo di viste indicizzate. Ciò ti consentirà di scambiare una piccola diminuzione incrementale di inserimento/aggiornamento incrementale per un grande aumento di perf select. Se non si utilizza MS SQL Server, è possibile replicarlo utilizzando trigger e tabelle.

+1

sì, l'ho visto fare in questo modo anche prima. Ma questo è un esempio molto appaiato, nella vera applicazione a cui sto pensando, un "ordine" avrà un mazzo di chiavi esterne con molti dati negli altri tavoli. Alla fine avrei un record di "ordine" con centinaia di colonne. –

+6

Questo è l'approccio corretto, perché l'indirizzo è una funzione dell'ordine, non del cliente, nel momento in cui viene effettuato l'ordine. Se si desidera semplificare la tabella degli ordini, suggerisco una chiave esterna a una tabella degli indirizzi.Infatti, i clienti e gli ordini possono entrambi memorizzare i loro indirizzi nella stessa tabella di indirizzi senza difficoltà. Ciò faciliterà anche l'inclusione di indirizzi di spedizione e fatturazione separati, ecc. –

+1

@Jeffrey L Whitledge sta facendo un punto cruciale qui, gli addres, il nome del cliente, il prezzo ecc. Sono ora una funzione dell'ordine, non del cliente o tabelle dei prezzi, ecco perché non c'è altra buona soluzione se non metterle in tabelle relative agli ordini. – HLGEM

4

Normalmente gli ordini memorizzano semplicemente le informazioni così come sono al momento dell'ordine. Questo è particolarmente vero per cose come numeri di parte, nomi di parti e prezzi, nonché indirizzo e nome del cliente. Quindi non devi unirti a 5 o sei tabelle per ottenere le informazioni che possono essere memorizzate in una sola. Questa non è denormalizzazione in quanto è effettivamente necessario disporre delle informazioni esistenti al momento dell'ordine. Penso che sia meno probabile che avere queste informazioni nell'ordine e nei dettagli dell'ordine (memorizza i singoli articoli ordinati) sia meno rischioso in termini di modifiche accidentali ai dati.

La tabella degli ordini non avrebbe centinaia di colonne. Avresti una tabella degli ordini e una tabella dei dettagli degli ordini dovuta a una o più relazioni. La tabella degli ordini includerebbe il numero d'ordine ID cliente 9 è possibile cercare tutto ciò che questo cliente ha mai ordinato anche se il nome è cambiato), il nome del cliente, l'indirizzo del cliente (nota che non è necessario zip dello stato della città ecc., inserire l'indirizzo in un campo), la data dell'ordine ed eventualmente un pochi altri campi che riguardano direttamente l'ordine ad un livello superiore. Poi hai una tabella dei dettagli dell'ordine che ha il numero dell'ordine, l'id_dettaglio, il numero di parte, la descrizione della parte (questo può essere un consolidamento di una serie di campi come dimensione, colore ecc. Oppure puoi separare il più comune), No di elementi, tipo di unità, prezzo unitario, tasse, prezzo totale, data di spedizione, stato. Hai inserito una voce per ogni articolo ordinato.

+0

Vedo quello che stai dicendo, ma come ho detto sulla risposta di Conrad Frix, alla fine la mia tabella "ordine" avrebbe centinaia di colonne, cosa che in realtà non è fattibile. Probabilmente dovrei aggiungere questo alla domanda. –

0

Mi piace essere semplice. Vorrei utilizzare due tabelle, una tabella clienti e una tabella cronologia clienti. Se hai la chiave (es. CustomerId) nella tabella della cronologia non c'è motivo di creare una tabella di unione, una selezione su quella chiave ti darà tutti i record.

Inoltre non si dispone di informazioni di controllo (ad es. Data di modifica, chi ha modificato ecc.) Nella tabella della cronologia come viene mostrato, mi aspetto che lo si desideri.

Quindi la mia sarebbe simile a questa:

CustomerTable (this contains current customer information) 
CustID (distinct non null) 
...all customer information fields 

CustomerHistoryTable 
CustId (not distinct non null) 
...all customer information fields 
DateOfChange 
WhoChanged 

Il campo DataOfChagne è la data della tabella dei clienti è stato modificato (a partire dai valori di questo record) per i valori in un disco più recente dei valori the CustomerTable

La tabella degli ordini richiede solo un CustomerID se è necessario trovare le informazioni sul cliente al momento dell'ordine è una selezione semplice.

+0

Non so che questo abbia qualche vantaggio rispetto allo schema suggerito dall'OP - e in qualche modo questo è un po 'più imbarazzante (se vuoi trovare informazioni storiche sui clienti., Come probabilmente vorrai, questo è un po 'più complesso) - detto questo, ho già usato questo approccio in precedenza e l'ho visto usato da molti altri programmatori. –

+0

Hai ragione, vorrei modificare la data, chi ha modificato. (L'ho menzionato nella scrittura, ma non nel tavolo, modifico per renderlo più chiaro). In realtà l'ho progettato esattamente in questo modo all'inizio, ma ho realizzato che si tratta di una duplicazione dei dati non necessaria. Con un semplice join (che dovrebbe essere molto veloce con indici appropriati) posso fare la stessa cosa e risparmiare dover scrivere tutte queste informazioni due volte ogni volta. Ma all'inizio ho avuto lo stesso pensiero. –

+0

Quale sarebbe il riferimento dell'ordine? L'ID cliente? In caso affermativo, la modifica dell'indirizzo del cliente influisce automaticamente sulle informazioni dell'ordine? –

0

Quello che vuoi è chiamato un datawarehouse. Poiché i datawarehouse sono OLAP e non OLTP, si consiglia di disporre di tutte le colonne necessarie per raggiungere i propri obiettivi. Nel tuo caso la tabella orders nel datawarehouse avrà 11 campi con una "istantanea" di ordini man mano che arrivano, indipendentemente dagli aggiornamenti degli account degli utenti.

Wiley -The Data Warehouse Toolkit, Second Edition 

È un buon inizio.

+0

Datawarehouse/Datamarts non sono necessariamente OLAP. Datawarehouse e datamarts potrebbero essere la fonte dei cubi OLAP ma potresti avere un datawarehouse e nessun OLAP. – jasonco

+0

@jasonco I feed di Datawarehouse sono generalmente OLTP, giusto ma è tutto. Tuttavia, Datawarehouse non calcola e per fare ciò devono dimenticare la normalizzazione e quindi avere tabelle enormi e tempi di risposta, che va bene dato che sono OLAP e non OLTP. @OP ha bisogno di un Datawarehouse, secondo la sua descrizione del problema, non solo un altro DB. – Ben

5

Quando si progettano le strutture dati, essere molto attenti a memorizzare le relazioni corrette, non qualcosa che è simile alle relazioni corrette. Se l'indirizzo per un ordine deve essere mantenuto, allora è perché l'indirizzo fa parte dell'ordine, non del cliente. Inoltre, i prezzi unitari sono parte dell'ordine, non il prodotto, ecc

Prova un accordo come questo:

Customer 
-------- 
CustomerId (PK) 
Name 
AddressId (FK) 
PhoneNumber 
Email 

Order 
----- 
OrderId (PK) 
CustomerId (FK) 
ShippingAddressId (FK) 
BillingAddressId (FK) 
TotalAmount 

Address 
------- 
AddressId (PK) 
AddressLine1 
AddressLine2 
City 
Region 
Country 
PostalCode 

OrderLineItem 
------------- 
OrderId (PK) (FK) 
OrderItemSequence (PK) 
ProductId (FK) 
UnitPrice 
Quantity 

Product 
------- 
ProductId (PK) 
Price 

etc. 

Se veramente bisogno di memorizzare storia per qualcosa, come tracciamento modifiche a un ordina nel tempo, quindi dovresti farlo con un registro o una tabella di controllo, non con le tue tabelle di transazione.

+0

i tavoli che ho usato erano solo a scopo illustrativo. Faremo esattamente ciò che descrivi, separando l'indirizzo dal cliente (lo chiameremo posizione). La mia domanda riguarda solo lo schema di archiviazione dei dati. –

+0

Questa risposta non aiuta con la domanda originale. Vuole aiuto con il tempo, questa soluzione continua a utilizzare una tabella Indirizzi e, se un indirizzo è aggiornato, aggiorna qualsiasi ordine e ha una relazione di chiave esterna. – maguy

+0

@maguy - Non è chiaro dalla mia risposta, ma la mia intenzione era che i dati dell'indirizzo non sarebbero mai stati aggiornati. Invece, se un indirizzo cliente cambia, allora verrà inserito un nuovo indirizzo e il cliente riceverà un nuovo ID indirizzo, lasciando invariato l'indirizzo ordine esistente. Allo stesso modo, se l'indirizzo sull'ordine deve cambiare. –

0

Il nostro sistema di gestione stipendi utilizza date effettive in molte tabelle. La tabella INDIRIZZI è impostata su EMPLID e EFFDT. Questo ci consente di monitorare ogni volta che cambia l'indirizzo di un dipendente. È possibile utilizzare la stessa logica per tenere traccia degli indirizzi storici per i clienti. Le tue domande dovrebbero semplicemente includere una clausola che confronta la data dell'ordine con la data di indirizzo del cliente che era in vigore al momento dell'ordine. Ad esempio

select o.orderID, c.customerID, c.address, c.city, c.state, c.zip 
from orders o, customers c 
where c.customerID = o.customerID 
and c.effdt = (
    select max(c1.effdt) from customers c1 
    where c1.customerID = c.customerID and c1.effdt <= o.orderdt 
) 

L'obiettivo è quello di selezionare la riga più recente in clienti aventi una data efficace e entro la data dell'ordine. Questa stessa strategia potrebbe essere utilizzata per mantenere informazioni storiche sui prezzi dei prodotti.

0

Se sei sinceramente interessato a questi problemi, posso solo suggerirti di dare un'occhiata seria a "Dati temporali e modello relazionale".

Warning1: non c'è SQL in là e quasi tutto ciò che pensi di sapere sul modello relazionale verrà reclamato come una menzogna. Con una buona ragione

Warning2: ci si aspetta che pensino e riflettano.

Warning3: il libro parla di come dovrebbe essere la soluzione per questa particolare famiglia di problemi, ma come dice l'introduzione, non si tratta di alcuna tecnologia disponibile oggi.

Detto questo, il libro è un'autentica illuminazione. Per lo meno, aiuta a chiarire che la soluzione per tali problemi non si troverà in SQl così com'è oggi, o negli ORM come quelli che si presentano oggi, del resto.

2

Come ho risposto prima per a similar question:

ho scoperto che quello che sembra essere un primo suono "maestro del cliente" di design, spesso in seguito si rompe perché: in precedenza il trattamento di un business come il cliente si evolve in trattamento di singoli dipendenti come i clienti, o un cliente cambierà/suddividerà/unirà gli indirizzi, o un'azienda cambierà il suo nome, ma vogliamo ancora consolidare vecchi e nuovi totali di rendimento, oppure un indirizzo di spedizione e un indirizzo di fatturazione devono ora essere espansi per includere un indirizzo di supporto, o gli operatori dimenticano o scambiano uno scopo di indirizzo per un altro, o un cliente desidera utilizzare un indirizzo di spedizione speciale solo temporaneamente, ecc. ecc.

Come risultato, sono giunto ad abbandonare del tutto l'idea di un file cliente principale. Invece, le informazioni su nome/azienda/indirizzo non sono mai record anagrafici (ad eccezione di applicazioni come la fatturazione di utenze o le tasse di proprietà in cui un indirizzo fisico specifico non è mai modificabile in un altro indirizzo), sono solo campi che indicano il contatto in uso A UN PARTICOLAR POINT IN TIME, di solito all'interno di qualcosa come un record di ordine di vendita. Ogni ordine di vendita è incatenato all'ordine precedente e successivo per quel cliente, anche quando il cliente cambia nome o indirizzo. Il vantaggio è che tutti gli ordini possono essere consolidati/totalizzati/analizzati nell'intera cronologia delle transazioni del cliente, anche se ciascun ordine potrebbe variare il nome o l'indirizzo del contatto. È alquanto controintuitivo, soprattutto quando si tenta di normalizzare i progettisti db, ma finisce per essere molto flessibile e conveniente.

Ad esempio, quando il cliente X effettua prima un ordine, non viene creato alcun record cliente. Invece, viene creato un record dell'ordine di vendita che contiene le informazioni necessarie sul nome/sulla società/indirizzo in vigore al momento dell'ordine. Quando il cliente X inserisce il suo secondo ordine, non cerchiamo un file cliente, cerchiamo il file dell'ordine di vendita, quindi lo copia/incateni per creare il suo secondo ordine di vendita. Se vuole cambiare il suo nome/azienda/indirizzo, va bene, modifichiamo questi campi nell'ordine di vendita n. 2 e l'ordine di vendita n. 1 rimane invariato. Ora è localizzabile in entrambe le varianti (ordine 1 o 2).

Per altre considerazioni quando si cerca di decidere se due record dei clienti sono in realtà la stessa, vedere http://semaphorecorp.com/mpdd/mpdd.html

Problemi correlati