Come conservare i dati storici


162

Alcuni colleghi e io siamo entrati in un dibattito sul modo migliore per archiviare dati storici. Attualmente, per alcuni sistemi, utilizzo una tabella separata per memorizzare i dati storici e conservo una tabella originale per il record corrente e attivo. Quindi, diciamo che ho un tavolo FOO. Sotto il mio sistema, tutti i record attivi andranno in FOO e tutti i record storici andranno in FOO_Hist. Molti campi diversi in FOO possono essere aggiornati dall'utente, quindi voglio tenere un resoconto accurato di tutto aggiornato. FOO_Hist contiene esattamente gli stessi campi di FOO, ad eccezione di un HIST_ID a incremento automatico. Ogni volta che viene aggiornato FOO, effettuo una dichiarazione INSERT INTO FOO_Hist simile a: insert into FOO_HIST select * from FOO where id = @id.

Il mio collega dice che questo è un cattivo design perché non dovrei avere una copia esatta di una tabella per motivi storici e dovrei semplicemente inserire un altro record nella tabella attiva con una bandiera che indica che è per scopi storici.

Esiste uno standard per gestire l'archiviazione dei dati storici? Mi sembra che non voglio ingombrare i miei record attivi con tutti i miei record storici nella stessa tabella considerando che potrebbe essere ben oltre un milione di dischi (sto pensando a lungo termine).

Come gestite voi o la vostra azienda?

Sto usando MS SQL Server 2008, ma vorrei mantenere la risposta generica e arbitraria di qualsiasi DBMS.

Risposte:


80

Supportare i dati storici direttamente all'interno di un sistema operativo renderà la tua applicazione molto più complessa di quanto non sarebbe altrimenti. In generale, non consiglierei di farlo a meno che tu non abbia un duro requisito per manipolare le versioni storiche di un record all'interno del sistema.

Se osservi attentamente, la maggior parte dei requisiti per i dati storici rientra in una delle due categorie:

  • Registrazione di controllo: è meglio farlo con le tabelle di controllo. È abbastanza facile scrivere uno strumento che genera script per creare tabelle di log di controllo e trigger leggendo i metadati dal dizionario dei dati di sistema. Questo tipo di strumento può essere utilizzato per aggiornare la registrazione di controllo sulla maggior parte dei sistemi. È inoltre possibile utilizzare questo sottosistema per acquisire i dati modificati se si desidera implementare un data warehouse (vedere di seguito).

  • Rapporti storici: rapporti sullo stato storico, posizioni "al momento" o rapporti analitici nel tempo. Potrebbe essere possibile soddisfare semplici requisiti di reportistica storica eseguendo una query sulle tabelle di registrazione di controllo del tipo sopra descritto. Se hai requisiti più complessi, potrebbe essere più economico implementare un data mart per il reporting piuttosto che provare a integrare la cronologia direttamente nel sistema operativo.

    Le dimensioni che cambiano lentamente sono di gran lunga il meccanismo più semplice per tracciare e interrogare lo stato storico e gran parte del tracciamento della cronologia può essere automatizzato. I gestori generici non sono così difficili da scrivere. In genere, i report storici non devono utilizzare dati aggiornati al minuto, quindi un meccanismo di aggiornamento in batch va normalmente bene. In questo modo l'architettura del core e del sistema di reporting è relativamente semplice.

Se i tuoi requisiti rientrano in una di queste due categorie, probabilmente stai meglio non conservare i dati storici nel tuo sistema operativo. Separare la funzionalità storica in un altro sottosistema sarà probabilmente meno sforzo complessivo e produrrà database transazionali e di audit / reporting che funzionano molto meglio per lo scopo previsto.


Penso di vedere quello che stai dicendo. Quindi quello che ho fatto con la mia tabella FOO_Hist è stato davvero creare una tabella di controllo. Invece di utilizzare un trigger per inserirlo nella tabella di controllo durante l'aggiornamento, ho appena eseguito una dichiarazione nel programma. È corretto?
Aaron,

6
Abbastanza. Tuttavia, è meglio eseguire questo tipo di registrazione di controllo con i trigger; i trigger assicurano che eventuali modifiche (comprese le correzioni manuali dei dati) vengano registrate nei log di controllo. Se hai più di 10-20 tabelle da controllare, probabilmente è più rapido creare uno strumento generatore di trigger. Se il traffico su disco per i registri di controllo è un problema, è possibile inserire le tabelle dei registri di controllo su un set separato di dischi.
Preoccupato di

Sì, sono d'accordo al 100%. Grazie.
Aaron,

40

Non penso che ci sia un modo standard particolare di farlo, ma ho pensato di aggiungere un metodo possibile. Lavoro in Oracle e nel nostro framework di applicazioni Web interno che utilizza XML per l'archiviazione dei dati delle applicazioni.

Usiamo qualcosa chiamato modello Master - Detail che nella sua forma più semplice è costituito da:

