2009-04-17 5 views
48

Ho letto alcune domande su SO (ad esempio this one) in merito al controllo delle versioni dei dati in un database.Database - Versioning dati

Mi sono piaciuti alcuni dei suggerimenti menzionati. Ho avuto il tempo più lungo desiderato (necessario) per revisionare molti dei miei tavoli, ma non ci sono mai riuscito. Essendo un programmatore con solo un semplice database lavoro sotto la cintura mi chiedevo come si sarebbe effettivamente andare a fare questo.

Non sto chiedendo la soluzione reale nella sintassi SQL. Finalmente posso capirlo da solo (o postare SO quando arriverà il momento). Sto solo chiedendo alla gente di commentare come farebbero per farlo e qualsiasi potenziale problema di prestazioni ci potrebbe essere se dovessi "revisionare" centinaia di milioni di record. O qualsiasi altro suggerimento purché sia ​​basato sull'esempio qui sotto.

dato un semplice esempio:

Person 
------------------------------------------------ 
ID    UINT NOT NULL, 
PersonID   UINT NOT NULL, 
Name    VARCHAR(200) NOT NULL, 
DOB    DATE NOT NULL, 
Email    VARCHAR(100) NOT NULL 

Audit 
------------------------------------------------ 
ID    UINT NOT NULL, 
UserID   UINT NOT NULL,    -- Who 
TableName   VARCHAR(50) NOT NULL,  -- What 
OldRecID   UINT NOT NULL,    -- Where 
NewRecID   UINT NOT NULL, 
AffectedOn  DATE NOT NULL,    -- When 
Comment   VARCHAR(500) NOT NULL  -- Why 

Non sono sicuro di come si potrebbe collegare la tabella di verifica per tutti gli altri tavoli (come persona) se il TableName è una stringa?

Inoltre, partendo dal presupposto che ho tre GUI per popolare:

  1. Una scheda completa per una persona specifica id
  2. Una vista tabella che elenca tutte le persone (da id)
  3. Una vista che mostra ogni persona con le loro informazioni di revisione sotto ciascuna voce (numero di revisioni per persona, date delle revisioni, commenti di revisione, ecc.) ordinate dalle revisioni più recenti.

Per eseguire 1 e 2, sarebbe meglio interrogare la tabella Persona o la tabella di controllo?

Per realizzare 3, un cosiddetto esperto di database otterrebbe semplicemente tutti i record e li inoltrerà al software per l'elaborazione, o raggrupperà per Persona e Data interessata? Di solito questo è gestito in una query o in molti?

+0

L'audit è improbabile che il commento venga usato molto a meno che non venga popolato automaticamente. –

risposta

42

ho fatto vari sistemi di audit nel corso degli anni e sono attualmente in corso di implementare qualcosa di simile:

Person 
------------------------------------------------ 
ID    UINT NOT NULL, 
PersonID   UINT NOT NULL, 
Name    VARCHAR(200) NOT NULL, 
DOB    DATE NOT NULL, 
Email    VARCHAR(100) NOT NULL 


Person_History 
------------------------------------------------ 
ID    UINT NOT NULL, 
PersonID   UINT NOT NULL, 
Name    VARCHAR(200) NOT NULL, 
DOB    DATE NOT NULL, 
Email    VARCHAR(100) NOT NULL 
AuditID   UINT NOT NULL 


Audit 
------------------------------------------------ 
ID    UINT NOT NULL, 
UserID   UINT NOT NULL,    -- Who 
AffectedOn  DATE NOT NULL,    -- When 
Comment   VARCHAR(500) NOT NULL  -- Why 

Le registrazioni attuali sono sempre nella tabella persona. Se c'è una modifica viene creato un record di controllo e il vecchio record viene copiato nella tabella Person_History (notare che l'ID non cambia e possono esserci più versioni)

L'ID di controllo si trova nelle tabelle * _Storia in modo da poter collega più modifiche ai record a un record di controllo, se lo desideri.

MODIFICA:
Se non si dispone di una tabella della cronologia separata per ogni tabella di base e si desidera utilizzare la stessa tabella per conservare i record vecchi e "eliminati", è necessario contrassegnare i record con un flag di stato. Il problema con questo è un vero dolore quando si eseguono query per i record correnti: credetemi, l'ho fatto.

+1

Grazie per la risposta! Questo era il modo in cui l'avevo immaginato all'inizio, ma poiché ho più di 100 tabelle da revisionare, volevo evitare anche di avere 100 tabelle di revisione e 100 tabelle di controllo. Questo è il motivo per cui stavo cercando di condividere la tabella di controllo su tutte le mie tabelle ed evitare anche la duplicazione di ogni tabella. Forse il mio obiettivo è irragionevole, ma è per questo che sto esplorando e facendo ricerche su questo. Ancora una volta, grazie! – Jeach

+10

+1 per la tabella della cronologia per tabella di base. C'è spesso il desiderio di avere una tabella di controllo.Ciò può causare seri problemi di prestazioni se il sistema è abbastanza occupato. La tabella di controllo diventa un collo di bottiglia per ogni transazione. Molto semplicemente se la transazione n. 2 è in attesa di trans n. 1 per completare il tuo utente si verificherà un rallentamento – Karl

+1

Hai solo bisogno di una tabella di controllo - ma hai ancora bisogno di una tabella di cronologia per ciascuna delle tabelle principali –

