Tak²ka po dvou letech zkoumání moºností SQL databází se dostáváme aº do samého finále.
Záv╪reƒn∞ sprint
Neº se pustíme do dokonƒení projektu OZNAMKA, rád bych popsal obecné moºnosti ²ízení v∞poƒtu uvnit² procedur. Nejdàleºit╪jτí je pojem bloku. BLOK je skupina p²íkazà sev²en∞ch mezi BEGIN a END. Uvnit² procedury je vºdy jeden blok tvo²ící její t╪lo. Bloky se mohou téº zahníz╘ovat a vytvá²et tak struktury. Posloupnost p²íkazà uzav²ená do bloku se totiº nejen v SQL chová jako jeden p²íkaz. Toho vyuºíváme jak p²i v╪tvení, tak p²i cyklech. P²íkaz v╪tvení má dva obecné tvary:
IF logick∞ v∞raz
THEN p²íkaz proveden∞ nejv∞τe jednou
IF logick∞ v∞raz
THEN p²íkaz proveden∞ p²i spln╪ní podmínky
ELSE opaƒn∞ p²íkaz
Logické v∞razy a p²íkazy uº známe dávno a bloky usnadní konverzi posloupnosti p²íkazà na jeden p²íkaz.
Pro cyklus s testováním podmínky p²ed zapoƒetím práce se pouºívá schéma:
WHILE logick∞ v∞raz
DO p²íkaz opakovan∞ n╪kolikrát
Pro cyklus p²es vτechny ²ádky tabulky urƒené p²íkazem SELECT se pouºívá schéma:
FOR select p²íkaz
DO p²íkaz aplikovan∞ na ²ádek selectu
Pro oτet²ení chyb pouºijeme schéma:
WHEN ANY
DO p²íkaz ƒíhající na chybu
Pro p²edƒasn∞ v∞chod z procedury pouºijeme p²íkaz EXIT.
Pouºití cyklu, bloku a oτet²ení v∞jimek je uvedeno v následujících dvou procedurách. Procedura GAUSS ²eτí Gaussàv τkoláck∞ problém seƒtení ƒísel od 1 do N. Procedura PRUSVIH ²eτí stejn∞ problém, ale hledá nejvyττí moºné ƒíslo, pro které se v∞poƒet jeτt╪ nezhroutí. Vτimn╪te si práce s bloky a lokálními prom╪nn∞mi:
CREATE PROCEDURE GAUSS (N INTEGER) RETURNS (S INTEGER)
Pod vlivem p²edchozího v∞kladu uº snadno vytvo²íme pot²ebné uloºené procedury. Zaƒneme s tabulkou CLOVEK, pro jejíº údrºbu je t²eba τest procedur. Procedura NOVYCLOVEK umoºσuje p²idat nového ƒlov╪ka a p²itom hlídat nenulovost rodného ƒísla, jména a p²íjmení spolu s unikátností rodného ƒísla:
IF NOT EXISTS(SELECT RC FROM CLOVEK WHERE RC=:RCX) AND :JX IS NOT NULL
AND :PX IS NOT NULL AND :RCX IS NOT NULL
THEN INSERT INTO CLOVEK(RC,JMENO,PRIJMENI,KOEUD,KOEOB)
VALUES (:RCX,:JX,:PX,1.0,1.0);
END
Procedu²e NOVYCLOVEK zb∞vá k dokonalosti uº jen logická kontrola rodného ƒísla :RCX, kterou ponechávám ƒtená²i k nedatabázov∞m úvahám. ¼lov╪ka màºeme zruτit, pokud jeτt╪ neudával, respektive nebyl udán tak, jak je uvedeno v procedu²e ZRUSCLOVEK:
CREATE PROCEDURE ZRUSCLOVEK(RCX VARCHAR(10))
AS
BEGIN
IF NOT EXISTS(SELECT RCUD FROM UDANI WHERE RCUD=:RCX OR RCOB=:RCX)
THEN DELETE FROM CLOVEK WHERE RC=:RCX;
END
Zm╪na jména nebo p²íjmení je proti tomu maliƒkost, jak vidíme v procedurách ZMENJMENO a ZMENPRIJMENI:
THEN UPDATE CLOVEK SET PRIJMENI=:PX WHERE RC=:RCX;
END
N╪které ob╪ti mají prominentní postavení a za jejich udání náleºí p²íplatek urƒen∞ koeficientem KOEX v╪tτím neº jedna nebo rovn∞m jedné. Podobn╪ prominentní udavaƒ bere za své sluºby více. Procedury JAKOOBET a JAKOUDAVAC se hodí pro aktualizaci p²íplatkà:
Nyní se budeme zab∞vat údrºbou tabulky CIN, na kterou staƒí pouºít ƒty²i procedury. Nejzajímav╪jτí z nich je procedura NOVYCIN, která sama urƒuje hodnotu nového primárního klíƒe CIC a kontroluje unikátnost názvu ƒinu. Je to praktická ukázka nepot²ebnosti p²íkazà TRIGGER p²i dàsledném postupu aktualizace:
CREATE PROCEDURE NOVYCIN (NX VARCHAR(30))
AS
DECLARE VARIABLE CIX INTEGER;
BEGIN
SELECT MAX(CIC) FROM CIN INTO :CIX;
IF :CIX IS NULL
THEN :CIX=1;
ELSE :CIX=:CIX+1;
IF NOT EXISTS(SELECT CIC FROM CIN WHERE NAZEVC=:NX) AND :NX IS NOT NULL
THEN INSERT INTO CIN(CIC,NAZEVC,CENAC) VALUES(:CIX,:NX,0.0 );
END
Procedura ZRUSCIN ruτí jen takov∞ ƒin, na kter∞ jeτt╪ nep²iτlo udání:
CREATE PROCEDURE ZRUSCIN(CIX INTEGER)
AS
BEGIN
IF NOT EXISTS(SELECT CICINU FROM UDANI WHERE CICINU=:CIX )
THEN DELETE FROM CIN WHERE CIC=:CIX;
END
Aktualizace názvu ƒinu musí b∞t domyτlena tak, aby nedoτlo k duplicit╪ názvà. ⁿeτení tohoto problému je vid╪t v procedu²e NAZEVCINU:
Poslední t²i procedury se budou hodit pro aktualizaci tabulky UDANI. Tabulka má sloºen∞ primární klíƒ a t²i cizí klíƒe. Nechceme-li spoléhat na integritní omezení, coº d╪láme od samého zaƒátku, musíme vτe p²edem otestovat:
IF NOT EXISTS(SELECT DEN FROM UDANI WHERE DEN=:DX AND RCUD=:UX AND RCOB=:OX
AND CICINU=:CX) AND :DX IS NOT NULL AND :UX IS NOT NULL AND :OX IS NOT NULL
AND :CX IS NOT NULL AND EXISTS(SELECT RC FROM CLOVEK WHERE RC=:UX)
AND EXISTS(SELECT RC FROM CLOVEK WHERE RC=:OX)
AND EXISTS(SELECT CIC FROM CIN WHERE CIC=:CX)
AND NOT(:UX=:OX)
THEN INSERT INTO (DEN,RCUD, RCOB,CICINU) VALUES(:DX,:UX,:OX, :CX );
END
Uvedenou d²inu si màºeme uτet²it pomocí WHEN ANY, které umoºní oτet²it p²ípadné chyby. Podobn∞m trikem by bylo moºné zjednoduτit i n╪které p²edchozí procedury, coº je necháno jako cviƒení. Následuje elegantn╪jτí verze procedury NOVEUDANI:
INSERT INTO (DEN,RCUD,RCOB,CICINU) VALUES(:DX,:UX,:OX,:CX );
WHEN ANY
DO EXIT;
END
N╪kdy je t²eba zahladit vτechny stopy po udavaƒi a jeho stupidní práci. Procedura ZAHLADUDAVACE nejprve zruτí vτechna udání, která uƒinil. Není-li zároveσ ob╪tí jin∞ch udavaƒà, zmizí i z tabulky CLOVEK:
CREATE PROCEDURE ZAHLADUDAVACE(RCX VARCHAR(10))
AS
BEGIN
DELETE FROM UDANI WHERE RCUD=:RCX;
IF NOT EXISTS(SELECT RCOB FROM UDANI WHERE RCOB=:RCX )
THEN DELETE FROM CLOVEK WHERE RC=:RCX;
END
Obdobn╪ pomocí procedury ZAHLADOBET zmizí beze stop ob╪£:
CREATE PROCEDURE ZAHLADOBET(RCX VARCHAR(10))
AS
BEGIN
DELETE FROM UDANI WHERE RCOB=:RCX;
IF NOT EXISTS(SELECT RCUD FROM UDANI WHERE RCUD=:RCX )
THEN DELETE FROM CLOVEK WHERE RC=:RCX;
END
Uºivatelàm databáze OZNAMKA staƒí zve²ejnit seznam názvà, v∞znamà a sloupcà vτech view a seznam názvà, v∞znamà a parametrà vτech uloºen∞ch procedur. P²ísluτné pohledy UDANICKO, UDAVAC, OBET, PRECIN, RCUDAV, RCOBET a MEDAILE nám umoºní dokonalé v∞stupy informací v reálném ƒase a v poºadovaném tvaru. Uloºené procedury NOVYCLOVEK, ZRUSCLOVEK, ZMENJMENO, ZMENPRIJMENI, JAKOOBET, JAKOUDAVAC, NOVYCIN, ZRUSCIN, NAZEVCINU, CENACINU, NOVEUDANI, ZAHLADUDAVACE a ZAHLADOBET poskytnou moºnost bezpeƒné aktualizace databáze. Jak to vτechno funguje a kolik tabulek je schováno "pod povrchem", se nedozví ani uºivatel, ani tvàrce klientské aplikace, pokud mu to n╪kdo nevykecá. Pomocí "ƒínské zdi" z procedur a view je tvrd╪ odd╪len vnit²ní systém realizující p²ísluτné know-how analytika od vn╪jτího prostoru ²adov∞ch realizátorà klientsk∞ch aplikací a nehrozí nebezpeƒí "brigádnického efektu", kdy kaºd∞ inteligentní prázdninov∞ brigádník pochopí vτe pot²ebné ke konkurenƒní ƒinnosti po prázdninách.
Slovo záv╪rem
Ten, kdo pozorn╪ ƒetl cel∞ seriál od τedé a flekaté plastelíny v Chipu 6/98 a pochopil normalizaci tabulek, relace 1:N, integritní omezení, tabulky, domény, indexy, primární, unikátní a cizí klíƒe, moºnosti p²íkazu select a v∞hody view a procedur, ten se jist╪ p²i samostudiu seznámí se zbyl∞mi moºnostmi SQL. Rád bych v prvé ²ad╪ uvedl, proƒ se v seriálu nezab∞vám problematikami TRIGGER a GRANT, které jsou chloubou kaºdého manuálu ke konkrétnímu SQL serveru. Ten, kdo dob²e navrhl databázov∞ systém a data aktualizuje zásadn╪ pomocí uloºen∞ch procedur, objektivn╪ TRIGGER nepot²ebuje. Ledaºe by se cht╪l pochlubit, ºe jej zná nebo se pokouτí velmi moderním zpàsobem automaticky korigovat svàj vlastní chaos. Naproti tomu zajiτt╪ní ràzn∞ch p²ístupov∞ch práv ràzn∞m uºivatelàm pomocí GRANT je ƒinnost pro zdárn∞ chod aplikací na SQL serveru zcela zásadní. Na druhé stran╪ je tak jednoduchá, ºe ji snadno pochopí kaºd∞, kdo doƒetl aº sem. Nejlépe GRANT chápou ti, kdo by nejrad╪ji vτechno vτem ostatním uºivatelàm zakázali. I zde platí vτeho s mírou, a proto by zákazy nem╪ly ochromit rutinní provoz databáze. Pokud vás práv╪ konƒící seriál p²ivedl do rozpakà, pak splnil svàj úƒel. V takovém rozpoloºení nejspíτ n╪co nového dobrého vytvo²íte nebo n╪co starého koneƒn╪ p²ed╪láte tak, aby to fungovalo lépe. Aº budete z klientu posílat SQL dotazy, màºe se stát, ºe nep²ijde kvalitní odpov╪╘, i kdyº je vτechno dob²e navrºeno. Pak patrn╪ nekladete dotaz na SQL server, ale pouze na jeho model v n╪jakém jiném prost²edí. Za dobu svého ƒty²letého SQL misioná²ství mi jen n╪kolik lidí vynadalo, ºe jsem je p²ivedl ke τpatné ví²e. Ve skuteƒnosti nepracovali s daty na SQL serveru, ale se soubory ve formátu DBF ƒi MDB. Jejich potíºe se t∞kaly sdílení dat nebo práce s velk∞m mnoºstvím poloºek v jedné tabulce.