COMPUTERWORLD
pod kapotou
Integritní omezení

Již ve čtvrtém pokračování Databázové abecedy jsme se věnovali referenční integritě, jako speciálnímu případu integritních omezení (IO). Připomeňme, že IO jsou tvrzení, která mají platit o datech v databázi. Samozřejmě, že by měla být v úzkém vztahu ke tvrzením na konceptuální úrovni, která platí o objektech světa aplikace. Při tvorbě IS se tedy setkáme s problémem, jak IO na konceptuální úrovni přenést do IO na databázové úrovni.

V dnešních možnostech, které nabízí standard SQL92 získáváme možnost specifikovat IO neprocedurálně přímo v definici databáze. Protože tato možnost z logického hlediska prakticky nepřekračuje možnosti příkazu SELECT, tj. v principu relační úplnost, je zřejmé, že jistá IO musí být “naprogramována”. To se v horším případě dělá na úrovni aplikačních programů, v lepším případě pomocí uložených procedur, pravidel, či nejlépe triggerů, které dokonce umožňují řešit “zotavení z chyb”, tj. specifikovat akce, které je třeba při narušení integrity databáze automaticky provést.

V dnešní sloupci se budeme věnovat neprocedurálním možnostem, která SQL nabízí. Protože platným standardem je SQL92, budeme využívat možnosti tohoto standardu, přestože jeho implementace je dosud běžnými databázovými produkty naplněna pouze částečně.

Z logického hlediska je IO tvrzení vyjádřitelné např. formulí predikátového kalkulu 1. řádu. V SQL lze k IO přistupovat strukturovaněji. IO lze rozdělit podle toho, vedou-li ke specifikaci:

  • domény
  • sloupce
  • řádku
  • tabulky(zahrnují i specifikaci primárního klíče)
  • vztahů mezi tabulkami

Začneme od domény. Podstatnou novinkou v SQL92 je možnost definovat doménu, tj. jako množinu hodnot ve smyslu původního Coddova RMD, pomocí CREATE DOMAIN, a tu pak použít v definici sloupce tabulky.

CREATE DOMAIN LETOS IS DATE

DEFAULT 1996-12-31

CHECK (VALUE > 1996-01-01 AND VALUE < 1996-12-31)

CREATE TABLE VÝPŮJČKY_FILMŮ

(Č_KOPIE INTEGER NOT NULL,

NÁZEV_KINA CHARACTER(20),

CENA DECIMAL (4,2) NOT NULL CHECK(CENA > = 100),

DATUM_VRÁCENÍ LETOS,

PRIMARY KEY(Č_KOPIE) )

Podpora domén je ovšem slabá. Melton a Simon ve své knize Understanding the New SQL: A Complete Guide (Morgan Kaufmann, 1993) nazývají domény SQL92 výstižně pseudo-typy. Jde vlastně o pouhé makra, která přibližují datový model SQL původnímu RMD. Na jedné straně je užitečné mít pouze jeden zápis definice domény, vyskytuje-li se ve více sloupcích tabulek, na druhé straně tu ale není podpora silné kontroly typů, např. při spojování tabulek. Ta se odehrává na základních typech.

IO pro sloupce tabulky zahrnuje možnost blíže specifikovat povolené hodnoty pro daný sloupec. Vedle již zmiňované referenční integrity lze specifikovat NOT NULL (neprázdná hodnota v každém řádku), UNIQUE (jedinečnost hodnot v rámci sloupce) a CHECK (rozsah hodnot). UNIQUE není v SQL92 podmíněno použitím NOT NULL, jak je tomu běžné u současných databázových produktů, tj. s UNIQUE, není-li řečeno NOT NULL, se může v daném sloupci vyskytnout jedna hodnota NULL. Pro úplnost dodejme, že lze specifikovat i DEFAULT hodnotu sloupce.

V definici tabulky je možné explicitně pojmenovat IO za účelem pozdějšího odkazování na ně např. v příkazu ALTER nebo při odložení kontroly IO klauzulí SET CONSTRAINTS OFF. (Připomeňme, že příkazy SET CONSTRAINTS OFF a SET CONSTRAINTS ON umožňují v transakci zajistit potlačení či aktivaci kontroly daného IO.) IO pro sloupec CENA lze pojmenovat, tj. lze psát např.

