Příklad databáze - relačně/objektově

 

Úvod

Zde uvádíme ilustrační příklad databáze vytvořené jednak podle objektových zásad, jednak také klasickým relačním způsobem -- pro srovnání obou přístupů. Jedná se o databázi, která uchovává informace o osobách, jejich vzájemných příbuzenských vztazích, místech jejich bydliště a dále některé charakteristiky obcí sloužící v našem příkladě k výpočtu dostupnosti dané obce z Brna.

 

Definice objektů

Nejdříve nadefinujeme nové objektové typy. Význam většiny atributů je zřejmý z jejich názvů.

CREATE TYPE obec AS OBJECT (
    id          INT,
    nazev       VARCHAR(15),
    psc         VARCHAR(6),
    vzdalenost  INT,
    doprava     INT,

    MAP MEMBER FUNCTION dostupnost RETURN INT
);

CREATE TYPE adresa AS OBJECT (
    id       INT,
    mesto    REF obec,
    ulice    VARCHAR(15),
    cislo    INT,

    MEMBER PROCEDURE pridej
    	(i_id INT, id_mesta INT, i_ulice VARCHAR, i_cislo INT)
);

CREATE TYPE clovek AS OBJECT (
    id       INT,
    jmeno    VARCHAR(22),
    bydliste REF adresa,
    otec     REF clovek,
    matka    REF clovek,

    MEMBER PROCEDURE pridej
    	(i_id INT, i_jmeno VARCHAR, id_bydliste INT, id_otce INT, id_matky INT)
);
 

Implementace metod

A nyní naimplementujeme výše deklarované metody našich objektů.

Nejdříve funkci dostupnost, která u objektu obec počítá ze vzdálenosti a dopravy (úrovně dopravních prostředků) určitý koeficient dostupnosti dané obce. Tato funkce je označena jako MAP -- tzn. bude používána k porovnávání objektů typu obec -- např. při třídění.

CREATE OR REPLACE TYPE BODY obec AS
    MAP MEMBER FUNCTION dostupnost RETURN INT AS
    BEGIN
    	RETURN (vzdalenost * doprava);
    END dostupnost;
END;

Funkce pridej u zbývajících objektů se starají o správné nastavení referencí při vkládání nových objektů do databáze.

CREATE OR REPLACE TYPE BODY adresa AS
    MEMBER PROCEDURE pridej 
    	(i_id INT, id_mesta INT, i_ulice VARCHAR, i_cislo INT) AS
    BEGIN
    	id := i_id;
    	ulice := i_ulice;
    	cislo := i_cislo;

    	SELECT REF(o) INTO mesto
    		FROM obce o
    		WHERE o.id = id_mesta;

    	INSERT INTO adresy values(SELF);
    END pridej;
END;


CREATE OR REPLACE TYPE BODY clovek AS
    MEMBER PROCEDURE pridej
    	(i_id int, i_jmeno varchar, id_bydliste int, id_otce int, id_matky int) AS
    BEGIN
    	id := i_id;
    	jmeno := i_jmeno;

    	SELECT REF(a) INTO bydliste
    		FROM adresy a
    		WHERE a.id = id_bydliste;

    	SELECT REF(l) INTO otec
    		FROM lidi l
    		WHERE l.id = id_otce;

    	SELECT REF(l) INTO matka
    		FROM lidi l
    		WHERE l.id = id_matky;

    	INSERT INTO lidi values(SELF);
    END pridej;
END;
 

Vytvoření tabulek

Nyní, když máme objektové typy nadefinovány, můžeme už velice jednoduchým způsobem vytvořit odpovídající objektové tabulky.

CREATE TABLE obce OF obec;
CREATE TABLE adresy OF adresa;
CREATE TABLE lidi OF clovek;

Pro relační případ musíme vše nadefinovat "znovu".

CREATE TABLE r_obce (
    id         INT,
    nazev      VARCHAR(15),
    psc        VARCHAR(6),
    vzdalenost INT,
    doprava    INT
);

CREATE TABLE r_adresy (
    id         INT,
    mesto      INT,
    ulice      VARCHAR(15),
    cislo      INT
);

CREATE TABLE r_lidi (
    id         INT,
    jmeno      VARCHAR(22),
    bydliste   INT,
    otec       INT,
    matka      INT
);
 

Vkládání dat

Vkládání dat se provádí i u objektových tabulek standardním způsobem. Rovněž je možno využít implicitního konstruktoru, jak je uvedeno níže.

INSERT INTO obce VALUES(1, 'Ostrava', '700 00', 160, 2);
INSERT INTO obce VALUES(2, 'Praha', '111 50', 200, 1);
INSERT INTO obce VALUES(3, 'Brno', '602 00', 0, 1);
INSERT INTO obce VALUES(4, 'Dolni Lhota', '602 00', 200, 5);
nebo
INSERT INTO obce VALUES (obec(1, 'Ostrava', '700 00', 160, 2));
INSERT INTO obce VALUES (obec(2, 'Praha', '111 50', 200, 1));
INSERT INTO obce VALUES (obec(3, 'Brno', '602 00', 0, 1));
INSERT INTO obce VALUES (obec(4, 'Dolni Lhota', '602 00', 200, 5));

