Hlavní navigace

Nový pohľad na tradičný relačný model

9. 6. 2011
Doba čtení: 5 minut

Sdílet

Relačný model je dobre známym pojmom v oblasti softvérového inžinierstva. Tento dvojdielny článok vám predstaví projekt Bandicoot, ktorý implementuje relačný model netradičným spôsobom. Zároveň si ukážeme, ako vyvíjať softvér pomocou relačného modelu, a to bez použitia tradičných relačných databáz či jazyka SQL.

Základné pojmy

Skôr než sa pustíme do praktických ukážok, zhrnieme si základné pojmy modelu a vlastnosti systému Bandicoot. Relačný model a algebra sú tu s nami už od roku 1969. Ide o komplexný matematický model, ale pre účely tohto článku predstavíme len nasledujúce pojmy: Relácia, Relačná premenná, Relačná algebra a operácia priradenia.

Základom modelu je dátový typ Relácia. Je to v podstate štruktúra, ktorá sa skladá z atribútov, kde každý atribút má svoje vlastné meno a dátový typ.

Príklad: relácia Kniha s atribútmi Autor (reťazec znakov) a Rok vydania (celočíselná hodnota).

Model teda definuje Reláciu ako dátový typ. Okrem toho definuje aj relačné premenné. Každá takáto premenná je typu konkrétnej Relácie. Obsah premennej je vždy množina záznamov s rovnakou štruktúrou.

Príklad: premenná Moje knihy je typu Kniha (táto premenná reprezentuje množinu všetkých mojich kníh)

Ďalším dôležitým pojmom je Relačná algebra. Tá definuje základnú množinu operácií pre manipuláciu s relačnými premennými. Tieto operácie sú definované tak, aby tvorili uzavretý systém. Pomocou nich je možné meniť existujúce relácie ľubovoľným spôsobom a tým vytvárať nové relácie. Pre ilustráciu odporúčam pozrieť si vizualizáciu týchto operácií.

Okrem definovania nových relácii, deklarácie relačných premenných a používania operátorov relačnej algebry, máme k dispozícii aj koncept priradenia novej hodnoty do relačnej premennej. V princípe je to to isté, ako príkaz priradenia v klasickom programovaní.

Bandicoot

Bandicoot, začínajúci open-source projekt kompletne napísaný v jazyku C, je programovací systém s novým jazykom založeným na relačnej algebre, zabudovanou persistenciou a vlastným “run-time” prostredím. Programovací jazyk je navrhnutý takým spôsobom, aby bol použiteľný nie len ako dotazovací jazyk, ale aj ako jazyk na programovanie aplikačnej logiky.

V tomot dieli si predstavíme iba jazyk systému Bandicoot. Na ostatné vlastnosti, ako funkcie, HTTP rozhranie, persistenciu a transakcie, sa detailne pozrieme v druhom dieli článku.

Nový programovací jazyk

Nový pohľad na relačný model začína novým programovacím jazykom. V súčasnosti je SQL všeobecne uznávaným jazykom „relačných“ databáz, aj keď porušuje základné princípy relačného modelu. Tejto tematike sa podrobnejšie venuje kniha „Database in Depth“, ktorej autorom je Chris Date. Nasledujúce odstavce popisujú syntax jazyka Bandicoot a porovnávajú ho s SQL.

Základnými prvkami sú: relácie, relačné premenné a funkcie.

Príklad: definícia relácie Kniha, jednej premennej a jednoduchej funkcie

    rel Kniha {
        titul: string,
        autor: string,
        rok: int,
        pocetStran: int,
        pocetSlov: int,
    }

    knihy: Kniha;

    fn Knihy(): Kniha
    {
        return knihy;
    } 

Tento príklad nie je možné prepísať do SQL, ktorý neposkytuje koncept oddeleného definovania relácie a premenných. Jediný spôsob ako niečo podobné zapísať je použitie príkazu na vytvorenie tabuľky.

    create table Kniha (
        titul character(30),
        autor character(30),
        rok  integer,
        pocetStran  integer,
        pocetSlov  integer
    ); 

Neprítomnosť relačných premenných v SQL má za následok niektoré zásadné nevýhody tohto jazyka a znemožňuje jeho použitie ako plnohodnotného progamovacieho jazyka pre implementáciu aplikačnej logiky. Tradičné relačné databázy to obchádzajú implementáciou procedurálnych jazykov (Oracle PL/SQL, Sybase Transact SQL, PostgreSQL PL/pgSQL, atď.), ktoré už ale neposkytujú vysokú abstrakciu relačnej algebry.

Bandicoot implementuje operátory relačnej algebry pre manipuláciu s premennými. V nasledujúcich odstavcoch si ukážeme každý operátor samostatne a porovnáme si ho s implementáciou v SQL. Pre účely týchto príkladov si definujeme nasledovné premenné.

    archiv: Kniha; # nová premenná rovnakého typu ako "knihy"

    rel Predajca {
       meno: string,

       # titul a cena knihy
       titul: string,
       cena: real,
    }

    predajcovia: Predajca; 