2

In seguito al post di DJ sull'utilizzo di una tabella di cronologia per tabella di base e un commento di Karl sui possibili problemi di prestazioni, ho svolto un po 'di ricerche SQL per trovare il modo più veloce per trasferire un record da una tabella ad un altro.

Volevo solo a documentare quello che ho trovato:

ho pensato che avrei dovuto fare uno SQL recuperare per caricare il record dalla tabella di base, seguito con una spinta SQL per mettere il record nella tabella storia , seguito da un aggiornamento alla tabella di base per inserire i dati modificati. Totale di 3 transazioni.

Ma con mia sorpresa mi sono reso conto che è possibile eseguire le prime due transazioni utilizzando un'istruzione SQL utilizzando la sintassi SELECT INTO. Sto scommettendo che le prestazioni sarebbero cento volte più veloci.

Quindi questo ci lascerebbe semplicemente AGGIORNARE il record con i nuovi dati all'interno della tabella di base.

Non ho ancora trovato una dichiarazione SQL per eseguire tutte e 3 le transazioni contemporaneamente (dubito che lo farò).

+0

Non troverai una singola istruzione da fare tutto in una volta. Puoi simularne tre in una volta con una transazione o un punto di salvataggio. –

+0

Un esempio di Sql sarebbe molto apprezzato – Jowen

5

Che ne dici di creare la tabella come di consueto, avere una colonna ModifiedDate su ogni record (e ModifiedBy se ti piace), e fare tutti i tuoi dati di accesso attraverso una vista materializzata che raggruppa i dati per Id e poi fa un HAVING ModifiedDate = MAX (ModifiedDate)?

In questo modo, l'aggiunta di un nuovo record con lo stesso ID di un altro rimuoverà il vecchio record dalla vista. Se si desidera interrogare la cronologia, non passare attraverso la vista

Ho sempre trovato la manutenzione di tabelle diverse con le stesse colonne per essere complesse e soggette a errori.

+0

Ho sperimentato ciò che stavi proponendo usando MySQL. So che è fattibile (in teoria e sembra abbastanza efficiente). Il problema è che MySQL (ho provato diverse versioni) ha dei bug principali con le sue viste (subquries, sort, order, etc). È un grande dolore per @ $$. – Jeach

+0

Qual è la tua strategia di indice cluster sul tavolo? – JoeBrockhaus

+0

Non so, questo approccio è stato solo un esperimento mentale! – mcintyre321

0

Mi piace la tua tabella di controllo, è un buon inizio. Hai un problema con il vostro cardinalità tabella di controllo, quindi vorrei busto fuori come due tabelle:

Person 
------------------------------------------------ 
ID    UINT NOT NULL, 
PersonID   UINT NOT NULL, 
Name    VARCHAR(200) NOT NULL, 
DOB    DATE NOT NULL, 
Email    VARCHAR(100) NOT NULL, 
AuditID   UINT NOT NULL 

Audit 
------------------------------------------------ 
ID    UINT NOT NULL, 
TableName   VARCHAR(50) NOT NULL,  -- What 
TableKey   UINT NOT NULL, 
CreateDate  DATETIME NOT NULL DEFAULT(NOW), 
CreateUserID  UINT NOT NULL, 
ChangeDate  DATETIME NOT NULL DEFAULT(NOW), 
ChangeUserID  UINT NOT NULL 

Audit_Item 
------------------------------------------------ 
ID    UINT NOT NULL, 
AuditID   UINT NOT NULL,    -- Which audit record 
UserID   UINT NOT NULL,    -- Who 
OldRecID   UINT NOT NULL,    -- Where 
NewRecID   UINT NOT NULL, 
AffectedOn  DATE NOT NULL,    -- When 
Comment   VARCHAR(500) NOT NULL  -- Why 

Il layout iniziale proposto ha un singolo record di audit che punta a (presumo) due record Persona. Le sfide di questo design sono:

  • Quali record nella tabella di persona sono i record "reali" correnti?
  • Come si rappresenta l'intera cronologia delle modifiche al record Persona? Se stai puntando a due record nella tabella Persona, quindi vedi il punto 1: quale è il record corrente?
  • I campi Crea *, Cambia * sono arrotolati da una raccolta di record Audit_Item . Sono lì solo per la facilità di accesso .
  • La chiave AuditID nella tabella persona consente di puntare di nuovo al tavolo Audit e arriva a la storia della singola persona senza bisogno di interrogare la tabella di controllo con la clausola WHERE TableName='Person'
+0

Ho osservato per un po 'il tuo problema di "cardinalità" e non riesco a capire i tuoi benefici previsti? Non sono ID utente, CreateUserID e ChangeUserID duplicati delle stesse informazioni? Is not AffectedOn, CreateDate e ChangeDate si duplicano anche? Alo, non è questo l'aggiunta di un'altra transazione SQL ogni volta che una persona viene modificata? Puoi modificare il tuo post per spiegare il tuo miglioramento proposto ... grazie! – Jeach

+1

Penso che tu abbia frainteso il processo della risposta accettata. Ci sarà sempre un solo record Person nella tabella Person. Ci saranno più righe Person_History. L'audit punta a più Person_History, ma tutti quelli puntano a una singola Persona. La persona è "attuale", ogni Person_Storia è una revisione precedente. – JoeBrockhaus