Има техника, наречена версия на версиите, която съществува от много години, но до голяма степен е неизпълнима по няколко причини. Въпреки това, има подобна техника, която наричам Version Normal Form, която намирам за много полезна. Ето пример за използване на таблица за служители.
Първо се създава статичната таблица. Това е основната таблица с обекти и съдържа статични данни за обекта. Статичните данни са данни, които не се очаква да се променят по време на живота на субекта, като например рождена дата.
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
);
Важно е да разберете, че има един запис за всеки служител, точно както при всяка такава таблица.
След това свързаната таблица с версии. Това установява 1-метрова връзка със статичната таблица, тъй като може да има няколко версии за един служител.
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 )
);
В бележката към таблицата с версиите има дата на влизане в сила, но не съответстващо вече неефективно поле. Това е така, защото след като една версия влезе в сила, тя остава в сила, докато не бъде заменена от следващата версия. Комбинацията от ID и EffDate трябва да е уникална, така че не може да има две версии за един и същ служител, които да са активни едновременно, нито да има разлика между времето, когато едната версия приключва и когато стартира следващата версия.
Повечето заявки ще искат да знаят текущата версия на данните за служителите. Това се осигурява чрез присъединяване на статичния ред за служителя с версията, която е в сила в момента. Това може да бъде намерено със следната заявка:
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;
Това връща една и единствена версия, стартирана в най-близкото минало. Използване на неравенството <=в проверката на датата (v2.EffDate <= NOW()
) позволява дати на влизане в сила в бъдеще. Да предположим, че знаете, че нов служител ще започне на първия ден от следващия месец или е насрочено увеличение на заплатата за 13-то число на следващия месец, тези данни могат да бъдат въведени предварително. Такива „предварително заредени“ записи ще бъдат игнорирани.
Не позволявайте на подзаявката да стигне до вас. Всички полета за търсене са индексирани, така че резултатът е доста бърз.
Има голяма гъвкавост с този дизайн. Заявката по-горе връща най-новите данни за всички служители, настоящи и минали. Можете да проверите TermDate
поле, за да получите само настоящи служители. Всъщност, тъй като много места във вашите приложения ще се интересуват само от текущата информация на настоящите служители, тази заявка ще направи добър изглед (пропуснете крайното where
клауза). Няма нужда приложенията дори да знаят, че съществуват такива версии.
Ако имате конкретна дата и искате да видите данните, които са били в сила по това време, променете v2.EffDate <= NOW()
в подзаявката към v2.EffDate <= :DateOfInterest
.
Повече подробности можете да намерите в слайд презентация тук и не съвсем завършен документ тук.
За да покажете малко от разширяемостта на дизайна, забележете, че има IsWorking
индикатор в таблицата с версиите, както и дата на прекратяване в статичната таблица. Когато служител напусне компанията, последната дата се вмъква в статичната таблица и копие на най-новата версия с IsWorking
зададен на false
се вмъква в таблицата с версиите.
Доста обичайно е служителите да напуснат компания за известно време, след което да бъдат наети отново. Само с датата в статичната таблица, записът може да бъде активиран отново, само като зададете тази дата обратно на NULL. Но заявка „поглед назад“ за всеки момент, когато лицето вече не е служител, ще върне резултат. Нямаше да има индикация, че са напуснали компанията. Но версия с IsWorking
=false при напускане на фирмата и IsWorking
=true при връщане в компанията ще позволи проверка на тази стойност в момента на интерес и ще игнорира служителите, когато вече не са били служител, дори ако се върнат по-късно.