Tabele temporalne

https://xpil.eu/ekr

Ju偶 w latach 90 zesz艂ego stulecia firma Postgres pr贸bowa艂a zrealizowa膰 ide臋 tabel temporalnych, jednak ze wzgl臋du na bardzo wysokie koszty sk艂adowania ogromnych ilo艣ci danych pomys艂 zarzucono.

W roku 2013 tabele temporalne trafi艂y do kolejnej wersji specyfikacji j臋zyka SQL.

Od niedawna wszyscy wi臋ksi dostawcy baz danych przej臋li si臋 tematem i zacz臋li implementowa膰 t臋 opcj臋 w swoich produktach. Jak na razie uda艂o si臋 to kilku firmom: Oracle, Postgres, Teradata, IBM DB2, Microsoft, Cockroach DB oraz MarkLogic (ten ostatni nie jest baz膮 stricte SQL-ow膮, ale mimo wszystko zaimplementowali "temporalno艣膰" w swoim produkcie).

Na czym polega trick?

Trick polega na tym, 偶e w zapytaniu typu SELECT mo偶emy doda膰 na ko艅cu operator AS OF, a zaraz po nim poda膰 przedzia艂 czasu (w szczeg贸lno艣ci: punkt w czasie), z kt贸rego chcemy dosta膰 wyniki.

Czyli taka jakby maszyna czasu.

Oczywi艣cie ZANIM zaczniemy u偶ywa膰 tej sk艂adni, musimy najpierw w艂膮czy膰 "temporalno艣膰" dla danej tabeli.

Jak to wygl膮da w praktyce?

No c贸偶. R贸偶ni dostawcy zrealizowali to lepiej lub gorzej. Poka偶臋 przyk艂ad Microsoftu - mo偶e nie najbardziej elegancki, ale najbli偶szy memu bazylowemu sercu. Uwaga: poni偶szy przyk艂ad jest bardzo uproszczony wzgl臋dem tego, co utworzyliby艣my w 艣wiecie rzeczywistym. Chc臋 tylko pokaza膰 sam mechanizm.

CREATE TABLE dbo.Ksiazki
(
	ID BIGINT NOT NULL PRIMARY KEY CLUSTERED
	, TYTUL NVARCHAR(5700) NOT NULL
	, ISBN CHAR(13) NOT NULL
	, WYDANIE int NOT NULL
	, WAZNE_OD DATETIME2 NOT NULL
	, WAZNE_DO DATETIME2 NOT NULL
	, PERIOD FOR SYSTEM TIME(WAZNE_OD, WAZNE_DO)
)
WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.KsiazkiHist));

Powy偶sze zapytanie utworzy dwie tabele: Ksiazki oraz KsiazkiHist. Dzi臋ki temu, 偶e u偶yli艣my s艂贸wka PERIOD na ko艅cu listy kolumn, a tak偶e w艂膮czyli艣my wersjonowanie ("WITH (SYSTEM_VERSIONING = ON..."), od tej pory wszelkie zmiany do tabeli Ksiazki b臋d膮 automatycznie rejestrowane w tabeli KsiazkiHist. Ale to nie wszystko.

Ot贸偶 sk艂adnia j臋zyka SQL zosta艂a odrobin臋 zmodyfikowana w celu uproszczenia wyci膮gania danych historycznych. Zamiast jawnie wysy艂a膰 zapytanie do tabeli KsiazkiHist, mo偶emy zrobi膰 tak:

SELECT * from Ksiazki
AS OF '2016-07-02';

Takie zapytanie zwr贸ci nam wszystkie rekordy z tabeli Ksiazki, kt贸re istnia艂y tam o p贸艂nocy drugiego lipca 2016.

Inny przyk艂ad:

SELECT * from Ksiazki
AS OF FROM '2016-07-02 12:34:55.000' TO '2016-07-02 12:38:51.000';

Tutaj pytamy o wszystkie rekordy, kt贸re by艂y aktywne drugiego lipca mi臋dzy godzin膮 12:34:55 a 12:38:51. To zapytanie (i wszystkie w przyk艂adach poni偶ej) mo偶e nam zwr贸ci膰 kilka rekord贸w dla tej samej ksi膮偶ki, je偶eli w podanym przedziale czasu rekord danej ksi膮偶ki by艂 modyfikowany wielokrotnie.