La tabella principale, ad esempio, viene Widgetsspesso chiamata semplicemente contenente un ID. Conterrà spesso dati che non cambieranno nel tempo / non sono storici.

Tabella dettagli / cronologia ad esempio chiamata Widget_Detailscontenente almeno:

  • ID: chiave primaria. Dettaglio / ID storico
  • MASTER_ID - ad esempio in questo caso chiamato "WIDGET_ID", questo è l'FK del record principale
  • START_DATETIME: data e ora che indicano l'inizio della riga del database
  • END_DATETIME: data e ora che indica la fine della riga del database
  • STATUS_CONTROL - la colonna a carattere singolo indica lo stato della riga. 'C' indica che corrente, NULL o 'A' sarebbero storici / archiviati. Lo usiamo solo perché non possiamo indicizzare su END_DATETIME essendo NULL
  • CREATED_BY_WUA_ID: memorizza l'ID dell'account che ha causato la creazione della riga
  • XMLDATA: memorizza i dati effettivi

Quindi, in sostanza, un'entità inizia con 1 riga nel master e 1 riga nel dettaglio. Il dettaglio con una data di fine NULL e STATUS_CONTROL di "C". Quando si verifica un aggiornamento, la riga corrente viene aggiornata per avere END_DATETIME dell'ora corrente e status_control è impostato su NULL (o 'A' se preferito). Nella tabella dei dettagli viene creata una nuova riga, ancora collegata allo stesso master, con status_control 'C', l'id della persona che effettua l'aggiornamento e i nuovi dati memorizzati nella colonna XMLDATA.

Questa è la base del nostro modello storico. La logica di creazione / aggiornamento è gestita in un pacchetto Oracle PL / SQL in modo da passare semplicemente la funzione all'ID corrente, all'ID utente e ai nuovi dati XML e internamente esegue tutto l'aggiornamento / inserimento di righe per rappresentarlo nel modello storico . I tempi di inizio e fine indicano quando è attiva quella riga nella tabella.

Lo spazio di archiviazione è economico, in genere non ELIMINIAMO i dati e preferiamo tenere una pista di controllo. Questo ci consente di vedere come apparivano i nostri dati in qualsiasi momento. Indicizzando status_control = 'C' o usando una vista, il disordine non è esattamente un problema. Ovviamente le tue domande devono tener conto che dovresti sempre usare la versione corrente (NULL end_datetime e status_control = 'C') di un record.


Ciao Chris, se lo fai, l'ID (chiave primaria) deve essere cambiato giusto? che ne dici della relazione con un'altra tabella se è usata da altre?
projo,

@projo l'ID sulla tua tabella principale è il PK e concettualmente il "PK" per qualunque concetto tu abbia a che fare. L'ID sulla tabella dei dettagli è il PK per identificare una versione storica per il master (che è un'altra colonna sul dettaglio). Quando si formano relazioni si fa spesso riferimento al vero PK del proprio concetto (ovvero l'ID sulla tabella principale o la colonna MASTER_ID nei dettagli) e si utilizza STATUS_CONTROL = 'C' per assicurarsi di ottenere la versione corrente. In alternativa, puoi fare riferimento all'ID dettaglio per mettere in relazione qualcosa con un determinato momento.
Chris Cameron-Mills,

+1 Ho implementato questo modello con grande successo su diversi grandi progetti.
Three Value Logic,

Stiamo usando lo stesso approccio, ma ora mi chiedo se è meglio archiviare solo START_DATETIME e non archiviare
END_DATETIME

Un paio di variazioni nella mia esperienza. Se la tua entità è "terminata", ovvero archiviata o eliminata, potresti in effetti non avere record di dettaglio con controllo dello stato 'C', cioè nessuna riga corrente, anche se non sapresti quando ciò accadrà. In alternativa, è possibile impostare un end_datetime sull'ultima riga e la presenza di una riga "C" terminata potrebbe indicare che l'entità è ora eliminata / archiviata. Infine, potresti rappresentarlo attraverso un'altra colonna, STATUS che probabilmente potresti già avere.
Chris Cameron-Mills,

15

Penso che il tuo approccio sia corretto. La tabella storica deve essere una copia della tabella principale senza indici, assicurati di avere anche il timestamp di aggiornamento nella tabella.

Se provi l'altro approccio abbastanza presto, incontrerai problemi:

  • spese generali di manutenzione
  • più flag in seleziona
  • rallentamento delle query
  • crescita di tabelle, indici

7

In SQL Server 2016 e versioni successive , è disponibile una nuova funzionalità denominata Tabelle temporali che mira a risolvere questa sfida con il minimo sforzo dello sviluppatore . Il concetto di tabella temporale è simile a Change Data Capture (CDC), con la differenza che la tabella temporale ha estratto la maggior parte delle cose che dovevi fare manualmente se stessi usando CDC.




1