CENA DECIMAL (4,2) CONSTRAINT IOCENA NOT NULL CHECK(CENA > = 100),

a zacházet později přímo s IOCENA.

Významným prvkem SQL92 je rozšíření klauzule CHECK. Za CHECK se může vyskytnout složitější výraz obsahující odkazy i na jiné tabulky. IO bude splněno tehdy, nabude-li podmínka za CHECK hodnoty TRUE nebo vede k hodnotě “nedefinováno”. Uvažujme tabulky FILM a PŘEDSTAVENÍ. IO "Nebude se dávat žádný film režírovaný Troškou" lze formulovat v tabulce PŘEDSTAVENÍ pro atribut JMÉNO_F jako

CHECK (JMÉNO_F < > ANY

(SELECT JMÉNO_F

FROM FILM

WHERE REŽISÉR = ¢ Troška¢ ) )

Nepojmenovanou či pojmenovanou (s CONSTRAINT) klausuli CHECK lze usadit za definici sloupců. Toho lze využít spíše pro IO týkající se řádku tabulky nebo tabulky. Podobně je možné použít UNIQUE, chceme-li definovat alternativní složené klíče, či PRIMARY KEY, jde-li o složený primární klíč. Pro úplnost dodejme, že za definicemi atributů lze definovat i referenční integritu, což má největší smysl tehdy, jsou-li cizí klíče složené.

Speciálním důsledkem pojetí IO v SQL (CONSTRAINTS a všechna ostatní) je, že musí být splněna i pro prázdnou relaci. Jistě je smysluplné tvrdit, že když se nehraje žádný film, pak ani ne od Trošky. To ovšem může vést k nežádoucím efektům. Představme si IO “Vždy se dává nějaký film”. Vyjádřeme IO v SQL jako

CONSTRAINT PŘEDSTAVENÍ_VŽDY

CHECK (SELECT COUNT(* ) FROM PŘEDSTAVENÍ) > 0)

Toto IO bude ale nabývat hodnoty TRUE i tehdy, když relace PŘEDSTAVENÍ bude prázdná. Tento nevhodný efekt se stal motivací pro formulaci IO mimo relace, tj. asi tak, jak jsou chápána v základní definici RMD. Připomeňme, že tam se schéma relační databáze skládá z množiny schémat relací a množiny IO. V SQL92 lze specifikovat tvrzení (ASSERTION) pomocí CREATE ASSERTION. Jde opět o pojmenované IO formulované za svým jménem pomocí CHECK Na rozdíl od předchozích možností nenabývá automaticky TRUE na prázdné tabulce!

Tím ovšem dochází k jisté redundanci zápisů IO. Např. NOT NULL je speciálním případem použití CHECK na jeden sloupec. Možnosti CHECK jsou zase speciálním případem ASSERTION.

Osamostatnění IO pomocí ASSERTION klade ovšem zvýšené nároky na implementaci. Teoreticky jde vlastně o problém, co přesně je třeba kontrolovat, dojde-li k aktualizaci databáze. Zatímco u IO týkajících se sloupce či jedné tabulky jde o problém řešitelný celkem úspěšně ve většině současných relačních SŘBD, “mezitabulkové” vztahy dané IO mohou být natolik spletité, že naivní kontrola databáze při aktualizaci by mohla vést k ochromení jejího provozu. Zdá se, že s implementací ASSERTION (ale ani CHECK s odkazem na více tabulek) nikdo příliš nespěchá.

Současné implementace SŘBD se zatím zaměřily hlavně na implementaci referenční integrity. Přístup i k těm nejzákladnějším IO je dost různý. Např. u většiny SŘBD se pro atributy primárního klíče automaticky vytváří index (ne však u SQLBase). Některé systémy však umožňují vytvořit na tytéž atributy ještě další index pomocí CREATE INDEX. Domény zatím nejsou podporovány, tj. vše o sloupci se musí vejít do definice sloupce v CERATE TABLE.

Jistou nevýhodou deklarativních (neprocedurálních) IO v SQL jako takovém je slabý aparát pro reakci na chybu. Ta musí být většinou ošetřena v dalších programech. Podstatně větší možností nabízí koncepce triggeru. A ten si necháme na příště.



<seznam dílů seriálu>   <COMPUTERWORLD>