Jeszcze inny przyk艂ad:

SELECT * from Ksiazki
AS OF BETWEEN '2016-07-02 12:34:55.000' AND '2016-07-02 12:38:51.000';

Tutaj dostaniemy wyniki identyczne, jak w poprzednim zapytaniu, z drobn膮 (ale istotn膮) r贸偶nic膮: je偶eli kto艣 zmodyfikowa艂 dany rekord dok艂adnie o godzinie 12:38:51, zobaczymy ten zmodyfikowany rekord (w poprzednim przyk艂adzie z FROM...TO... taki rekord zostanie odrzucony z wynik贸w).

I ostatni przyk艂ad:

SELECT * from Ksiazki
AS OF CONTAINED IN ('2016-07-02 12:34:55.000', '2016-07-02 12:38:51.000');

Tu mamy istotn膮 zmian臋 wzgl臋dem poprzednich przyk艂ad贸w: zapytanie z operatorem CONTAINED IN zwr贸ci wszystkie rekordy historyczne, kt贸re zosta艂y uaktywnione a nast臋pnie zdezaktywowane w podanym przedziale czasu.

Je偶eli wi臋c zmienili艣my ksi膮偶ce o ID=34765 tytu艂 o godzinie 12:34:55.244, a nast臋pnie zmienili艣my ISBN o godzinie 12:34:55:761 i jeszcze potem numer wydania o godzinie 12:38:21.000 (i nic ponadto), w贸wczas powy偶sze zapytanie zwr贸ci dla tej ksi膮偶ki rekord zmieniony o 12:34:55.244 oraz 12:34:55:761, ale ju偶 nie ten zmieniony o 12:38:21.000 (bo nie zosta艂 on zdezaktywowany w podanym przedziale czasowym).

Na zako艅czenie odpowiem jeszcze na pytanie, kt贸re z pewno艣ci膮 ci艣nie si臋 na usta Czytelnikowi: A co w sytuacji, kiedy mamy strasznie du偶o danych historycznych, a chcemy zachowa膰 tylko histori臋 z, dajmy na to, ostatniego roku?

No c贸偶. Tutaj te偶 implementacji jest kilka, jedne lepsze, inne gorsze. W Cockroach DB na ten przyk艂ad definiujemy sobie po prostu maksymalny okres, w kt贸rym dane "temporalne" maj膮 by膰 przechowywane i gotowe - silnik bazy "sam" dba o to, 偶eby kasowa膰 "stare" dane. Bardzo higienicznie.

A jak to zrobi艂 Microsoft?

Troch臋 powi膮za艂 zagadnienie sznurkami i pozbija艂 starymi, zardzewia艂ymi gwo藕dziami. Czyszczenie danych historycznych z tabel temporalnych odbywa si臋 w spos贸b nast臋puj膮cy:

1. Wy艂膮czamy temporalno艣膰 dla danej tabeli:

ALTER TABLE dbo.Ksiazki SET (SYSTEM_VERSIONING = OFF);

2. Kasujemy dane historyczne (tyle, ile potrzebujemy):

DELETE FROM dbo.Ksiazki WHERE WAZNE_DO <= '2015-07-05';

3. W艂膮czamy z powrotem temporalno艣膰:

ALTER TABLE dbo.Ksiazki SET (SYSTEM_VERSIONING = ON (HISTORY_TABLE  = dbo.KsiazkiHist, DATA_CONSISTENCY_CHECK = OFF));

Jak wida膰 w punkcie 3. musimy przy ponownym w艂膮czaniu temporalno艣ci wy艂膮czy膰 sprawdzanie sp贸jno艣ci danych w tabeli historycznej.

Troch臋 to pachnie 艣redniowieczem, ale skoro dzia艂a, nie b臋d臋 narzeka艂...

https://xpil.eu/ekr

1 Comment

Leave a Comment

Komentarze mile widziane.

Je偶eli chcesz do komentarza wstawi膰 kod, u偶yj sk艂adni:
[code]
tutaj wstaw sw贸j kod
[/code]

Je偶eli zrobisz liter贸wk臋 lub zmienisz zdanie, mo偶esz edytowa膰 komentarz po jego zatwierdzeniu.