Esiste una tecnica chiamata versioning che esiste da molti anni ma è in gran parte impraticabile per diversi motivi. Tuttavia, esiste una tecnica simile che chiamo Version Normal Form che ho trovato molto utile. Ecco un esempio di utilizzo di una tabella Dipendenti.
Innanzitutto, viene creata la tabella statica. Questa è la tabella dell'entità principale e contiene dati statici sull'entità. I dati statici sono dati che non dovrebbero cambiare durante la vita dell'entità, come la data di nascita.
create table Employees(
ID int auto_generated primary key,
FirstName varchar( 32 ),
Hiredate date not null,
TermDate date, -- last date worked
Birthdate date,
... -- other static data
);
È importante rendersi conto che esiste una voce per ogni dipendente, proprio come con qualsiasi tabella di questo tipo.
Quindi la tabella delle versioni associata. Questo stabilisce una relazione di 1 m con la tabella statica poiché potrebbero esserci diverse versioni per un dipendente.
create table Employee_versions(
ID int not null,
EffDate date not null,
char( 1 ) IsWorking not null default true,
LastName varchar( 32 ), -- because employees can change last name
PayRate currency not null,
WorkDept int references Depts( ID ),
..., -- other changable data
constraint PK_EmployeeV primary key( ID, EffDate )
);
Nella nota della tabella delle versioni è presente una data di validità ma non un campo corrispondente non più valido. Questo perché una volta che una versione diventa effettiva, rimane in vigore fino a quando non viene sostituita dalla versione successiva. La combinazione di ID ed EffDate deve essere univoca, quindi non possono esserci due versioni per lo stesso dipendente attive contemporaneamente, né può esserci un intervallo tra il momento in cui una versione termina e l'inizio della versione successiva.
La maggior parte delle query vorrà conoscere la versione corrente dei dati dei dipendenti. Ciò viene fornito unendo la riga statica per il dipendente con la versione attualmente in vigore. Questo può essere trovato con la seguente query:
select ...
from Employees e
join Employee_versions v1
on v1.ID = e.ID
and v1.EffDate =(
select Max( v2.EffDate )
from EmployeeVersions v2
where v2.ID = v1.ID
and v2.EffDate <= NOW()
)
where e.ID = :EmpID;
Questo restituisce l'unica versione iniziata nel passato più recente. Usando la disuguaglianza <=nel controllo della data (v2.EffDate <= NOW()
) consente date di entrata in vigore in futuro. Supponiamo che tu sappia che un nuovo dipendente inizierà il primo giorno del mese successivo o che un aumento della retribuzione sia previsto per il 13 del mese successivo, questi dati possono essere inseriti in anticipo. Tali voci "precaricate" verranno ignorate.
Non lasciare che la sottoquery ti raggiunga. Tutti i campi di ricerca sono indicizzati, quindi il risultato è abbastanza veloce.
C'è molta flessibilità con questo design. La query sopra restituisce i dati più recenti di tutti i dipendenti, presenti e passati. Puoi controllare TermDate
campo per ottenere solo dipendenti presenti. In effetti, poiché un buon numero di posti nelle tue app saranno interessati solo alle informazioni attuali dei dipendenti attuali, quella query sarebbe una buona visualizzazione (ometti il where
finale clausola). Non c'è bisogno che le app sappiano che esistono versioni di questo tipo.
Se hai una data particolare e vuoi vedere i dati che erano in vigore in quel momento, cambia il v2.EffDate <= NOW()
nella sottoquery a v2.EffDate <= :DateOfInterest
.
Maggiori dettagli possono essere trovati in una presentazione di diapositive qui e in un documento non del tutto completato qui.
Per mostrare un po' dell'estendibilità del design, nota che c'è un IsWorking
indicatore nella tabella delle versioni e una data di fine nella tabella statica. Quando un dipendente lascia l'azienda, nella tabella statica viene inserita l'ultima data e una copia dell'ultima versione con IsWorking
impostato su false
viene inserito nella tabella delle versioni.
È abbastanza comune che i dipendenti lascino un'azienda per un po' e poi vengano assunti di nuovo. Con solo la data nella tabella statica, la voce può essere riattivata semplicemente riportando quella data a NULL. Ma una query "guarda indietro" per qualsiasi momento in cui la persona non era più un dipendente restituirebbe un risultato. Non ci sarebbe stata alcuna indicazione che avessero lasciato l'azienda. Ma una versione con IsWorking
=false quando si lascia l'azienda e IsWorking
=true al rientro in azienda consentirà una verifica di tale valore al momento dell'interesse e ignorerà i dipendenti quando non erano più dipendenti anche se rientrati successivamente.