22/1/2009 Úvod do databáz, skúškový test, max 25 bodov, 90 min Riešenia tohto testu sú písané pedagogicky, preto sú relatívne dlhé. (Samozrejme, pri riešení úloh nebolo treba zachádzať až do takých detailov...) 0. Súhlasím so zverejnením výsledku môjho testu vo forme [Meno, Výsledok] na webstránke prednášky. ÁNO (1), NIE (0). ÁNO 1. Daná je databáza: capuje(krcma, Alkohol, Cena), navstivil(idn, Pijan, Krcma, Od), lubi(pijan, Alkohol), vypil(idn, Alkohol, Mnozstvo). a) Skrblík je pijan, ktorý si pri každej návšteve krčmy všimne a zapamätá ceny všetkých alkoholov, ktoré tá krčma čapuje. Pri návšteve krčmy je ochotný (ale nemusí) vypiť len najlacnejší alkohol, ktorý tá krčma čapuje a ktorý on zároveň ľúbí a aj to len vtedy, ak zatiaľ nepozná (t.j. predtým nenavštívil) krčmu, ktorá ten alkohol čapuje lacnejšie. Sformulujte v Datalogu (2) a v SQL (2) dotaz, ktorý nájde všetkých pijanov, ktorí sa chovajú ako skrblíci. Prirodzene, aj abstinenti sú skrblíci. (Atribút Od je časová pečiatka návštevy, teda skutočný čas, kedy návšteva začala.) Datalog: /* Skrblík je opakom frajerského pijana, ktorý sa aspoň raz nezachoval ako skrblík. Asi by nebolo celkom fair nejakého pijana nazvať skrblíkom ešte pred jeho prvou navštevou krčmy; fair je dať mu aspoň jednu šancu sa zachovať frajersky. */ answer(p) navstivil(_, P, _, _), not frajer(p). /* Frajer mohol porušiť skrblícke zásady buď tým, že niekedy vypil alkohol, ktorý neľúbi... */ frajer(p) navstivil(i, P, _, _), vypil(i, A, _), not lubi(p, A).
/*... alebo tým, že vypil v niektorej krčme iný ako najlacnejší alkohol... */ frajer(p) navstivil(i, P, K, _), vypil(i, A, _), capuje(k, A, C), capuje(k, _, C2), C2 < C. /*... alebo tým, že vypil nejaký alkohol, pričom niekedy predtým navštívil krčmu, v ktorej je ten alkohol lacnejší. Iné zásady skrblík nemá (resp. o ďalších nevieme). */ frajer(p) navstivil(i, P, K, Od), vypil(i, A, _), capuje(k, A, C), navstivil(_, P, K2, Od2), Od2 < Od, capuje(k2, A, C2), C2 < C. SQL: /* Návod z prednášky je excelentný, stačí čítať krásny Datalog a rýchlo písať. (Som presvedčený, že porozumenie nasledujúcemu SQL textu je pre človeka náročnejšie než porozumenie predošlému textu v Datalogu. A platí to o všetkých ekvivalentných zápisoch tohto dotazu v SQL. A nielen tohto dotazu...) */ create temporary table frajer as select N.pijan from navstivil N, vypil V where N.idn = V.idn and not exists ( select * from lubi L where L.pijan = N.pijan and L.alkohol = V.alkohol) union select N.pijan from navstivil N, vypil V, capuje C, capuje C2 where N.idn = V.idn and N.krcma = C.krcma and N.krcma = C2.krcma and V.alkohol = C.alkohol and C2.cena < C.cena union select N.pijan from navstivil N, vypil V, capuje C, navstivil N2, capuje C2 where N.idn = V.idn and N.pijan = N2.pijan and N.krcma = N2.krcma and N2.od < N.od and V.alkohol = C.alkohol and V.alkohol = C2.alkohol and C2.cena < C.cena and N2.krcma = C2.krcma
/* main */ select N.pijan from navstivil N where not exists ( select * from frajer F where N.pijan = F.pijan)
b) Sformulujte nasledujúci dotaz v relačnom kalkule (2), Datalogu (2), SQL (2) a relačnej algebre (2): Nájdite dvojice [Krcma, Suma], ktoré hovoria, koľko peňazí v tej krčme celkovo prepili pijani, ktorí tú krčmu navštívili viac než stokrát. Dvojice s nulovou sumou nemajú byť vo výsledku. Datalog: /* Toto počíta dvojice [P, K] také, že pijan P navštívil krčmu K viac než stokrát. */ navstivil100(p, K) navstivil(_, P, K, _), subtotal(navstivil(i, P, K, _), [P, K], [count(i, Cnt)]), Cnt > 100. /* Toto počíta štvorice [I, K, A, S] také, že S je účet v krčme K za alkohol A pri návšteve I; ale len ak návštevu I absolvoval pijan, ktorý krčmu K navštívil aspoň stokrát. Štvorice s nulovým súčtom sa tu neobjavia. (Ak by to boli napríklad len dvojice [K, S], tak by sa rôzne účty z rovnakej krčmy s rovnakou sumou S zliali do jednej dvojice, čo nechceme.) */ ucet(i, K, A, S) navstivil100(p, K), navstivil(i, P, K, _), vypil(i, A, M), capuje(k, A, C), S is C * M. /* To is vyžaduje Prolog. S = C * M je OK. */ answer(k, T) ucet(_, K, _, _), subtotal(ucet(_, K, _, S), [K], [sum(s, T)]). Relačný kalkul: {[K, T]: I, A, T = sum(s) ( P Od M C /* ucet(i, K, A, S) */ ( Cnt /* navstivil100(p, K) */ ( Od, Cnt = count(i) navstivil(i, P, K, Od)) Cnt > 100 ) navstivil(i, P, K, Od) vypil(i, A, M) capuje(k, A, C) S = C * M ) }
SQL: create temporary table navstivil100 as select N.pijan, N.krcma from navstivil N group by N.pijan, N.krcma having count(n.idn) > 100 /* main (načo ďalšiu temporary table, keď sa to dá pohodlne napísať rovno) */ select N.krcma, sum(c.cena * V.mnozstvo) as suma from navstivil100 N100, navstivil N, vypil V, capuje C where N100.pijan = N.pijan and N100.krcma = N.krcma and N100.krcma = C.krcma and N.idn = V.idn and V.alkohol = C.alkohol group by N.krcma Relačná algebra: navstivil100 = Π pijan, krcma (σ count(idn) > 100 (Γ pijan, krcma, count(idn) (navstivil))) Γ krcma, sum(cena * mnozstvo) (navstivil100 navstivil vypil capuje) 2. Do ktorej triedy sériovateľnosti a obnoviteľnosti patria rozvrhy generované striktným 2-fázovým zamykaním? (1) Trieda rozvrhov generovaných striktným 2-fázovým zamykaním, je podmnožinou CSR ST (konflikt-sériovateľné a striktné rozvrhy). Rozhodnite a zdôvodnite, či sa táto trieda zmení, ak transakciám dovolíte kedykoľvek zmeniť svoj read-lock na write-lock (opačnú zmenu nedovolíte). Príklad transakcie: rl(x), r(x), wl(x), w(x), c. (2) Áno, zmení sa. Ak systém toto bez ďalších obmedzení dovolí, prestane garantovať sériovateľnosť, napríklad dovolí vygenerovať takýto nesériovateľný rozvrh: r1(x), r2(x), w1(x), c1, w2(x), c2. Takto vyzera tento rozvrh rozšírený o zamykacie operácie: rl1(x), r1(x), rl2(x), r2(x), wl1(x), w1(x), c1, wl2(x), w2(x), c2. To je veľmi zlé, lebo motiváciou použitia 2-fázového zamykania je práve garancia konflikt-sériovateľnosti. (Okrem toho, učiteľov databáz irituje, keď ich študenti používajú zámky týmto spôsobom takže už len preto to robiť netreba.) Systém síce môže zmenu read-locku na write-lock dovoliť, ale veľmi opatrne. Na to slúžia tzv. upgrade-locks, o ktorých sa dá dočítať (doporučujem) v knihe od Bernstein & Hadzilacos & Goodman. Upgrade-locks sú zámky, ktoré sa pridajú k read-locks a write-locks. Pridaním upgrade-locks sa rozšíri trieda rozvrhov generovaných 2-fázovým zamykaním, pričom sa zachovávajú pôvodné garancie.
3. a) Rozhodnite a zdôvodnite, či v relačnej algebre platí nasledujúca rovnosť pre všetky relácie typu r(x, Y, Z), s(x, Y) bez NULL hodnôt (ak áno, dokážte; ak nie, uveďte kontrapríklad s konkrétnymi reláciami a uveďte výsledky výrazov na oboch stranách rovnosti): Π X (r) Π X (s) = Π X (r s). (2) Táto rovnosť neplatí. Napríklad pre relácie r(x, Y, Z) = {[1, 1, 1]} a s(x, Y) = {[1, 2]} výraz na ľavej strane vráti {1}, zatiaľ čo výraz na pravej strane vráti prázdnu množinu. b) Preložte ľavú aj pravú stranu rovnosti do relačného kalkulu. (2) Ľavá strana: {X: Y1 Y2 Z (r(x, Y1, Z) s(x, Y2))}. Pravá strana: {X: Y Z (r(x, Y, Z) s(x, Y))}.
4. a) Definujte bezpečnosť Datalogových programov. (1) Datalogový program je bezpečný práve vtedy, ak je bezpečné každé jeho pravidlo. Datalogové pravidlo je bezpečné práve vtedy, ak platí: každá premenná, ktorá sa vyskytuje kdekoľvek pravidle (vrátane hlavy pravidla), sa vyskytuje aj v nejakom nie negovanom relačnom podcieli toho pravidla. (Premenné, ktoré sa vyskytujú v niektorom relačnom podcieli pravidla, môžeme nazvať bezpečné premenné v tom pravidle.) b) Vysvetlite, prečo je rozumné od Datalogových programov vyžadovať, aby boli bezpečné. (1) Datalogový program (presnejšie, množiny n-tíc hodnôt, pre ktoré sú splnené predikáty definované v Datalogovom programe), chceme vedieť strojovo (algoritmicky) počítať. Všetky bezpečné Datalogové programy počítať vieme. Ak je extenzionálna databáza konečná, výsledky aj medzivýsledky všetkých bezpečných Datalogových programov sú vždy konečné a dokážeme ich vypočítať nezávisle na doménach (typoch) atribútov extenzionálnej databázy. Bezpečnosť formúl je to, čo vydeľuje teóriu databáz z matematickej teórie modelov a odlišuje ju od algebry. Bezpečnosť zaručuje, že keď pracujeme s konečnou databázou, výsledky a medzivýsledky budú vždy konečné a dokážeme ich vypočítať. Nezávisle na tom, aké sú domény. Skúmanie modelov množín formúl (teórií) je rozvinutá disciplína algebry. Matematikov zaujíma prevažne to, aký dopad majú formuly na štruktúru oborov definície (tá štruktúra oboru definície môže byť napríklad torzná grupa, pole, cylindrická algebra a pod.). V databázach nás zaujímajú len tie vlastnosti, ktoré vyplývajú iba z obsahu relácií (tabuliek), ktoré nezávisia na štruktúre domén. Inak povedané, zaujímajú nás iba doménovo nezávislé formuly t.j. také formuly, ktorých pravdivosť a nepravdivosť (množiny ohodnotení premenných, pre ktoré sú tie formuly pravdivé) závisí len na naplnení relácií a nie na obsahu domén. Zistiť, či formula je doménovo nezávislá, je nerozhodnuteľný problém (M. Vardi). Bezpečné formuly sú vlastnou podmnožinou doménovo nezávislých formúl. Bezpečnosť formúl sa testuje jednoducho, stačí formulu strojovo preložiť do Datalogového programu a ten strojovo otestovať, či je bezpečný. Toto je hlbší dôvod, prečo vyžadujeme bezpečnosťdatalogových programov.
c) Rozhodnite o každom z nasledujúcich dvoch programov, či je bezpečný; svoju odpoveď zdôvodnite. (2) P1: f(x) a(x, Z), Y = X * Z, not g(x, Y). g(x, Y) a(x, Y), not a(z, Y). P2: p(x) a(x, _), X = Y * Z, not q(y), not q(z). q(z) a(x, Z), not a(z, X). P1 nie je bezpečný program, lebo v pravidle pre f(x) sa premenná Y vyskytuje v aritmetickom podcieli, ale nie v pozitívnom relačnom podcieli. (Ďalší dôvod je ten, že v pravidle pre g(x, Y) sa premenná Z vyskytuje iba v negovanom relačnom podcieli. Toto je však len formálny dôvod, ku ktorému sa vrátim v nasledujúcej podúlohe.) P2 nie je bezpečný program, lebo v pravidle pre p(x) sa premenné Y a Z vyskytujú v aritmetickom podcieli, ale nie v pozitívnom relačnom podcieli. d) Je niektorý z týchto 2 programov síce nie bezpečný v zmysle definície (t.j. v predošlej podúlohe ste preň povedali NIE), ale napriek tomu ste ochotní ho neformálne za bezpečný považovať a boli by ste dokonca ochotní kvôli nemu definíciu bezpečnosti trošku zoslabiť? Ak áno, povedzte ktorý a vysvetlite prečo. (1) (V oboch programoch a(_, _) je ext. databáza.) Program P1 nie je síce formálne bezpečný, ale napriek tomu sa dá strojovo počítať. Neformálne ho teda môžeme považovať za bezpečný program. Dôvod je takýto. Pozrime sa na pravidlo pre f(x). Premenná Y sa síce nevyskytuje v pozitívnom relačnom podcieli, ale ak má platiť f(x), Y môže nadobúdať len konečne veľa hodnôt; lebo Y je výsledkom súčinu X * Z, kde X aj Z môžu nadobúdať len konečne veľa hodnôt (aby platilo f(x), musí platiť a(x, Z)). Inak povedané, vieme vymenovať všetky trojice [X, Y, Z], pre ktoré môže f(x) platiť. Takýchto trojíc je konečne veľa (ak je zvyšok toho programu bezpečný.) Pozrime sa teraz na pravidlo pre g(x). Premenná Z sa síce nevyskytuje v pozitívnom relačnom podcieli, ale v skutočnosti je tá premenná nevýznamná (dá sa nahradiť anonymnou premennou _, ale to nie je podstatné). Pravidlo g(x, Y) a(x, Y), not a(z, Y). je antijoin a dá sa dá ekvivalentne nahradiť dvomi bezpečnými pravidlami: g(x, Y) a(x, Y), not h(y). h(y) a(_, Y). Definíciu bezpečnosti môžeme rozšíriť o antijoin: bezpečnosť pravidla neovplyvňuje, ak negovaný relačný podcieľ obsahuje anonymné premenné. Ďalšie rozšírenie sa týka aritmetiky: bezpečnosť pravidla neovplyvňuje, ak sa niektorá premenná toho pravidla vyskytuje iba v aritmetickom podcieli v prípade, ak tá premenná je vo výstupnej množine toho podcieľa a zároveň všetky premenné vstupnej množiny toho podcieľa sú bezpečné (o tom bude reč v ďalšom semestri).
Program P2 nie je formálne bezpečný a je rozumné ho nepovažovať za bezpečný ani neformálne. Dôvodom je, že ak chce niekto vymenovať všetky dvojice [Y, Z] typu integer, pre ktoré platí X = Y * Z (kde X je integer), tak musí riešiť problém faktorizácie o ktorom sa v súčasnosti verí, že je ťažký (na tomto probléme sú napríklad založené prakticky všetky masovo používané šifrovacie algoritmy). Ale dobre povedali sme, že nás zaujíma algoritmická vypočítateľnosť, nie výpočtová zložitosť. Faktorizácia vyzerá byť síce ťažký (t.j. časovo veľmi náročný) problém, ale algoritmicky sa počítať dá. Lenže čo ak doména (typ) prvého atribútu relácie a(_, _) nie je integer, ale napríklad reálne číslo? V tom prípade stojíme pred problémom, nájsť všetky také reálne čísla Y a Z, pre ktoré platí X = Y * Z, kde X je reálne číslo. Tento problém algoritmicky riešiteľný nie je. Aj pri použití floating point aritmetiky sa dá tento problém pokladať za prakticky neriešiteľný. (Skúste v ľubovoľnom programovacom jazyku napísať program, ktorý nájde všetky čísla Y a Z typu float, pre ktoré platí napríklad Y * Z = 3.14, pričom použijete presnosť aritmetiky svojho počítača.) V tomto konkrétnom prípade nás síce môže zachrániť to, že nemusíme nájsť všetky dvojice. Stačí nájsť jednu dvojicu [Y, Z] pre ktorú platí X = Y * Z a zároveň platia všetky ostatné podciele. Ale ak aj takú dvojicu nájdeme, môžeme právom hovoriť o šťastí. (Ak by sa nám totiž pre podobný program chvíľu nedarilo takú dvojicu uhádnuť, tak by sme ju museli hľadať nejako systematicky, a to sa nedá.)