Select

Operácia na filtrovanie záznamov z premennej pomocou podmienky.

Príklad: výber kníh s rokom vydania vyšším než 1990

    Bandicoot:  knihy select(rok > 1990);

    SQL:        select * from knihy where rok > 1990; 

Project

Operácia na zmenu relácie definovaním atribútov, ktoré sa majú zachovať vo výsledku.

Príklad: výber autorov v rámci kníh

    Bandicoot:  knihy project(autor);

    SQL:        select distinct autor from knihy; 

Extend

Operácia na pridávanie nových atribútov, ktorých hodnota je vypočítaná z hodnôt ostatných atribútov v relácií.

Príklad: výpočet počtu slov na stránku

    Banicoot:   knihy extend(slovNaStranku = real(pocetSlov) / real(pocetStran));

    SQL:        select k.*, pocetSlov / pocetStran as slovNaStranku from knihy k; 

Join

Operácia na spojenie dvoch relačných premenných. Výsledná relácia má atribúty obidvoch vstupných premenných a záznamy sa spájajú na atribútoch rovnakého názvu a typu.

Príklad: spojenie predajcov a kníh cez atribút „titul“

    Banicoot:   knihy * predajcovia;

    SQL:        select * from knihy natural join predajcovia; 

Union

Operácia na sčítanie dvoch premenných rovnakého typu. Vo výsledku sú všetky záznamy zo vstupných premenných.

Príklad: sčítanie kníh a archívu

    Banicoot:   knihy + archiv;

    SQL:        select * from knihy union select * from archiv; 

Minus

Operácia na odpočítanie obsahu jednej premennej od obsahu druhej premennej.

Príklad: vyňatie kníh, ktoré sú aspoň u jedného predajcu

    Banicoot:   knihy - predajcovia;

    SQL:        select * from knihy k
                 where not exists (select 1
                                     from predajcovia p
                                    where p.titul = k.titul); 

Summary

Operácia na sumárne výpočty jednej premennej nad druhou premennou.

Príklad: výpočet priemernej ceny knihy u predajcov

    Banicoot:   (predajcovia, knihy) summary(priemer = avg(cena, 0.0));

    SQL:        -- pre jednoduchosť použijeme funkciu nvl() z Oracle
                select k.titul, k.autor, k.rok, k.pocetStran,
                       nvl(avg(p.cena), 0.0) as priemer
                  from knihy k
                  left outer join predajcovia p on (a.titul = k.titul)
                 group by k.titul, k.autor, k.rok, k.pocetStran; 

Priradenie

Samostatnou kapitolou je priradenie novej hodnoty do relačnej premennej. Koncept priradenia v Bandicoote je identický s tradičným programovaním. SQL namiesto toho poskytuje príkazy na pridanie (insert), vymazanie (delete) a zmenu (update) záznamov priamo v tabuľke.

Príklad: priradenie obsahu jednej premennej do druhej premennej

    Bandicoot:  archiv = knihy;

    SQL:        delete from archiv;
                insert into archiv select * from archiv; 

Príklad: pridanie a vymazanie záznamov

    Banicoot:   archiv = archiv + knihy;
                archiv = archiv - knihy;

    SQL:        insert into archiv
                select * from knihy minus select * from archiv;

                delete from archiv a
                 where exists (select 1
                                 from knihy k
                                where a.titul = k.titul); 

Programovací jazyk Bandicoot umožňuje použiť viacero operátorov v jednom príkaze. Podobne je to aj v SQL. Zásadným rozdielom je však to, že v SQL nie je možné určiť poradie vykonávania týchto príkazov. V prípade Bandicootu sa príkazy vykonávajú podľa jednoznačne určených pravidiel. Výhodou je, že vývojár má plnú moc nad tým, ako vykonať výpočet. Preto optimalizátor, ako ho poznáme z tradičných relačných databáz, nie je potrebný.

CS24_early

Príklad: výpočet priemernej ceny stránky na knihu u rôznych predajcov

    Bandicoot:  (predajcovia * knihy)
                extend(cenaStranky = cena / real(pocetStran))
                project(titul, meno, cenaStranky);

    SQL:        select titul, meno, cenaStranky
                  from (select *, cena / pocetStran as cenaStranky
                          from predajcovia natural join knihy); 

Záver

Bandicoot umožňuje používanie relačného modelu netradičným a jednoduchým spôsobom. Snaží sa poskytnúť softvérovým inžinierom jeho potenciál v plne miere. Druhý diel článku popíše zvyšné vlastnosti systému ako funkcie, globálne a lokálne premenné, presistenciu, transakcie a HTTP rozhranie. Zároveň poskytne návod, ako vytvoriť jednoduchú webovú aplikáciu v Bandicoote, ktorej používateľské rozhranie je napísané v jazyku JavaScript.

Byl pro vás článek přínosný?

Autor článku

Július Chrobák je spoluautorom projektu Bandicoot. Pracuje ako softvérový inžinier so zameraním na serverové komponenty a databázy.