![]() |
10. CVIČENÍ |
V současné době směřuje trend ve vývoji DBS směrem k objektovým DBS, neboť objektové DBS umožňují snadněji a přesněji modelovat většinu z různých tříd aplikací. Proto se výrobci relačních DBS snaží své produkty rozšířit o některé základní vlastnosti objektových DBS, čímž vznikají tzv. objektově-relační DBS. Zmíněný tred se promítá i do nového standardu SQL 3, jehož součástí jsou i abstraktní datové typy, persistentní programové moduly a vhnízděné tabulky. Jedním z objektově-relačních DBS je i systém Oracle 8, na kterém si nyní ukážeme některá objektová rozšíření relačního DBS.
Abstraktní datové typy
Abstraktní datový typ lze nadefinovat příkazem CREATE TYPE. Tento uživatelem definovaný ADT lze pak použít všude, kde jsou používány standardní datové typy SQL. Následující příklad ukazuje definici typu t_adresa reprezentujícího adresu obyvatele. Ukázána je i definice tabulky, kde jedním z atributů je objekt typu t_adresa :
CREATE TYPE t_adresa AS OBJECT ( ulice VARCHAR2(30), cislo NUMBER(3), obec VARCHAR2(30), psc NUMBER(5) ) / CREATE TABLE obyvatele ( prijmeni VARCHAR2(30), jmeno VARCHAR2(30), adresa t_adresa ) /
Práce s objekty v SQL je, až na malé výjimky, víceméně intuitivní. Vkládání záznamů ukazuje spodní příklad. Instance objektu třídy t_adresa je vytvořena voláním implicitního konstruktoru :
INSERT INTO obyvatele (jmeno, prijmeni, adresa) VALUES ( 'Jan', 'Pavlasek', t_adresa('Na hrazi', 15, 'Liptakov', 32100) );
SELECT prijmeni, adresa FROM obyvatele; SELECT prijmeni, jmeno FROM obyvatele WHERE adresa = t_adresa('Na hrazi', 15, 'Liptakov', 32100);
SELECT o.jmeno AS jmeno, o.prijmeni AS prijmeni, o.adresa.obec AS obec, o.adresa.psc AS psc FROM obyvatele o;
UPDATE obyvatele o SET o.adresa.ulice = 'Pod hrazi', o.adresa.cislo = 8 WHERE prijmeni = 'Pavlasek' AND jmeno = 'Jan'; UPDATE obyvatele SET adresa = t_adresa('Na hrazi', 15, 'Liptakov', 32100) WHERE prijmeni = 'Pavlasek' AND jmeno = 'Jan';
UPDATE obyvatele SET adresa = NULL WHERE prijmeni = 'Pavlasek' AND jmeno = 'Jan';
Metody objektů
Samozřejmě, že objekty v SQL 3 mohou mít kromě datových prvků i metody. Následující ukázka slouží pro ilustraci definice objektu s několika metodami. Implementace metod je definována v příkazu CREATE TYPE BODY.
Rozhraní objektu :
CREATE OR REPLACE TYPE t_osoba AS OBJECT ( jmeno VARCHAR2(30), prijmeni VARCHAR2(30), naroz DATE, plat NUMBER(7,2), MEMBER FUNCTION vek RETURN INTEGER, MEMBER PROCEDURE zvys_plat(castka IN NUMBER), MEMBER PROCEDURE sniz_plat(castka IN NUMBER) ) / |
Implementace metod objektu :
CREATE OR REPLACE TYPE BODY t_osoba AS MEMBER FUNCTION vek RETURN INTEGER IS BEGIN RETURN to_number(sysdate - naroz)/365; END; MEMBER PROCEDURE zvys_plat(castka IN NUMBER) IS BEGIN plat := plat + castka; END; MEMBER PROCEDURE sniz_plat(castka IN NUMBER) IS BEGIN IF (plat - castka < 0) THEN plat := 0; ELSE plat := plat - castka; END IF; END; END; / |
Řádek v tabulce může být reprezentován i objektem. Tabulku pak nadefinujeme jednoduše takto :
CREATE TABLE osoby OF t_osoba;
Nyní vložíme do tabulky několik záznamů a zkusíme zavolat metodu věk() :
INSERT INTO osoby (jmeno, prijmeni, naroz, plat) VALUES ('Jarda', 'Kabrnak', to_date('1.1.1980', 'DD.MM.YYYY'), 1234.56); INSERT INTO osoby (jmeno, prijmeni, naroz, plat) VALUES ('Josef', 'Jiricka', to_date('1.1.1970', 'DD.MM.YYYY'), 6543.21); SELECT o.prijmeni, o.jmeno, o.vek() AS vek FROM osoby o;
Volání metod uvnitř PL/SQL bloku je stejné jako u ostatních objektově-orientovaných jazyků :
DECLARE clovek t_osoba; c_ref REF t_osoba; BEGIN clovek := t_osoba('Jan', 'Machacek', to_date('1.1.1975', 'DD.MM.YYYY'), 1122.33); clovek.zvys_plat(1000.0); INSERT INTO osoby o VALUES(clovek) RETURNING REF(o) INTO c_ref; UPDATE osoby o SET plat = plat - 100.0 WHERE REF(o) = c_ref; END; / |
Pole jako atributy
V praxi se často vyskytují případy, kdy je třeba uložit do jednoho atributu více hodnot. Typickým případem jsou alternativní čísla telefonu, na kterých je dosažitelná určitá osoba. Relační model dat nás nutí vytvořit novou entitu pro telefonní čísla, což se nám může oprávněně zdát poněkud nepřirozené. V takovýchto případech lze s výhodou použít pole proměnné délky - VARRAY. Pole VARRAY může obsahovat různý počet položek stejného datového typu (tzn. i objekty), který nesmí překročit definovanou velikost pole. Způsob použití polí je ukázán v následujícím příkladě :
CREATE TYPE t_tel_seznam AS VARRAY(5) OF VARCHAR2(30) / CREATE TYPE t_potomek AS OBJECT ( jmeno VARCHAR2(30), vek NUMBER(3) ) / CREATE TYPE t_pot_seznam as VARRAY(10) OF t_potomek / CREATE TABLE personal ( jmeno VARCHAR2(30), prijmeni VARCHAR2(30), telefony t_tel_seznam, deti t_pot_seznam ) /
INSERT INTO personal (jmeno, prijmeni, telefony, deti) VALUES ( 'Jan', 'Kokoska', t_tel_seznam('123', '124', '125'), t_pot_seznam(t_potomek('Jirka', 10), t_potomek('Jakub', 16))); SELECT * FROM personal;
SELECT p.prijmeni, d.jmeno, d.vek FROM personal p, TABLE(p.deti) d; SELECT p.prijmeni, COUNT(d.jmeno) AS pocet_deti FROM personal p, TABLE(p.deti) d GROUP BY p.prijmeni ORDER BY p.prijmeni;
UPDATE personal SET telefony = t_tel_seznam('222', '333') WHERE prijmeni = 'Kokoska';
V PL/SQL je práce s polem VARRAY velmi snadná. K jednotlivým položkám lze přistupovat přes index. Kromě toho každé pole obsahuje atribut count, jehož hodnota udává počet prvků pole :
SET SERVEROUTPUT ON DECLARE i INTEGER; p personal%ROWTYPE; BEGIN SELECT * INTO p FROM personal WHERE prijmeni = 'Kokoska'; dbms_output.new_line; dbms_output.put_line('Pocet telefonu : '||p.telefony.count); dbms_output.put_line('Deti :'); FOR i IN 1..p.deti.count LOOP dbms_output.put_line(p.deti(i).jmeno); END LOOP; END; / |
Vhnízděné (vnořené) tabulky
Vnořené tabulky mají oproti polím tu výhodu, že z hlediska aktualizace lze přistupovat k jednotlivým řádkům, jejichž počet není nijak omezen (teoreticky). Nevýhodou však je, že prvky (řádky) v tabulce nemají definované pořadí, jak tomu je u polí. Vnořenou tabulku vytvoříme tak, že nadefinujeme uživatelský datový typ podobně jako u polí :
CREATE TYPE t_zamestnanec AS OBJECT ( jmeno VARCHAR2(30), prijmeni VARCHAR2(30), plat NUMBER(5) ) / CREATE TYPE tab_zamestnanci AS TABLE OF t_zamestnanec /
Definujeme-li tabulku, která obsahuje vnořenou tabulku, musíme definovat jméno tabulky, kde budou fyzicky uloženy záznamy z vnořených tabulek. V uvedeném příkladě to je tabulka NTAB_ZAMESTNANCI :
CREATE TABLE katedry ( cislo_kat NUMBER(5), nazev VARCHAR2(50), zamestnanci tab_zamestnanci ) NESTED TABLE zamestnanci STORE AS ntab_zamestnanci /
INSERT INTO katedry (cislo_kat, nazev, zamestnanci) VALUES (111, 'KIV', tab_zamestnanci( t_zamestnanec('Max', 'Otta', 12345), t_zamestnanec('Pavel', 'Herout', 12345), t_zamestnanec('Ladislav', 'Pesicka', 12345)));
SELECT * FROM katedry; SELECT z.* FROM katedry k, TABLE(k.zamestnanci) z WHERE k.cislo_kat = 111;
INSERT INTO TABLE( SELECT zamestnanci FROM katedry WHERE cislo_kat = 111 ) VALUES ('Martin', 'Simek', 12345); --- UPDATE TABLE( SELECT zamestnanci FROM katedry WHERE cislo_kat = 111 ) SET plat = 9999 WHERE jmeno = 'Max'; --- UPDATE TABLE( SELECT zamestnanci FROM katedry WHERE cislo_kat = 111 ) z SET VALUE(z) = t_zamestnanec('Martin', 'Simacek', 1234) WHERE z.prijmeni = 'Simek'; --- DELETE FROM TABLE( SELECT zamestnanci FROM katedry WHERE cislo_kat = 111 ) WHERE jmeno = 'Max';
UPDATE katedry SET zamestnanci = NULL WHERE cislo_kat = 111;
UPDATE katedry SET zamestnanci = tab_zamestnanci() WHERE cislo_kat = 111; UPDATE katedry SET zamestnanci = tab_zamestnanci(t_zamestnanec('Max', 'Otta', 9999)) WHERE cislo_kat = 111;
Reference na objekty
V některých případech nechceme v atributu uchovávat celý objekt, ale pouze referenci (ukazatel, odkaz) na něj. Proto byl v SQL3 zaveden typ reference. Uvažme případ, že máme danou databázi pracovišť a předmětů (nábytek apod.) uložených na jednotlivých pracovištích. Vzhledem k centrální správě předmětů by bylo poněkud nevýhodné mít u každého pracoviště vnořenou tabulku předmětů. Navíc předměty mohou být rozděleny do několika kategorií a tedy i tabulek. Následující příklad ukazuje nastíněnou situaci, kdy předměty jsou ukládány v jediné tabulce a u každého předmětu je reference na pracoviště, na němž se nachází :
CREATE TYPE t_pracoviste AS OBJECT ( zkratka VARCHAR2(3), nazev VARCHAR2(50) ) / CREATE TABLE kancelare OF t_pracoviste / CREATE TYPE t_predmet AS OBJECT ( nazev VARCHAR2(30), ev_cislo NUMBER(5), pracoviste REF t_pracoviste ) / CREATE TABLE predmety OF t_predmet /
Pro ilustraci si vyzkoušejte vytvořit nový objekt a vypsat jeho OID (referenci) :
INSERT INTO kancelare (zkratka, nazev) VALUES ('KIV', 'Kancelar KIV'); SELECT REF(k) FROM kancelare k WHERE zkratka = 'KIV';
CREATE TABLE predmety OF t_predmet ( SCOPE FOR (pracoviste) IS kancelare ) /
INSERT INTO predmety (nazev, ev_cislo, pracoviste) VALUES ('Zidle', 555, (SELECT REF(k) FROM kancelare k WHERE k.zkratka = 'KIV'));
ORA-22889: REF value does not point to scoped table
SELECT p.nazev, p.ev_cislo, p.pracoviste.zkratka FROM predmety p;
DELETE FROM kancelare; SELECT p.nazev FROM predmety p WHERE p.pracoviste IS DANGLING;
Malá případová studie na závěr :
Uvažme databázi nějakého ústavu, kde jsou centrálně evidování zaměstnanci, jednotlivá pracoviště jsou evidována zvlášť. Vazba mezi pracovníkem a jeho pracovištěm je realizována referencí u záznamu pracovníka na záznam jeho pracoviště. Celou situaci ilustruje spodní obrázek :
Z předchozích příkladů využijeme typ t_pracoviste a tabulku KANCELARE. Zbývající tabulky vytvoříme takto :
CREATE TABLE dilny OF t_pracoviste ( PRIMARY KEY (zkratka) ) / CREATE TABLE laboratore OF t_pracoviste ( PRIMARY KEY (zkratka) ) / CREATE TYPE t_pracovnik AS OBJECT ( prijmeni VARCHAR2(30), jmeno VARCHAR2(30), pracoviste REF t_pracoviste ) / CREATE TABLE pracovnici OF t_pracovnik /
INSERT INTO dilny VALUES ('D1', 'Dilna 1'); INSERT INTO dilny VALUES ('D2', 'Dilna 2'); INSERT INTO kancelare VALUES ('K1', 'Kancelar 1'); INSERT INTO laboratore VALUES ('L1', 'Laborator 1'); INSERT INTO pracovnici VALUES ('Max', 'Otta', (SELECT REF(p) FROM dilny p WHERE zkratka = 'D1')); INSERT INTO pracovnici VALUES ('Ladislav', 'Pesicka', (SELECT REF(p) FROM dilny p WHERE zkratka = 'D1')); INSERT INTO pracovnici VALUES ('Ondrej', 'Rohlik', (SELECT REF(p) FROM dilny p WHERE zkratka = 'D2')); INSERT INTO pracovnici VALUES ('Helena', 'Benesova', (SELECT REF(p) FROM kancelare p WHERE zkratka = 'K1')); INSERT INTO pracovnici VALUES ('Roman', 'Moucek', (SELECT REF(p) FROM laboratore p WHERE zkratka = 'L1')); COMMIT;
SELECT p.prijmeni, p.jmeno, p.pracoviste.nazev FROM pracovnici p;