Nyní ještě vložíme několik ilustrativních dat do tabulek adresy a lidi. Protože tyto objekty používají reference, použijeme při vkládání dat metody pridej, které se postarají o to, aby se referenece správně nastavily. (Objekty s otazníkem slouží k zaznamenání toho, že rodiče jsou neznámí.)

INSERT INTO lidi VALUES (0, '?', NULL, NULL, NULL);

DECLARE
    adr   adresa;
    cl    clovek;

BEGIN
    adr := adresa(0, NULL, 'prazdna', 0);
    cl  := clovek(0, '?', NULL, NULL, NULL);

    adr.pridej(1, 1, 'Muchova', 14);
    adr.pridej(2, 2, 'Lapkova', 11);
    adr.pridej(3, 3, 'Dourova', 50);
    adr.pridej(4, 4, 'Stara', 10);
    adr.pridej(5, 4, 'Nova', 5);

    cl.pridej(1, 'Horymir Krehula', 4, 0, 0);
    cl.pridej(2, 'Horymira Krehulova', 4, 0, 0);
    cl.pridej(3, 'Jeronym Doura', 3, 0, 0);
    cl.pridej(4, 'Jeronyma Dourova', 3, 0, 0);

    cl.pridej(5, 'Otakar Muchanek', 1, 1, 2);
    cl.pridej(6, 'Otakarka Muchankova', 1, 3, 4);
    cl.pridej(7, 'Bedrich Mucholapka', 2, 3, 4);
    cl.pridej(8, 'Bedriska Mucholapkova', 2, 1, 2);

    cl.pridej(9, 'Jan Krehula', 5, 5, 6);
    cl.pridej(10,'Jana Krehulova', 5, 7, 8);
END;

V relačním případě vkládáme data standardním způsobem.

INSERT INTO r_lidi VALUES (0, '?', NULL, NULL, NULL);

INSERT INTO r_adresy VALUES (1, 1, 'Muchova', 14);
INSERT INTO r_adresy VALUES (2, 2, 'Lapkova', 11);
INSERT INTO r_adresy VALUES (3, 3, 'Dourova', 50);
INSERT INTO r_adresy VALUES (4, 4, 'Stara', 10);
INSERT INTO r_adresy VALUES (5, 4, 'Nova', 5);

INSERT INTO r_lidi VALUES (1, 'Horymir Krehula', 4, 0, 0);
INSERT INTO r_lidi VALUES (2, 'Horymira Krehulova', 4, 0, 0);
INSERT INTO r_lidi VALUES (3, 'Jeronym Doura', 3, 0, 0);
INSERT INTO r_lidi VALUES (4, 'Jeronyma Dourova', 3, 0, 0);

INSERT INTO r_lidi VALUES (5, 'Otakar Muchanek', 1, 1, 2);
INSERT INTO r_lidi VALUES (6, 'Otakarka Muchankova', 1, 3, 4);
INSERT INTO r_lidi VALUES (7, 'Bedrich Mucholapka', 2, 3, 4);
INSERT INTO r_lidi VALUES (8, 'Bedriska Mucholapkova', 2, 1, 2);

INSERT INTO r_lidi VALUES (9, 'Jan Krehula', 5, 5, 6);
INSERT INTO r_lidi VALUES (10,'Jana Krehulova', 5, 7, 8);
 

Ukázky dotazů

prosté použití metody

Následující jednoduchý select ukazuje možnost použití metod objektů v SQL dotazu. Dá se očekávat (a je to velmi pravděpodobné), že taková funkce bude mnohem složitější -- a takto se nám podaří dotaz mnohem zpřehlednit. Navíc se tato funkce může chovat zapouzdřeně -- můžeme měnit její vnitřní výpočet, aniž by o tom někdo věděl a aniž by se musel SQL dotaz jakkoli upravovat.

SELECT o.id, o.nazev, o.dostupnost() AS dost
    FROM obce o;

Takto by stejný dotaz vypadat v relační variantě:

SELECT o.id, o.nazev, o.vzdalenost * o.doprava AS dost
    FROM r_obce o;

Jako výsledek, získáme na našem malém příkladě následující data:

        ID NAZEV                 DOST
---------- --------------- ----------
         1 Ostrava                320
         2 Praha                  200
         3 Brno                     0
         4 Dolni Lhota           1000
 

použití srovnávací metody MAP

Tento příklad prezentuje schopnost objektových databází porovnávat celé objeky mezi sebou -- opět aniž bychom museli do dotazu vkládat implementační "detaily".

