Existuje technika zvaná verzování, která existuje již mnoho let, ale z několika důvodů je z velké části nepoužitelná. Existuje však podobná technika, kterou nazývám Version Normal Form a kterou jsem shledal jako velmi užitečnou. Zde je příklad s použitím tabulky Zaměstnanci.
Nejprve je vytvořena statická tabulka. Toto je hlavní tabulka entit a obsahuje statická data o entitě. Statická data jsou data, u kterých se neočekává, že by se během životnosti entity změnily, jako je datum narození.
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
);
Je důležité si uvědomit, že pro každého zaměstnance existuje jedna položka, stejně jako u každé takové tabulky.
Potom přidružená tabulka verzí. Tím se vytvoří 1-metrový vztah se statickou tabulkou, protože pro zaměstnance může existovat několik verzí.
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 )
);
V poznámce k tabulce verzí je datum účinnosti, ale ne odpovídající pole, které již není účinné. Důvodem je to, že jakmile verze vstoupí v platnost, zůstane v platnosti, dokud nebude nahrazena následující verzí. Kombinace ID a EffDate musí být jedinečná, takže nemohou existovat dvě verze pro stejného zaměstnance, které jsou aktivní ve stejnou dobu, ani nemůže být mezera mezi koncem jedné verze a zahájením verze další.
Většina dotazů bude chtít znát aktuální verzi dat zaměstnanců. To je zajištěno spojením statického řádku pro zaměstnance s verzí, která je nyní platná. To lze nalézt pomocí následujícího dotazu:
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;
Tím se vrátí jediná verze, která začala v poslední minulosti. Použití nerovnosti <=v kontrole data (v2.EffDate <= NOW()
) umožňuje datum účinnosti v budoucnosti. Předpokládejme, že víte, že nový zaměstnanec nastoupí první den příštího měsíce nebo že je na 13. dne příštího měsíce naplánováno zvýšení platu, lze tato data vložit předem. Takové "předem načtené" položky budou ignorovány.
Nedovolte, aby se k vám poddotaz dostal. Všechna vyhledávací pole jsou indexována, takže výsledek je poměrně rychlý.
Tento design nabízí velkou flexibilitu. Výše uvedený dotaz vrací nejnovější data všech zaměstnanců, současných i minulých. Můžete zkontrolovat TermDate
pole získat právě přítomné zaměstnance. Ve skutečnosti, protože mnoho míst ve vašich aplikacích se bude zajímat pouze o aktuální informace o současných zaměstnancích, by tento dotaz poskytl dobrý přehled (vynechejte poslední where
doložka). Aplikace nemusí ani vědět, že takové verze existují.
Pokud máte konkrétní datum a chcete zobrazit data, která byla v tu dobu platná, změňte v2.EffDate <= NOW()
v dílčím dotazu na v2.EffDate <= :DateOfInterest
.
Více podrobností lze nalézt v prezentaci zde a ne zcela dokončený dokument zde.
Chcete-li předvést trochu rozšiřitelnosti designu, všimněte si, že existuje IsWorking
indikátor v tabulce verzí a také datum ukončení ve statické tabulce. Když zaměstnanec opustí společnost, do statické tabulky se vloží poslední datum a kopie nejnovější verze s IsWorking
nastavit na false
se vloží do tabulky verzí.
Je poměrně běžné, že zaměstnanci na nějakou dobu odejdou z firmy a pak jsou znovu přijati. Pouze s datem ve statické tabulce lze položku znovu aktivovat nastavením tohoto data zpět na NULL. Ale dotaz "ohlédnutí zpět" pro každou dobu, kdy osoba již nebyla zaměstnancem, by vrátil výsledek. Nic nenasvědčuje tomu, že ze společnosti odešli. Ale verze s IsWorking
=false při odchodu ze společnosti a IsWorking
=true při návratu do společnosti umožní kontrolu této hodnoty v době zájmu a ignoruje zaměstnance, kteří již nebyli zaměstnancem, i když se vrátili později.