Volevo solo aggiungere un'opzione che ho iniziato a usare perché uso Azure SQL e la cosa con più tabelle era troppo ingombrante per me. Ho aggiunto un trigger di inserimento / aggiornamento / eliminazione sul mio tavolo e quindi ho convertito la modifica prima / dopo in json utilizzando la funzione "FOR JSON AUTO".

 SET @beforeJson = (SELECT * FROM DELETED FOR JSON AUTO)
SET @afterJson = (SELECT * FROM INSERTED FOR JSON AUTO)

Ciò restituisce una rappresentazione JSON per il record prima / dopo la modifica. Quindi memorizzo quei valori in una tabella cronologica con un timestamp di quando si è verificata la modifica (memorizzo anche l'ID per il record corrente di preoccupazione). Utilizzando il processo di serializzazione, posso controllare come i dati vengono riempiti nuovamente in caso di modifiche allo schema.

Ho imparato questo da questo link qui


0

Potresti semplicemente partizionare le tabelle no?

"Strategie di tabelle e indici partizionate utilizzando SQL Server 2008 Quando una tabella di database aumenta di dimensioni fino a centinaia di gigabyte o più, può essere più difficile caricare nuovi dati, rimuovere vecchi dati e mantenere gli indici. Solo la dimensione pura della tabella fa sì che tali operazioni richiedano molto più tempo. Anche i dati che devono essere caricati o rimossi possono essere molto considerevoli, rendendo impraticabili le operazioni INSERT e DELETE sulla tabella. Il software di database Microsoft SQL Server 2008 fornisce il partizionamento delle tabelle per rendere più gestibili tali operazioni. "


Sì, posso partizionare le tabelle, ma è quello standard quando si tratta di dati storici? I dati storici devono essere inclusi nella stessa tabella dei dati attivi? Queste sono le domande che volevo discutere. Anche questo non è arbitrario in quanto riguarda SQL Server 2008.
Aaron

0

La vera domanda è: è necessario utilizzare insieme dati storici e dati attivi per i rapporti? In tal caso, tenerli in una tabella, partizionare e creare una vista da utilizzare per i record attivi nelle query attive. Se hai solo bisogno di guardarli di tanto in tanto (per ricercare problemi relativi al campionato o alcuni di questi), mettili in un tavolo separato.


2
È più difficile JOINdue tabelle in un paio di report storici o è più difficile modificare ogni singolo inserto / aggiornamento / eliminazione della tabella per essere consapevoli delle preoccupazioni storiche? In realtà, un registro di controllo includerebbe anche i dati correnti nella tabella della cronologia, quindi la tabella corrente non dovrebbe nemmeno essere necessaria in un rapporto.

0

Un'altra opzione è quella di archiviare i dati operativi su base [giornaliera | oraria | qualunque]. La maggior parte dei motori di database supporta l'estrazione dei dati in un archivio .

Fondamentalmente, l'idea è quella di creare un lavoro programmato per Windows o CRON

  1. determina le tabelle correnti nel database operativo
  2. seleziona tutti i dati da ogni tabella in un file CSV o XML
  3. comprime i dati esportati in un file ZIP, preferibilmente con il timestamp della generazione nel nome del file per un'archiviazione più semplice.

Molti motori di database SQL sono dotati di uno strumento che può essere utilizzato per questo scopo. Ad esempio, quando si utilizza MySQL su Linux, è possibile utilizzare il seguente comando in un lavoro CRON per pianificare l'estrazione:

mysqldump --all-databases --xml --lock-tables=false -ppassword | gzip -c | cat > /media/bak/servername-$(date +%Y-%m-%d)-mysql.xml.gz

2
Questo non è affatto appropriato per i dati storici perché se qualcuno cambia un valore e lo cambia nel ciclo di archiviazione, gli aggiornamenti vanno persi. Inoltre, non esiste un modo semplice per esaminare le modifiche a un'entità nel tempo o ripristinare parzialmente un'entità.
Sgoettschkes,

0

Conosco questo vecchio post ma volevo solo aggiungere alcuni punti. Lo standard per tali problemi è ciò che funziona meglio per la situazione. è molto importante comprendere la necessità di tale archiviazione e il potenziale utilizzo dei dati storici / di controllo / di rilevamento delle modifiche.

Audit (scopo di sicurezza) : utilizzare una tabella comune per tutte le tabelle verificabili. definire la struttura per memorizzare i nomi delle colonne, prima dei valori e dopo i valori.

Archivio / Storico : per casi come tenere traccia dell'indirizzo precedente, numero di telefono ecc., Creare una tabella separata FOO_HIST è meglio se lo schema della tabella delle transazioni attive non cambia in modo significativo in futuro (se la tabella della cronologia deve avere la stessa struttura). se prevedi la normalizzazione della tabella, il tipo di dati modifica l'aggiunta / rimozione di colonne, archivia i tuoi dati storici in formato xml. definire una tabella con le seguenti colonne (ID, Data, Versione schema, Dati XML). questo gestirà facilmente le modifiche dello schema. ma devi fare i conti con XML e questo potrebbe introdurre un livello di complicazioni per il recupero dei dati.



Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.