SELECT o.id, o.nazev, o.dostupnost() AS dost
    FROM obce o
    ORDER BY VALUE(o);

a opět relační varianta:

SELECT o.id, o.nazev, o.vzdalenost * o.doprava AS dost
    FROM r_obce o
    ORDER BY dost;

Oproti předcházejícímu dotazu získáme jako výsledek seznam obcí setříděný podle jejich dostupnosti.

        ID NAZEV                 DOST
---------- --------------- ----------
         3 Brno                     0
         2 Praha                  200
         1 Ostrava                320
         4 Dolni Lhota           1000
 

využití referencí

Následující dotaz ukazuje, jak je možné využít objektových referencí k získání dat, na která se objekty odkazují.

SELECT l.jmeno, l.otec.jmeno as otec, l.matka.jmeno as matka
    FROM lidi l;

V relační verzi musíme použít operaci JOIN a propojit tabulky na základě odpovídajících si identifikátorů.

SELECT a.jmeno, b.jmeno as otec, c.jmeno as matka
    FROM r_lidi a, r_lidi b, r_lidi c
    WHERE (a.otec = b.id) AND (a.matka = c.id);

Dotaz vrátí seznam všech osob -- ke každé z nich její rodiče.

JMENO                  OTEC                   MATKA
---------------------- ---------------------- ----------------------
Horymir Krehula        ?                      ?
Horymira Krehulova     ?                      ?
Jeronym Doura          ?                      ?
Jeronyma Dourova       ?                      ?
Otakar Muchanek        Horymir Krehula        Horymira Krehulova
Otakarka Muchankova    Jeronym Doura          Jeronyma Dourova
Bedrich Mucholapka     Jeronym Doura          Jeronyma Dourova
Bedriska Mucholapkova  Horymir Krehula        Horymira Krehulova
Jan Krehula            Otakar Muchanek        Otakarka Muchankova
Jana Krehulova         Bedrich Mucholapka     Bedriska Mucholapkova
 

reference přes více tabulek

Další dotaz ukazuje, jak se v objektových databázích můžeme díky referencím vyhnout komplikovaným propojováním tabulek.

SELECT l.jmeno
    FROM lidi l
    WHERE
    	l.matka.jmeno = 'Horymira Krehulova' OR
    	l.matka.matka.jmeno = 'Horymira Krehulova' OR
    	l.otec.matka.jmeno  = 'Horymira Krehulova';

V relační databázi bychom to museli opět složitě spojovat z jednotlivých tabulek a identifikátorů.

SELECT DISTINCT a.jmeno
    FROM r_lidi a, r_lidi b, r_lidi c
    WHERE
    	(a.matka = b.id  AND  b.jmeno = 'Horymira Krehulova') OR
    	(a.matka = b.id  AND  b.matka =  c.id  AND  c.jmeno = 'Horymira Krehulova') OR
    	(a.otec  = b.id  AND  b.matka =  c.id  AND  c.jmeno = 'Horymira Krehulova');

Jako výsledek dává dotaz seznam všech dětí a vnoučat paní Horymíry Křehulové.

JMENO
----------------------
Otakar Muchanek
Bedriska Mucholapkova
Jan Krehula
Jana Krehulova
 

kombinovaný dotaz

Poslední dotaz kombinuje několik předchozích technik.

SELECT
    l.jmeno,
    l.bydliste.mesto.nazev AS mesto,
    l.bydliste.mesto.vzdalenost AS km,
    l.bydliste.mesto.doprava AS doprava,
    l.bydliste.mesto.dostupnost() AS dost
    FROM lidi l
    ORDER BY dost;

v relační verzi:

SELECT
    l.jmeno,
    o.nazev AS mesto,
    o.vzdalenost AS km,
    o.doprava AS doprava,
    o.doprava * o.vzdalenost as dost
    FROM r_lidi l, r_adresy a, r_obce o
    WHERE
    	l.bydliste = a.id  AND  a.mesto = o.id
    ORDER BY
    	dost;

Výstupem jsou informace týkající se dostupnosti jednotlivých osob (např. k posouzení přidělení kolejí).

JMENO                  MESTO                   KM    DOPRAVA       DOST
---------------------- --------------- ---------- ---------- ----------
Jeronym Doura          Brno                     0          1          0
Jeronyma Dourova       Brno                     0          1          0
Bedrich Mucholapka     Praha                  200          1        200
Bedriska Mucholapkova  Praha                  200          1        200
Otakar Muchanek        Ostrava                160          2        320
Otakarka Muchankova    Ostrava                160          2        320
Horymir Krehula        Dolni Lhota            200          5       1000
Horymira Krehulova     Dolni Lhota            200          5       1000
Jan Krehula            Dolni Lhota            200          5       1000
Jana Krehulova         Dolni Lhota            200          5       1000