rozdzial8


8

Zorientowane obiektowo j ęzyki zapytań

W bieżącym rozdziale przedstawimy dwie koncepcje wprowadzenia pro­gramowania zorientowanego obiektowo do świata baz danych. Oba języki: OQL i SQL3 są raczej propozycjami standardów niż językami o szerokim zakresie zastosowań, ale oba zyskują coraz szerszą akceptację i pewne ich elementy bywają raptownie dolączane do systemów komercyjnych.

Obiektowy język zapytań OQL (Object Query Language) stanowi próbę standaryzacji zorientowanych obiektowo języków zapytań, w której starano się połączyć elementy programowania wysokiego poziomu SQL z paradyg­matami programowania obiektowego. Na początku omówimy metody i obszar stosowania ODL, języka definiowania obiektów, który w rozdziale 2 służyl do opisu modelu. Te dwa elementy mają ważny wpływ na język zapytań OQL. Potem omówimy kilka rożnych aspektów programowania w języku OQL.

Podczas gdy OQL stanowi próbę jak najlepszego przystosowania SQL do programowania obiektowego, to SQL3 można traktować jako próbę włą czenia podejścia obiektowego do modelu relacyjnego. W pewnym sensie oba języki spotykają się gdzieś „po środku", ale istnieją także między nimi spore różnice, które polegają przede wszystkim na tym, że pewne operacje wyko­nuje się latwiej w jednym języku, a inne w drugim. Dlatego zaraz po opisaniu zorientowanych obiektowo wlaściwości standardu SQL3 dokonamy porów­nania możliwości tkwiących w obu językach.

W zasadzie różnica między tymi dwiema koncepcjami sprowadza się do różnych rozwiązań zagadnienia: ,jak ważna jest relacja?". W środowisku skupionym wokół ODL i OQL odpowiedź na tak postawione pytanie brzmi: „niezbyt". W tych językach bowiem można korzystać ze wszystkich obiek­tów: zbiorów, wielozbiorów, struktur (czyli relacji) itd. Natomiast w SQL3 relacja jest nadal podstawowym pojęciem bazy danych. W podejściu SQL3, które bywa często nazywane obiektowo-relacyjnym, model relacyjny jest roz­szerzony przez dopuszczenie bardziej złożonych typów krotek oraz dziedzin atrybutów relacji. Zatem klasy i obiekty są dolączone do modelu relacyjnego, ale zawsze w powiązaniu z relacjami.

4%O 8. ZORIENTOWANE OBIEKTOWO JĘZYKI ZAPYTAŃ

8 .1. Elementy zapytań w j ęzyku ODL

W bieżącym rozdziale będziemy kontynuować rozważania z rozdziału 2 na temat języka ODL. Najpierw opiszemy różne sposoby współdziałania klas ODL z szerszym środowiskiem, w którym są one zanurzane. Potem rozwa­żymy sprawę „zasięgu" klasy, która w języku OQL odgrywa rolę podobną do roli, jakąw SQL odgrywa relacja.

8.1.1. Działania na obiektach

Wkrótce przekonamy się, że język OQL, podobnie jak język SQL, umożliwia zapis działań związanych z relacjami i zbiorami. Jednak często trzeba wyrazić w języku działania dotyczące innego typu obiektów. Jeśli bowiem na przykład obiekty są dokumentami, to warto mieć dostęp do funk­cji, która odnajduje w nich pewne słowa kluczowe. Jeśli z kolei obiekta­mi są mapy lub inne obrazy, to warto mieć możliwość wyświetlania ich w określony sposób. Nawet na danych o konwencjonalnej strukturze rekor­dów, takich jak dane w przykładzie filmowym, można wykonać dodatko­we działania, na przykład generowanie wykresu określającego dla danej gwiazdy filmowej liczbę filmów, w których ona wystąpiła w poszczegól­nych latach.

W systemie SQL takie działania trzeba programować w podstawowym języku programowania, np. w C, w którym osadza się instrukcje SQL. Warto­ści między zmiennymi SQL a zmiennymi programu podstawowego przeka­zuje się, korzystając z mechanizmu opisanego w podrozdziale 7.1.

Powiązanie definicji ODL z językiem podstawowym jest łatwiejsze. Za­kłada się, że językiem podstawowym jest język zorientowany obiektowo, np. C++ lub Smalltalk. Deklaracje wyrażane w języku ODL są na tyle podobne do deklaracji w każdym języku obiektowym, że można je bezpośrednio tłu­maczyć z ODL. A ponadto zmienne z języka podstawowego, które odpowia­dają obiektom, mogą bezpośrednio reprezentować obiekty występujące w instrukcjach ODL".

W ODL występuje jeszcze (poza atrybutami i związkami) inny rodzaj elementów: metody, które ułatwiają łączenie języka podstawowego z deklara­cjami ODL oraz zapytaniami OQL. Metoda jest to funkcja powiązana z klasą. Metoda działa na obiekcie, a może mieć poza nim jeszcze inne argumenty. Przekonamy się, że w języku OQL metod można używać nieomal tak samo jak atrybutów klasy.

` Przypomnijmy tutaj, że typy zmiennych z języka podstawowego, takie jak np. typ cał­kowity, nie pasują do podstawowych typów danych SQL: do krotek i relacji. A zatem łączenie SQL i języka podstawowego jest z natury rzeczy niewygodne, była o tym mowa w podroz­dziale 7.1.

8.1. ELEMENTY ZAPYTAŃ W JĘZYKU ODL 4% 1

8.1.2. Deklarowanie sygnatur metod

W języku ODL można deklarować nazwy metod związanych z klasami oraz typy wejścia/wyjścia tych metod. Deklaracje te nazywa się sygnaturami i są one odpowiednikami deklaracji funkcji w Club C++ (w odróżnieniu od definicji funkcji, która jest kodem implementującym funkcję). Kod metody można zapisać w języku podstawowym, nie stanowi on elementu ODL.

Dlaczego sygnatury?

Zaleta wprowadzenia sygnatur polega na tym, że w trakcie implemento­wania schematu w języku programowania można automatycznie spraw­dzać, czy implementacja pasuje do projektu zawartego w schemacie. Oczywiście nie można w ten sposób kontrolować, czy implementowane funkcje działają zgodnie ze swoim znaczeniem, ale można chociaż spraw­dzać poprawność parametrów co do ich liczby i typu.

Deklaracje metod występują w deklaracji interfejsu razem z atrybutami i związkami. Jak to się dzieje zazwyczaj w językach wysokiego poziomu, tak i tutaj każda metoda jest związana z klasą (tzn. z interfejsem) i uruchamia się ją na obiektach tej klasy. Oznacza to, że obiekt jest „ukrytym" argumentem metody. Dzięki temu można tej samej nazwy metody używać w wielu róż­nych klasach, ponieważ to obiekt, na którym ma zostać wykonane dziakanie, przesądza o znaczeniu metody. Taki sposób korzystania z metod nazywa się przeładowaniem (dotyczy metod określanych dla więcej niż jednej klasy).

Syntaktyka deklaracji metody jest podobna do deklaracji funkcji w języ­ku C, przy czym jest ona rozszerzona o dwa elementy.

1. Parametry funkcji określa się jako in, out lub inout, co oznacza od­powiednio parametry wejścia, wyjścia lub wejścia-wyjścia. Wartości dwóch ostatnich typów parametrów mogą ulec zmianie w wyniku dzialania funkcji, natomiast wartość parametru typu in nie może być zmieniona. Dlatego parametry typu out oraz inout są przekazywane przez referencje, a parametr typu in przez wartość. Zauważmy, że funkcja może tworzyć także wartość powrotu, co stanowi dodatkowy, inny niż parametry out oraz inout, sposób przekazywania wyliczo­nych w niej wartości.

2. W funkcji mogą pojawiać się wyjc~tki, są to odpowiedzi funkcji na zdarzenia nietypowe, które nie mieszczą się w standardowych mecha­nizmach komunikowania się funkcji poprzez parametry lub wartość powrotu. Wyjątek na ogól oznacza nietypowe lub nieoczekiwane wa­runki, które są obsługiwane przez pewne funkcje także odpowiedzial­ne za ich występowanie (najczęściej bezpośrednio lub w ciągu wy­wolań). Na przykkad jako wyjątek można potraktować dzielenie przez

8. ZORIENTOWANE OBIEKTOWO J~ZYKI ZAPYTAN

zero. W ODL deklarację funkcji można zakończyć słowem kluczo­m raises, po którym w nawiasie umieszcza się listę wyjątków, które funkcja może wywołać.

PRZYKŁAD 8.1

I Na rysunku 8.1 można prześledzić kolejny etap ewolucji definicji interfejsu ', klasy Film, jej poprzednia faza była przedstawiona na rys. 2.6. Poza sygnatu­rami występujątu jeszcze dwie zmiany.

1. W wierszu 2) umieszczono deklarację extent. Szersze omówienie celu tej instrukcji nastąpi w p. 8.1.3.

2. W wierszu 3) zadeklarowano atrybuty tytuł oraz rok jako klucz re­lacji Film.

1) 2) 3) 4) 5) 6) 7)

8)

9)

10) 11) 12)

interface Film (extent Filmy key (tytuł, rok))

attribute string tytuł; attribute integer rok; attribute integer dlugość;

attribute enumeration (kolor, czarno-biały) typFilmu; relationship Set<Gwiazda> gwiazdy

inverse Gwiazda:: występujeW; relationship Studio należyDo

inverse Studio::posiada;

float długośćWGodzinach() raises(brakDługości); nazwiskaGwiazd(out Set<String>);

inneFilmy(in Gwiazda, out Set<Film>) raises ( brakTakiejGwiazdy);

i; RYSUNEK 8.1

Dołączenie sygnatur metod do klasy Film

Do deklaracji interfejsu dołączono także metody. W wierszu 10) umiesz­czono deklarację długośćWGodzinach. Zakładamy, że służy ona do utwo­rzenia wartości określającej długość filmu, którego obiekt wywołuje, ale wy­rażonej nie w minutach (jak to się dzieje w przypadku atrybutu długość), a skonwertowanej do liczby zmiennopozycyjnej, określającej jej wartość wy­rażoną w godzinach. Zauważmy, że ta funkcja nie ma żadnych parametrów. Obiekt Film, do którego stosuje się tę metodę, jest jedynym, „ukrytym" jej argumentem i w związku z tym kod tej funkcji będzie miał dostęp wyłącznie do długości danego obiektu klasy film.

8.1. ELEMENTY ZAPYTAŃ W J~ZYKU ODL 473

Można również zauważyć, że funkcja może się zakończyć wywokaniem wyjątku o nazwie brakDługości. Zakłada się, że uruchomienie wyjątku następuje, jeśli nie jest określona wartość atrybutu długość obiektu, do któ­rego stosuje się metodę długośćwGodzinach, albo gdy ta wartość jest nie­wlaściwa, np. ujemna.

Pamiętajmy, że z nazwy metody wcale nie musi wynikać jej faktyczne znaczenie. Na przykład metoda długośćWGodzinach może mieć kod, który zawsze zwraca wartość 3 .14159, bez względu na obiekt klasy Film, który ją uruchamia. Można także zaimplementować ją w ten sposób, że będzie zwra­cany kwadrat długości w postaci zmiennopozycyjnej. Każda funkcja, która nie ma argumentów (oprócz obiektu, który ją uruchamia), zwraca wartość zmiennopozycyjną i nie dopuszcza innych, poza brakDługości, wyjątków.

W wierszu 11) umieszczono inną sygnaturę o nazwie nazwiskoGwiaz­dy. Ta funkcja nie zwraca żadnej wartości, ale ma parametr wyjściowy, któ­rego typem jest zbiór tekstów. Zakładamy, że wartość tego parametru jest wyliczana przez kod funkcji i w wyniku powstaje zbiór napisów odpowiada­jący wartościom atrybutu nazwisko gwiazd filmu, którego obiekt uruchamia metodę. Jednak, jak zawsze, nie ma gwarancji, że zaimplementowana funkcja zachowa się w ten właśnie sposób.

W końcu, w wierszu 12), występuje deklaracja trzeciej metody: inne­Filmy. Ma ona jeden parametr wejściowy typu Gwiazda. Można oczekiwać, że ma ona następującą implementację. Zakładamy, że parametr Gwiazda oznacza jedną z gwiazd obiektu, który uruchamia metodę, jeśli tak nie jest, to uruchamia się wyjątek brakTakiejGwiazdy. Jeśli jednak jest to gwiazda występująca w danym filmie, to wartością parametru wyjściowego, którego typ określono jako zbiór filmów, staje się zbiór obejmujący wszystkie inne filmy, w których dana gwiazda występowała.

0

8.1.3. Zasięg klasy

Dla każdej klasy w języku ODL (dla interfejsu) można określić jej za­sięg, czyli nazwę zbioru obejmującego bieżące obiekty tej klasy. Deklaracja obejmuje slowo kluczowe extent, po którym podaje się nazwę zasięgu. Mu­si ona wystąpić bezpośrednio po deklaracji nazwy interfejsu (klasy).

W pewnym sensie zasięg klasy jest odpowiednikiem nazwy relacji, a de­finicja samej klasy jest podobna do deklaracji typu atrybutów tej relacji. Prze­konamy się, że zapytania OQL odwołują się do zasięgu, a nie do nazwy klasy.

PRZYKŁAD 8.2

W wierszu 2) na rys. 8.1 występuje definicja zasięgu klasy Film. Jego nazwą jest wyraz Filmy. Wartością Filmy jest zbiór wszystkich obiektów klasy Film, które w danej chwili są zapisane w bazie danych.

0

474 8. ZORIENTOWANE OBIEKTOWO JĘZYKI ZAPYTA7V

8.1.4. Ćwiczenia do podrozdziału 8.1

Ćwiczenie 8.1.1. Trzy typy produktów są przedstawione na rys. 8.2 jako trzy różne podklasy klasy Produkt. Łatwo jest zauważyć, że typ produktu można określić albo na podstawie wartości atrybutu typ, albo na podstawie podklasy, do której należy obiekt. Taki zapis nie stanowi najlepszego projektu, ponieważ dopuszcza występowa­nie obiektu podklasy PC, którego atrybut typ ma wartość „laptop" lub „dru­interface Produkt

jj (extent Produkty key model)

attribute integer model; attribute string producent; `' attribute string typ;

attribute real cena;

); interface PC: Produkt

(Extent PCety)

attribute integer szybkość; attribute integer ram; attribute integer hd; attribute string cd;

i;

interface Laptop: Produkt (extent Laptopy)

attribute integer szybkość; attribute inteqer ram; attribute integer hd; attribute real ekran;

); interface Drukarka: Produkt

(extent Drukarki) i

attribute boolean kolor; attribute string typDrukarki;

}a RYSUNEK 8.2

Schemat bazy Produkt w języku ODL

8.1. ELEMENTY ZAPYTAŃ W JĘZYKU ODL 4% S interface Klasa

(extent Klasy key nazwa)

attribute string nazwa; attribute string kraj; attribute integer liczbaDział; attribute integer średnica; attribute integer wyporność;

relationship Set<Okręt> okręty inverse Okręt:: klasaDla;

i;

interface Okręt (extent Okręty

key nazwa) {

attribute nazwa;

attribute integer wodowanie;

relationship Klasa klasaDla inverse Klasa:: okręty;

relationship Set<Wynik> wBitwie inverse Wynik:: tenOkręt; };

interface Bitwa

', (extent Bitwy key nazwa)

{ attribute nazwa; attribute Date dataBitwy; relationship Set<Wynik>rezultat

inverse Wynik:: taBitwa; );

interface Wynik (extent Wyniki) {

attribute enum Stat {ok, zatopiony, uszkodzony} status;

relationship Okręt tenOkręt inverse Okręt:: wBitwie;

relationship Battle taBitwa inverse Bitwa:: rezultat; ' }.

RYSUNEK 8.3

Baza danych okrętów wojennych wjęzyku ODL

4%6 8. ZORIENTOWANE OBIEKTOWO JĘZYKI ZAPYTAŃ

karka". Ale powstają tu również interesujące możliwości dotyczące sposobu zada­wania zapytań.

Ponieważ atrybut typ dziedziczy się z klasy Produkt do podklasy Drukar­ka, a więc można atrybut typ w podklasie nazwać typDrukarki. Będzie on okre­ślał typ drukarki (np. laserowa lub atramentowa), podczas gdy atrybut typ w klasie Produkt przyjmuje wartość PC, laptop lub drukarka.

Do kodu ODL, zapisanego na rys. 8.2, należy dołączyć sygnatury metod odpo­wiednie dla następujących funkcji:

*a) Odjęcie wartości x od ceny produktu. Zakładamy, że x jest parametrem wej­ściowym funkcji.

*b) Zwrócenie szybkości produktu, jeśli jest to PC lub laptop, albo uruchomie­nie wyjątku nieKomputer w przeciwnym razie.

c) Określenie wartości atrybutu ekran laptopa według zadanego x.

!d) Stwierdzenie, jeśli produkt p określa wartość wejścia, a produkt g urucha­mia metodę, czy g ma większą szybkość i jednocześnie niższą cenę niż p. Jeśli p nie jest właściwym produktem (tzn. nie jest ani PC, ani laptopem), to należy uruchomić wyjątek błądWejścia, a wyjątek brakSzybkości, jeśli q jest niewłaściwym produktem.

Ćwiczenie 8.1.2. Na rysunku 83 przedstawiono opis w ODL schematu bazy danych „Okręty". Należy do niego dołączyć sygnatury następujących metod:

a) Obliczyć moc bojową okrętu, tzn. liczbę dział przemnożoną przez sześcian średnicy.

b) Należy znaleźć okręt bliźniaczy danego okrętu. Jeśli w danej klasie jest tylko jeden egzemplarz, to uruchomić wyjątek brakBliźniaczego.

c) Podana jest bitwa b jako parametr, a metodę stosuje się dla okrętu s. Należy odnaleźć wszystkie okręty, który uczestniczyły w bitwie b, jeśli s także w niej uczestniczył. Jeśli s nie uczestniczył w bitwie b, to należy uruchomić wyjątek nieUczestniczy.

d) Jako parametry podaje się nazwę i rok wodowania okrętu. Okręt o tych atry­butach należy dołączyć do klasy okrętów, która uruchamia metodę.

8.2. Wprowadzenie do j ęzyka OQL

W bieżącym rozdziale opiszemy ogólnie obiektowy język zapytań (OQL). Ani w tym, ani w następnych dwóch rozdziałach opis nie będzie tak obszerny jak w przypadku opisu SQL. Objaśnimy tylko najważniejsze in­strukcje i właściwości tego języka, ale wiele elementów języka OQL nie bę­dzie uwzględnionych. Często te pozostałe możliwości występują albo w języ­ku SQL, albo w typowych obiektowych językach programowania.

W języku OQL, inaczej niż w konwencjonalnych językach programowa­nia, nie ma sposobu jawnego definiowania funkcji. Notacja OQL przypomina raczej notację SQL, dostarczając mechanizmu do opisu pewnych zapytań na

8.2. WPROWADZENIE DO JĘZYKA OQL 4%%

wyższym poziomie abstrakcji niż można było to wyrazić w typowych instruk­cjach języków programowania, np. w C. W założeniu OQL ma być rozszerze­niem pewnego podstawowego języka zorientowanego obiektowo, takiego jak C++, Smalltalk lub Java. Do obiektów można mieć zatem dostęp zarówno poprzez typowe instrukcje języka podstawowego, jak i poprzez zapytania OQL. Ta możliwość przemieszania instrukcji języka programowania i zapy­tań OQL, dzięki której unika się konieczności pośredniego przekazywania wartości między dwoma językami, stanowi o przewadze opisywanej techniki nad osadzaniem SQL w języku podstawowym, które było omawiane w pod­rozdziale 7.1.

8.2.1. Obiektowa wersja przykładu filmowego

Aby lepiej zobrazować pojęcia OQL, posłużymy się przykładem znanych już klas Film, Gwiazda oraz Studio. Na rysunku 8.1 zamieszczono defini­cję klasy Film. Natomiast definicje klas studio oraz Gwiazda zostaly za­czerpnięte z rys. 2.6, rozszerzyliśmy je na rys. 8.4 o deklaracje kluczy i zasię­gu. Nie wprowadziliśmy jeszcze jednak deklaracji metod.

interface Gwiazda (extent Gwiazdy key nazwa)

attribute string nazwisko; attribute Struct Adr

{string ulica, string miasto} adres; relationship Set<Film> występujeW inverse Film::gwiazdy;

interface Studio (extent Studia key nazwa)

attribute string nazwa; attribute string adres; relationship Set<Filmy> posiada

inverse Film::należyDo;

RYSUNEK 8.4

Fragment zorientowancj obiektowo bazy danych filmów

478

8.2.2. System typów

8. ZORIENTOWANE OBIEKTOWO JĘZYKI ZAPYTAŃ

Struktura typów w języku OQL jest określona tak, aby pasowała do de­klaracji ODL (patrz p. 2.1.7). Ale w języku OQL nie ma ograniczeń na głębo­kość zagnieżdżania konstruktorów typów.

Przy okazji omawiania systemu typów obowiązującego w językach pro­gramowania trzeba było odróżniać deklarację typu zmiennej (czasami nazywa­ną obiektem mutowalnym) od opisu wartości stałej (nazywaną również obiektem niemutowalnym). W instrukcjach OQL korzysta się albo ze zmiennych zadekla­rowanych w języku podstawowym, gdzie często korzysta się z zapisu ODL, który wprowadzono w p. 2.1.7, albo z podobnego do niego. W języku ODL, który służy do definiowania danych, nie ma potrzeby definiowania stałych, ale w programach OQL jest taka konieczność. Stąd też trzeba się dowiedzieć, w jaki sposób tworzyć stałe określonego typu w języku OQL. Poniżej przed­stawiamy elementy i sposób ich łączenia przy opisie stałych w języku OQL:

1. Typy podstawowe:

a) Typy atomowe: całkowity, zmiennopozycyjny, znak, tekst oraz lo­giczny. Są one reprezentowane tak samo jak w SQL, tyle tylko, że do wyróżniania tekstów używa się podwójnego cudzysłowu.

b) Typ wyliczeniowy. Jego wartości są na bieżąco definiowane w ODL. Każdej z tych wartości można użyć jako stałej.

2. Typy złożone są tworzone przez zastosowanie konstruktora dowolnego typu:

a) Set ( . ) b)Bag(...). c) List ( . . . ) . d) Array ( . . . ) . e) Struct ( . . . ) .

Pierwsze cztery z nich są określane wspólnym mianem kolekcji. Zarówno kolekcje, jak i struktury można dowolnie stosować do wartości odpo­wiednich typów podstawowych lub złożonych. Jednakże w przypadku stosowania operatora struct trzeba określać nazwę pola i wartość w tym polu. Po każdej nazwie pola występuje dwukropek, a następnie wartość; pary złożone z pola i wartości oddziela się przecinkami.

PRZYKŁAD 8.3

Wyrażenie bag(2,1,2) oznacza wielozbiór, w którym liczba całkowita 2 występuje dwa razy, a liczba całkowita 1 tylko jeden raz. Wyrażenie:

struct (ala: bag(2,1,2), bar: „flis")

oznacza strukturę o dwóch polach. Pierwsze, o nazwie ala, zawiera wielo­zbiór wraz z jego wartościami, a drugie, które nazywa się bar, ma wartość tekstową „ f 1 i s ".

D

8.2. WPROWADZENIE DO JĘZYKA OQL 479

8.2.3. Wyrażenia ścieżkowe

Składowe zmiennych typu złożonego są dostępne przez zastosowanie notacji kropkowej, podobnej do kropki używanej w języku C, a także do kropki w języku SQL. Obowiązuje przy tym następująca zasada: Jeśli a ozna­cza pewien obiekt klasy C, ap określa jakiś element tej klasy - jest to atrybut, związek bądź metoda - to a.p oznacza wynik „zastosowania" p do obiektu a. Oznacza to:

1. Jeśli p jest atrybutem, to a.p oznacza wartość tego atrybutu dla obiektu a.

2. Jeśli p jest związkiem, to a.p jest obiektem lub kolekcją obiektów do­stępną z a przez związek p.

3. Jeśli p jest metodą (być może z parametrami), to a.p oznacza wynik zastosowania metody p do obiektu a.

Strzałki zamiast kropek

W OQL strzałka ~ stanowi synonim kropki. Można wyczuć w tym roz­wiązaniu wpływ języka C, w którym kropka i strzałka służą do uzyskania dostępu do składowych struktury. Jednakże w języku C zakres stosowania tych dwóch operatorów jest inny, natomiast w OQL korzysta się z nich wymiennie. W języku C a . f oznacza, że a jest strukturą, podczas gdy pyf oznacza, że p jest wskaźnikiem do tej struktury. W obu przypad­kach wynikiem jest po prostu wartość pola f tej struktury.

PRZYKŁAD 8.4

Niech mój Film oznacza zmienną w języku podstawowym, której wartością jest pewien film. Wówczas:

Wartościąwyrażenia mój Film. długość jest długość tego filmu.

Wartością wyrażenia mój Film. długośćWGodzinach ( ) jest liczba rzeczywista, określająca czas trwania filmu wyrażony w godzinach, a obliczony przez zastosowanie metody dlugośćwGodzinach do obiektu mój Film.

Wartością wyrażenia mój Film. gwiazdy jest zbiór obiektów typu Gwiazda, powiązanych z filmem mój Film poprzez związek gwiazdy.

WartoścląwyrażeniamójFilm.nazwiskaGwiazd (mojaGwiazda) nie zwraca żadnej wartości (tzn. w C++ typem tego wyrażenia jest void). Ubocznym efektem działania tej metody jest przypisanie zmiennej wynikowej mojaGwiazda zbioru napisów, które oznaczają nazwiska gwiazd występujących w filmie.

0

4ÓO 8. ZORIENTOWANE OBIEKTOWO J~ZYKI ZAPYTAŃ

Jeśli ma to sens, to można tworzyć wyrażenia zawierające wiele kropek. Jeśli na przykład mój Film oznacza obiekt typu Film, to wyrażenie mój­Film. należyDo oznacza obiekt typu studio, do którego ten film należy, a z kolei wyrażenie mój Film. należyDo. nazwa oznacza tekst z nazwą tego studia.

8.2.4. Wyrażenia typu Select-From-Where

W języku OQL można wyrażać zapytania podobnie jak w języku SQL, stosując składnię wyrażenia select-from-where. Podajemy teraz przykład za­pytania o rok produkcji filmu Przeminę~o z wiatrem.

SELECT mrok FROM Filmy m

WHERE m.tytuł = „Przeminęło z wiatrem";

Zauważmy, że poza podwójnym cudzysłowem przy stałej tekstowej to zapy­tanie jest takie jak w języku SQL. Nie jest tylko oczywiste, czy można ocze­kiwać, aby klauzula EROM w SQL:

EROM Filmy AS m

była zapisana bez słowa kluczowego As. Ale w języku OQL słowo kluczowe AS jest opcjonalne, tak jak w SQL. Wydaje się, że w OQL jest ono zupełnie zbędne, ponieważ tę frazę czyta się w sposób następujący: Filmy m oznacza, że m jest zmienną, która przyjmuje po kolei wartości obiektów z zasięgu Fil­my; zasięg oznacza stan bieżący zbioru obiektów klasy Film.

Ogólna postać wyrażenia select-from-where w języku OQL składa się z następujących elementów.

1. Słowo kluczowe SELECT, po którym występuje lista wyrażeń.

2. Słowo kluczowe EROM, po którym występuje lista deklaracji zmien­IlyCh.Zmienną deklaruje się, podając:

a) Wyrażenie, którego wartość jest typu kolekcja, tzn. jest to zbiór, wielozbiór itd.

b) Opcjonalnie słowo kluczowe AS. c) Nazwa zmiennej.

Najczęściej wyrażenie (a) stanowi zasięg pewnej klasy: np. Filmy w naszym przykładzie jest zasięgiem dla klasy Film. Zasięg w tym kontekście stanowi analogię relacji w klauzuli FROM w języku SQL. Ale w języku OQL, można w deklaracji zmiennej użyć dowolnego wy­rażenia oznaczającego kolekcję, np. może to być kolejne wyrażenie typu select-from-where. W języku SQL2 nie ma bezpośredniej analo­

8.2. WPROWADZENIE DO JĘZYKA OQL

481

gii tego przypadku, ale niektóre komercyjne systemy SQL dopusz­czają występowanie podzapytań w klauzuli FROM.

3. Slowo kluczowe WHERE i wyrażenie logiczne. W tym wyrażeniu, po­dobnie jak w wyrażeniach w klauzuli sELECT, można jako operandów używać tylko stałych i zmiennych zadeklarowanych w klauzuli EROM. Operatory relacji są takie jak w języku SQL z tym, że do oznaczenia „nierówne" stosuje się !_, a nie < >. Operatory logiczne są takie same jak w języku SQL: AND, OR i NOT.

W wyniku podzapytania powstaje wielozbiór obiektów. Powstaje on przez rozpatrywanie wszystkich możliwych wartości zmiennych określonych w klauzuli FROM, w pętli zagnieżdżonej. Jeśli jakaś kombinacja wartości tych zmiennych spelnia wyrażenie warunkowe klauzuli wHERE, to obiekt opisany w klauzuli SELECT zostaje dołączony do wielozbioru będącego wynikiem instrukcji select-from-where.

PRZYKŁAD 8.5

Poniżej przedstawiamy bardziej złożone zapytanie OQL, które obrazuje strukturę select-from-where.

SELECT s.nazwisko

EROM Filmy m, m.gwiazdy s WHERE m.tytuł = „Casablanca"

Wynikiem tego zapytania mają być nazwiska gwiazd występujących w filmie Casablanca. Zwróćmy uwagę na kolejność wyrazów w klauzuli EROM. Naj­pierw określa się m jako dowolny obiekt klasy Film, stwierdzając, że wartość m ma być pobrana z zasięgu klasy Filmy. Następnie dla każdej wartości m oczekujemy, że s będzie obiektem klasy Gwiazda, należącym do zbioru m.gwiazdy, czyli do zbioru gwiazd filmu m. Czyli w pętli zagnieżdżonej roz­patruje się wszystkie pary (m, s ) , takie że m jest filmem, a s jest gwiazdą z tego filmu. Szkic przetwarzania tego wyrażenia można przedstawić w na­stępujący sposób:

DLA każdego m należącego do Filmy WYKONUJ

DLA każdego s należącego do m. gwiazdy WYKONUJ JEŚLI m.tytuł = „Casablanca" TO

dołącz s.nazwisko do wynikowego wielozbioru

Klauzula wHERE zawęża nasze rozważania do tych par, w których war­tość zmiennej m jest obiektem klasy Film o tytule Casablanca. Następnie w klauzuli sELECT powstaje wielozbiór (który w tym przypadku akurat bę­dzie po prostu zbiorem) zawierający wszystkie wartości atrybutu nazwisko obiektów gwiazdy, które są wartościami s w tych parach (m, s) spełniają­

4Ó2 8. ZORIENTOWANE OBIEKTOWO JĘZYKI ZAPYTAŃ

cydr klauzulę WHERE. Te nazwiska są nazwiskami gwiazd w zbiorze m~.. gwiazdy, gdzie m~. oznacza obiekt reprezentujący film Casablahca.

0

8.2.5. Eliminowanie powtórzeń

Od strony technicznej zapytania, takie jak w przykładzie 8.5, tworzą w wyniku wielozbiory, a nie zbiory. Czyli w języku OQL powtórzono z języ­ka SQL, że przez domniemanie z wyniku nie usuwa się powtórzeń, chyba że nastąpi jawne polecenie ich eliminowania. I tak samo jak w języku SQL pole­cenie eliminowania powtórzeń polega na umieszczeniu słowa kluczowego DISTINCT w klauzuli SELECT.

PRZYKŁAD 8.6

Zapytajmy teraz o nazwiska gwiazd występujących w filmach Disneya. Po­niższe zapytanie wypełnia to zadanie i eliminuje powtarzające się nazwiska, gdy ta sama gwiazda występuje w kilku filmach Disneya.

SELECT DISTINCT s.nazwisko FROM Filmy m, m. gwiazdy s

WHERE m.należyDo.nazwa = „Disney"

Sposób wykonania tego zapytania jest podobny do opisanego w przykła­dzie 8.5. Podobnie jak tam, w dwóch zagnieżdżonych pętlach rozważa się wszystkie pary złożone z filmu i gwiazdy z tego filmu. Teraz jednak warun­kiem dla pary (m, s ) jest to, że Disney jest nazwą studia, którego obiektem Studio jest m. należyDo.

O

8.2.6. Zkożone typy wyjścia

Wyrażenie lub wyrażenia występujące w klauzuli SELECT nie muszą być zmiennymi prostymi. Mogą to być dowolne wyrażenia, także takie, które powstają w wyniku stosowania konstruktorów typu. Na przykład można użyć konstruktora typu Struct do kilku wyrażeń, a następnie zastosować zapyta­nie select-from-where, które tworzy zbiór lub wielozbiór struktur.

PR7YKŁAD 8.7

Chcemy teraz otrzymać zbiór par gwiazd mieszkających pod tym samym adresem. Można to uzyskać w wyniku następującego zapytania:

SELECT DISTINCT STRUCT (gwiazdal: sl, gwiazda2: s2) EROM Gwiazdy sl, Gwiazdy s2

WHERE sl.adr = s2.adr AND sl.nazwisko < s2.nazwisko

8.2. WPROWADZENIE DO JĘZYKA OQL 483

Zatem będziemy rozważać wszystkie pary gwiazd sl i s2. W klauzuli WHERE sprawdza się warunek, że mają one ten sam adres. Poza tym sprawdza się warunek, że nazwisko pierwszej gwiazdy poprzedza nazwisko drugiej gwiaz­dy w porządku alfabetycznym, dzięki czemu unikamy tworzenia par złożo­nych z powtórzonego nazwiska gwiazdy oraz eliminuje się powtórzenia tej samej pary zapisanej w odwrotnym porządku.

Każda para, która spełnia oba warunki, tworzy strukturę. Typem tej struk­tury jest rekord o dwóch polach, nazwanych gwiazdal i gwiazda2. Oba pola są typu Gwiazda, ponieważ taki jest typ zmiennych sl i s2, których wartości są także wartościami pól. Formalnie typ tej struktury wygląda następująco:

Struct N {gwiazdal: Gwiazda, gwiazda2: Gwiazda}

gdzie N jest dowolną nazwą. W wyniku zapytania powstaje zbiór struktur typu N, tzn.:

Set<Struct N{ gwiazdal: Gwiazda, gwiazda2: Gwiazda}>

Zauważmy, że typ wyniku zapytania może występować w programie OQL, ale nie można takiego typu używać jako typu atrybutu lub związku w deklara­cjach ODL.

O

Niekiedy można uzyskać taki sam efekt jak w przykładzie 8.7, nie defi­niując jawnie typu struktury, a umieszczając po prostu listę składowych i nazw pól po słowie kluczowym SELECT. A więc pierwszy wiersz z przykła­du 8.7 można zapisać jako:

SELECT gwiazdal: sl, gwiazda2: s2

8.2.7. Podzapytania

Wyrażenie select-from-where może wystąpić w każdym kontekście, któ­ry jest odpowiedni do użycia kolekcji (czyli np. zbioru). Jedyną niespodzianką może być umieszczenie podzapytania w klauzuli EROM, gdzie kolekcję stano­wiącą zakres zmiennej można tworzyć dynamicznie przez użycie wyrażenia select-from-where. Taką możliwość, czyli korzystanie z wyrażeń definiują cydr tabele w kontekstach właściwych dla nazw tabeli, zawiera także standard SQL3 i jest ona dostępna już w niektórych komercyjnych systemach SQL.

PRZYKŁAD 8.8

Powtórzmy zadanie z przykładu 8.6, w którym trzeba byto określić zbiór gwiazd występujących w filmach produkcji Disneya. Najpierw tworzymy zapytanie o filmy produkowane przez Disneya:

484 8. ZORIENTOWANE OBIEKTOWO JĘZYKI ZAPYTAŃ

SELECT m EROM Filmy m

WHERE m.należyDo.nazwa = „Disney"

Teraz używamy tego zapytania jako podzapytania definiującego zbiór stano­wiący zakres dla zmiennej d, która reprezentuje filmy Disneya.

SELECT DISTINCT s.nazwisko EROM (SELECT m

EROM Filmy m

WHERE m.należyDo.nazwa = „Disney") d, d. gwiazdy s

Takie przedstawienie wyrażenia „znajdź wszystkie gwiazdy z filmów Disneya" nie jest bardziej zwięzłe niż to, które podano w przykładzie 8.6, a jest ono na­wet pewnie mniej zwięzłe. Jednakże stanowi ono dobrą ilustrację nowych moż­liwości konstruowania zapytań w języku OQL. W omawianym zapytaniu w klauzuli FROM występują dwie pętle zagnieżdżone. W pierwszej z nich zmienna d przebiega zbiór filmów Disneya, który powstaje w wyniku podza­pytania w klauzuli FROM. W pętli wewnętrznej zmienna s przebiega zbiór gwiazd występujących w filmie d. Zauważmy, że nie potrzeba w tym przypad­ku klauzuli WHERE.

O

8.2.8. Porządkowanie wyniku

Wynikiem wyrażenia select-from-where w języku OQL jest albo wielo­zbiór, albo (jeśli skorzysta się z DISTINCT) zbiór. Można określić wynik jako listę, a jednocześnie wybrać sposób porządkowania elementów listy, jeśli na końcu instrukcji select-from-where dopisze się klauzulę ORDER BY. Klauzula ORDER BY w języku OQL jest całkiem podobna do takiej klauzuli w języku SQL. Po słowie kluczowym ORDER BY umieszcza się listę wyrażeń. Pierwsze z nich wylicza się dla każdego obiektu dopisywanego do wyniku zapytania i określa ono pozycję tego obiektu na liście wynikowej. Jeśli wartości na li­ście powtarzają się, to o porządku elementów decyduje drugie wyrażenie w klauzuli ORDER By, a potem trzecie itd.

PRZYKŁAD 8.9

Znajdźmy ponownie zbiór filmów Disneya, ale tym razem wynik ma być listą filmów uporządkowanych według czasu trwania. W przypadku filmów o równej długości obowiązuje kolejność alfabetyczna. Oto nowa postać zapytania:

SELECT m EROM Filmy m

8.2. WPROWADZENIE DO JĘZYKA OQL 48S

WHERE m.należyDo.nazwa = „Disney" ORDER BY m.długość, m.tytuł

Pierwsze trzy wiersze są identyczne z podzapytaniem z przykładu 8.8. W czwartym wierszu określa się, że obiekty m utworzone w wyniku zapytania select-from-where mają być uporządkowane według wartości atrybutu m. długość (tzn. według długości filmu), a jeśli długości są równe, to według wartości atrybutu m. tytuł (tzn. według tytułu filmu). Zapytanie tworzy listę obiektów klasy Film. Przez domniemanie przyjmuje się, że porządek ma być rosnący, ale można jawnie określić, czy ma on być rosnący, czy malejący, umieszczając odpowiednie słowo kluczowe: ASC lub DESC na końcu klauzuli ORDER BY, tak samo jak to się robi w języku SQL.

0

8.2.9. Ćwiczenia do podrozdzia~u 8.2

Ćwiczenie 8.2.1. Korzystając ze schematu ODL utworzonego w ćwiczeniu 8.1.1 oraz rys. 8.2, należy zapisać w języku OQL następujące zapytania:

*a) Należy określić numery wszystkich produktów typu PC, których cena wy­nosi poniżej 2000 $.

b) Należy określić numery wszystkich produktów typu PC z RAM co najmniej 32 megabajty.

*!c) Należy odnaleźć wszystkich producentów, którzy wytwarzają co najmniej dwa różne modele drukarek laserowych.

d) Należy podać zbiór par (r, h) takich, że pewien PC lub laptop ma r mega­bajtów RAM i h gigabajtów dysku twardego.

e) Utworzyć listę PC (obiektów, a nie numerów modeli) w porządku rosnącym ze względu na szybkość procesora.

!f) Utworzyć listę numerów modeli laptopów z co najmniej 16 megabajtami RAM w porządku malejącym ze względu na wielkość ekranu.

!Ćwiczenie 8.2.2. Należy powtórzyć wszystkie punkty z ćwiczenia 8.2.1, każde za­pytanie zapisując w postaci zawierającej podzapytanie.

Ćwiczenie 8.2.3. Korzystając ze schematu ODL utworzonego w ćwiczeniu 8.1.2 oraz rys. 8.3, należy zapisać w języku OQL następujące zapytania:

a) Należy określić nazwy tych klas, w których okręty mają co najmniej dzie­więć dział.

b) Należy odszukać wszystkie okręty (obiekty, a nie nazwy), które mają co najmniej dziewięć dział.

c) Należy wyszukać nazwy okrętów o wyporności poniżej 30 000 ton. Wynik należy przedstawić w postaci listy uporządkowanej według roku wodowa­nia, a w przypadku powtórzeń alfabetycznie.

d) Należy określić pary okrętów bliźniaczych (tzn. z tej samej klasy). Należy pamiętać o tym, że elementami par mają być obiekty, a nie nazwy okrętów.

486 8. ZORIENTOWANE OBIEKTOWO JĘZYKI ZAPYTAŃ

!e) Należy znaleźć nazwy bitew, w których zatonęły okręty z co najmniej dwóch różnych państw.

!!~ Należy podać nazwy tych wszystkich bitew, w których nie umieszczono żadnych danych o zniszczonych okrętach.

8.3. Dodatkowe postacie wyrażeń w języku OQL

W bieżącym rozdziale przyjrzymy się jeszcze innym operatorom, obok select-from-where, pomocnym przy tworzeniu wyrażeń. Do tych operatorów zaliczamy kwantyfikatory logiczne - dla-każdego oraz istnieje - operatory agregowania i grupowania oraz operatory algebry zbiorów -suma, przecięcie oraz różnica.

8.3.1. Wyrażenia kwantyfikowane

Można sprawdzić, czy wszystkie albo co najmniej jeden element zbioru spełnia pewien warunek. Aby sprawdzić, czy wszystkie elementy zbioru S spełniają warunek C(x), gdzie x jest zmienną, używamy następującego wyra­żenia OQL:

FOR ALL x IN S: C(x)

Wynik tego wyrażenia wynosi TRUE, jeśli wszystkie elementy x ze zbioru S po kolei spełniają warunek C(x), a FALSE w przeciwnym przypadku.

Z kolei wyrażenie

EXISTS x IN S: C(x)

ma wartość TRUE, jeśli w S występuje co najmniej jeden taki element x, że warunek C(x) ma wartość TRUE, w przeciwnym razie wyrażenie przyjmuje wartość FALSE.

PRZYKŁAD 8.10

Na rysunku 8.5 przedstawiamy kolejny sposób wyrażenia zapytania „znajdź wszystkie gwiazdy filmów Disneya". Tutaj uwagę koncentrujemy na gwieź­dzie s i pytamy, czy są takie filmy m produkcji Disneya, w których ona wy­stępuje. W wierszu 3) zapisano polecenie rozpatrzenia wszystkich filmów m ze zbioru filmów s . występuj ew, który określa zbiór tych wszystkich fil­mów, w których dana gwiazda s wystąpiła. Następnie w wierszu 4) spraw­dzamy, czy film m został wyprodukowany przez Disneya. Jeśli znajdzie się chociaż jeden taki film m, to wartością wyrażenia EXISTS z wierszy 3) i 4) będzie TRUE, inaczej będzie ona równa FALSE.

0

8.3. DODATKOWE POSTACIE WYRAŻEŃ W JĘZYKU OQL

1) SELECT s

2) EROM Gwiazdy s

3) WHERE EXISTS m IIV s.WystępujeW: 4) m.należyDo.nazwa = „Disney"

RYSUNEK 8.5

Korzystanie z zapytania egzystencjalnego

1) SELECT s

2) FROM Gwiazdy s

3) WHERE FOR ALL m IN s.WystępujeW: 4) m.należyDo.nazwa = „Disney"

RYSUNEK 8.6

Korzystanie z zapytania z kwantyfikatorem ogólnym

PRZYKŁAD 8.11

487

Skorzystamy teraz z operatora dla-każdego do zapisu zapytania o te gwiazdy filmowe, które wystąpiły wyłącznie w filmach produkowanych przez Disneya. W naszym przypadku ten zbiór będzie pusty. Można do tego zapytania dolą czyć jeszcze jeden warunek, żeby do tego zbioru dolączać tylko te gwiazdy, które wystąpily chociaż w jednym filmie, ale tę poprawkę pozostawimy do wykonania jako ćwiczenie. Zapytanie zostalo przedstawione na rys. 8.6.

0

8.3 .2. Wyrażenia agreguj ące

W języku OQL można korzystać z takich samych pięciu operatorów agregowania jak w języku SQL, tzn.: AvG, CoUNT, sUM, MIN i MAx. Jednakże w języku SQL byly one stosowane do poszczególnych kolumn tabeli, a w języku OQL te same operatory stosuje się do kolekcji, których elementy muszą być określonego typu. A więc: CoUNT można stosować do dowolnej kolekcji, sUM oraz AvG można zastosować tylko do kolekcji typów arytme­tycznych, jak np. typ integer, a z kolei MIN i MAx można zastosować do ko­lekcji elementów typu, który daje się porządkować (np. typ całkowity lub tekstowy).

PRZYKŁAD 8.12

Przy wyliczaniu średniej dkugości filmu trzeba utworzyć wielozbiór zawie­rający dlugości wszystkich filmów. Zauważmy, że nie należy w tym przy­padku tworzyć zbioru, ponieważ wówczas dwie takie same długości dwóch rożnych filmów zostałyby potraktowane jako jedna. Zapytanie ma następu­jącą postać:

AVG (SELECT m. długość EROM Filmy m)

4ÓÓ 8. ZORIENTOWANE OBIEKTOWO JĘZYKI ZAPYTAN

Korzystamy tutaj z podzapytania po to, by wyodrębnić składowe długości filmów. W wyniku tego podzapytania powstaje wielozbiór z dlugościami wszystkich filmów i po zastosowaniu do niego operatora AvG otrzymujemy właściwy wynik.

0

8.3.3. Wyrażenia GROUP BY

Klauzula GROUP BY, znana z SQL, rozszerza się w bardzo ciekawy spo­sób w języku OQL. Składa się ona z następujących elementów.

1. Słowo kluczowe GROUP BY.

2. Lista atrybutów grupujc~cych, oddzielanych przecinkami. Każdy z nich składa się z kolei z:

a) nazwy pola, b) przecinka oraz c) wyrażenia.

A więc postać klauzuli GROUP BY można przedstawić następująco:

GROUP BY f :el, f2:ez, ..., f„:en

Klauzulę GROUP BY umieszcza się na końcu zapytania select-from­-where. Wyrażenie:

eI, ez, ..., e„

może zawierać zmienne wymienione w klauzuli EROM. Aby ułatwić objaśnie­nie działania GROVP BY, ograniczymy się do dość popularnego przypadku, gdy w klauzuli from występuje tylko jedna zmienna x. Zmienna x przebiega pewną kolekcję C. Dla każdego elementu z C, powiedzmy i, który spełnia warunek klauzuli WHERE, oblicza się wartości wszystkich wyrażeń klauzuli GROUP BY i w ten sposób otrzymujemy wartości e,(i), ez(i), ..., e„(i). Ta lista wartości tworzy grupę, do której należy wartość i.

Wartość klauzuli GROUP BY jest zbiorem struktur. Elementy tego zbioru mają następującą postać:

St ruct( fl:vl, fz:vz, ..., f„:vn, part it ion: P)

Pierwsze n pól określa grupy. To znaczy vl, vz, ..., vn są wartościami powsta­łymi w wyniku obliczenia wyrażeń e,(i), ez(i), ..., e„(i) dla co najmniej jednej wartości i z kolekcji C, która spełnia warunek WHERE.

Ostatnie pole jest nazwane partition. Intuicyjnie jego wartość P jest równa pewnej wartości i, która należy do tej grupy. A mówiąc dokładniej, P jest wielozbiorem złożonym ze struktur o następującej postaci:

Struct(x:i)

gdzie x jest zmiennąklauzuli EROM.

8.3. DODATKOWE POSTACIE WYRAŻEŃ W JĘZYKU OQL 489

Klauzula sELECT wyrażenia select-from-where, w którym występuje klauzula GROUP sY, może zawierać tylko te pola, które są elementami GROUP BY, a Więcf, f, ..., f oraz partition. Poprzez partition można odwoły­wać się do zmiennej x, która występuje w strukturach będących elementami wielozbioru P, który z kolei jest wartością pola partition. Krótko mówiąc, można odwoływać się do zmiennej x, która występuje w klauzuli FROM, ale można to zrobić tylko poprzez operator agregowania, który jest zastosowany do wielozbioru P.

PRZYKŁAD 8.13

Utwórzmy tabelę zawierającą calkowitą dlugość wszystkich filmów wypro­dukowanych w poszczególnych latach przez poszczególne studia. A zatem chcemy wyrazić w języku OQL utworzenie wielozbioru struktur - każda struktura ma trzy skladowe: studio, rok oraz cakkowitą dlugość wyproduko­wanych w tym czasie filmów w tym studio. Zapytanie zostało przedstawione na rys. 8.7.

SELECT std, r, sumaDługości: SUM(SELECT p.m.długość EROM partition p) EROM Filmy m

GROUP BY std: m. studio, r: mrok

RYSUNEK 8.7

Grupowanie filmów według studia i roku

Zacznijmy od omówienia działania klauzuli EROM w tym zapytaniu. Wi­dzimy, że zmienna m przebiega zbiór obiektów klasy Film. A więc m odgry­wa rolę x z naszego ogólnego opisu. W klauzuli GROUP BY występują dwa pola std i r, które odpowiadają kolejno wyrażeniom: m. studio oraz m. rok.

Na przykład film Pretty Woman został wyprodukowany w studiu Disneya w 1990 roku. Jesli m oznacza ten film, to m. studio ma wartość „Disney", a m. rok ma wartość 1990. A więc jednym z elementów zbioru tworzonego w klauzuli GROUP BY jest struktura następująca:

Struct(std:„Disney", r:1990, partition:P)

gdzie P jest zbiorem struktur. Należy do niego między innymi następująca struktura:

Struct (m:m~w)

gdzie m~W oznacza obiekt opisujący film Pretty Woman. W P występują struktury o jednej skladowej oznaczającej film wyprodukowany w studiu Disneya w roku 1990.

49O 8. ZORIENTOWANE OBIEKTOWO JĘZYKI ZAPYTAŃ

Zajmijmy się teraz klauzulą sELECT. Dla każdej struktury, która należy do zbioru powstałego jako wynik klauzuli GROUP BY, tworzymy nową struk­turę, która jest wielozbiorem wyniku zapytania. Pierwsza składowa to std, tzn. nazwą pola jest std, a jego wartością jest wartość pola std struktury wynikowej GROUP BY. Druga sk~adowa powstaje w sposób analogiczny i zawiera pole r oraz wartość pola r z wyniku GROUP BY.

Trzecia składowa w każdej strukturze wyjściowej ma postać następującą:

SUM(SELECT p.m.długość EROM partition p)

Zwróćmy tu najpierw uwagę na fakt, że zmienna p przebiega elementy pola partition struktury wynikowej GROUP BY. Każda wartość p ma postać struct (m: o), gdzie o jest obiektem typu flm. A zatem wyrażenie p.m od­nosi się do obiektu o. Stąd też p.m.długość oznacza składową długość obiektu klasy Film.

W związku z tym zapytanie select-from-where tworzy wielozbiory dłu­gości filmów w poszczególnych grupach. Zatem, jeśli na przykład wartością std jest „Disney", a wartością r - 1990, to wynikiem klauzuli select-from jest wielozbiór długości filmów wyprodukowanych przez Disneya w 1990 roku. Po zastosowaniu do tego wielozbioru operatora SUM otrzymamy sumę wszystkich długości filmów należących do tej grupy. A więc jedna ze struktur wielozbioru wyjściowego ma następującąpostać:

Struct(std: „Disney", r: 1990, sumaDługości: 1234)

gdzie 1234 jest liczbą, która określa sumę długości wszystkich filmów Disneya wyprodukowanych w 1990 roku.

0

Jeśli w klauzuli FROM występuje więcej niż jedna zmienna, to podana interpretacja wymaga kilku modyfikacji, ale podstawowe zasady są takie sa­me jak przy opisie przypadku z jedną zmienną. Załóżmy więc, że w klauzuli FROM występują następujące zmienne: xI, xz, ..., xk. Wówczas:

1. Wszystkie zmienne xI, x2, ..., xk mogą występować w wyrażeniach e,, e2, ..., e„ klauzuli GROUP BY.

2. Pola w strukturach należących do wielozbioru, który jest wartością pola partition, nazywają się: xI, x2, ..., xk.

3. Załóżmy, że ż,, i2, ..., ik są kolejnymi wartościami zmiennych x,, x2, ..., xk, które spełniają klauzulę wHERE. Wówczas w zbiorze wynikowym klauzuli GROUP BY istnieje struktura o następującej postaci:

StrUCt~ f : 2I~lI, Z2, ..., lk), ..., fn: 2n~ll, l2, ..., lk), part1t10n: P~

gdzie P jest strukturąpostaci:

Struct(xl: i,, x2: iZ, ..., xk: ik)

8.3. DODATKOWE POSTACIE WYRAŻEŃ W J~ZYKU OQL 491

8.3.4. Klauzula HAVING

Po klauzuli GROUP BY w języku OQL może występować jeszcze klau­zula HAVING, której znaczenie jest takie jak klauzuli HAVING w języku SQL. Zatem klauzula postaci:

HAVING <warunek>

służy do usunięcia pewnych grup utworzonych w klauzuli GROUP BY. Waru­nek dotyczy wartości pola partition w poszczególnych strukturach wyniku GROUP BY. Jeśli warunek jest spelniony, to struktura zostaje dolączona do wyniku dla przetworzenia zgodnego z opisem zamieszczonym w p. 8.3.3. Jeśli jednak warunek nie jest spełniony, to tej struktury nie przetwarza się dalej.

SELECT std, r, sumaDługości: SUM(SELECT p.m.długość EROM partition p) FROM Filmy m

GROUP BY std: m. studio, r: mrok

HAVING MAX (SELECT p.m.długość EROM partition p) > 120

RYSUNEK 8.8

Ograniczenie rozważanych grup

PRZYKŁAD 8.14

Powtórzmy teraz przykład 8.13, ale tym razem zapytajmy o sumę dlugości filmów wyprodukowanych tylko w tych studiach, które w każdym roku wy­produkowały co najmniej jeden film trwający dłużej niż 120 minut. Zapytanie zamieszczone na rys. 8.8 rozwiązuje to zadanie. Zauważmy, że w klauzuli HAVING użyliśmy tego samego zapytania, które występuje w klauzuli SE­LECT, a służy do uzyskania wielozbioru długości filmów w danym studio, w danym roku. W klauzuli HAVING maksymalną z tych wartości porównuje­my z wartością 120.

0

8.3.5. Operatory algebry zbiorów

Dla dwóch obiektów typu zbiór lub wielozbiór można użyć operatorów sumowania, przecięcia lub różnicy. Te operatory w języku OQL są reprezen­towane tak samo jak w języku SQL, odpowiednio słowami kluczowymi UNIOM, INTERSECTION 1 EXCEPT.

PRZYKŁAD 8.15

Zbiór wszystkich filmów z udzialem Harrisona Forda, które nie byty produ­kowane w studio Disneya, można otrzymać jako różnicę dwóch klauzul se­

492 8. ZORIENTOWANE OBIEKTOWO JĘZYKI ZAPYTAŃ

lect-from-where; przedstawiono ją na rys. 8.9. W wierszach od 3) do 9) szuka się zbioru wszystkich filmów, w których występuje Harrison Ford, a w wier­szach od 5) do 7) szuka się wszystkich filmów, które nie były produkowane w studio Disneya. Operator EXCEPT w wierszu 4) służy do obliczenia różnicy tych dwóch zbiorów.

0

1) (SELECT DISTINCT m

2) EROM Filmy m, m. gwiazda s

3) WHERE s.nazwisko = „Harrison Ford") 4) EXCEPT

5) (SELECT DISTINCT m 6) EROM Filmy m

7) WHERE m.należyDo.nazwa = „Disney")

RYSUNEK 8.9

Zapytanie, w którym korzysta się z różnicy dwóch zbiorów

Należy zwrócić uwagę na słowo kluczowe DISTINCT w wierszach 1) i 5) na rys. 8.9. Jego użycie powoduje, że wyniki obu zapytań są zbiorami; gdyby słowo DISTINCT nie występowało, to wynik byłby wielozbiorem. W języku OQL operatory UNION, INTERSECTION i EXCEPT działają zarówno na wielozbiorach, jak i na zbiorach. Gdy oba argumenty są zbiorami, to w wyniku też powstaje zbiór.

Jednakże, jeśli oba argumenty są wielozbiorami, albo tylko jeden z nich jest wielozbiorem, a drugi jest zbiorem, to operator jest używany w znaczeniu wielozbiorów. Przypomnijmy tu, że w wielozbiorze ta sama wartość może występować dowolnie wiele razy, była o tym mowa w p. 4.6.2. Zasady dzia­łania na wielozbiorach są następujące. Załóżmy, że BI oraz BZ są dwoma wielozbiorami, a x jest elementem, który występuje nI razy w B, oraz n2 razy w B2. Oba, zarówno n~, jak i nz mogą być zerem.

W BI u BZ x występuje nI+ hz razy.

W B1 n Bz x występuje min(n~, nz) razy. W B~ - BZ x występuje

I . Jeśli n, <_ nZ, to 0 razy.

2. Jeśli n, > nz, to n~ - nZ razy.

W naszym konkretnym zapytaniu z rys. 8.9 film może wystąpić w wyni­ku podzapytania tylko jeden raz albo wcale, a więc wynik nie zależy od uży­cia słowa DISTINCT. Jednakże typ wyniku jest inny. Gdy korzysta się z DI­STINCT, to typem wyniku jest set<Film>, jeśli opuścimy DISTINCT w jed­nym albo w obu miejscach, to wynik jest typem Bag<Film>.

8.4. TWORZENIE I PRZYPISYWANIE OBIEKTÓW W JIrZYKU OQL 493

8.3.6. Ćwiczenia do podrozdziału 8.3

Ćwiczenie 8.3.1. Korzystając ze schematu ODL utworzonego w ćwiczeniu 8.1.1 oraz rys. 8.2, należy zapisać w języku OQL następujące zapytania:

*a) Wyszukać wszystkich producentów, którzy wytwarzają zarówno PC, jak i drukarki.

*b) Wyszukać tych producentów komputerów PC, którzy wytwarzają PC z co najwyżej 2 gigabajtami dysku twardego.

c) Wyszukać tych producentów PC, którzy nie wytwarzają laptopów. *d) Znaleźć średnią szybkość PC.

*e) Dla każdej szybkości CD podać średnią wielkość pamięci RAM w PC. Wyszukać tych producentów, którzy wytwarzają jakieś produkty z co naj­mniej 16 MB RAM, a także produkty, które kosztują co najmniej 1000 $.

!!g) Dla każdego producenta, który wytwarza PC ze średnią szybkością co najmniej 150, należy odszukać największą oferowaną przez nich w PC wielkość RAM.

Ćwiczenie 8.3.2. Korzystając ze schematu ODL utworzonego w ćwiczeniu 8.1.2 oraz rys. 8.3, należy zapisać w języku OQL następujące zapytania:

a) Wyszukać te klasy okrętów, w których wszystkie okręty byty wodowane przed rokiem 1919.

b) Określić największą wyporność w każdej klasie.

!c)Dla każdego kalibru działa określić rok wodowania pierwszego wodowane­go okrętu z takim działem.

*!!d)Dla każdej klasy okrętów, w której chociaż jeden okręt był wodowany przed rokiem 1919, określić, ile okrętów w tej klasie zatonęło podczas bitew.

!e) Określić średnią liczbę okrętów w klasie. !~ Znaleźć średniąwyporność okrętu

!!g) Wyszukać te bitwy (obiekty, a nie nazwy), w których uczestniczył chociaż­by jeden okręt brytyjski, a w której zatonęly co najmniej dwa okręty.

Ćwiczenie 8.3.3. W przykładzie 8.11 wspominaliśmy, że w wyniku zapytania OQL z rys. 8.6 mogą zostać umieszczone takie gwiazdy, które wcale nie występowały w żadnym filmie, a zatem „technicznie" występowały „tylko w filmach Disneya". Należy tak zapisać zapytanie, aby w wyniku były umieszczane tylko te gwiazdy, które występowały już w filmach, ale były to tylko filmy Disneya.

8.4. Tworzenie i przypisywanie obiektów w j ęzyku OQL

W bieżącym rozdziale dowiemy się, w jaki sposób język OQL łączy się z językiem podstawowym, przy czym w naszym przypadku funkcje języka podstawowego będzie pełnił język C++, pomimo że w pewnyci~ systemach te same funkcje pełnią inne języki programowania.

494 8 ZORIENTOWANE OBIEKTOWO J~ZYKI ZAPYTAŃ

8.4.1. Przypisywanie wartości zmiennym języka podstawowego

Inaczej niż to się dzieje w przypadku języka SQL, gdzie trzeba przeka­zywać wartości między krotkami bazy danych a zmiennymi języka podsta­wowego, język OQL łączy się z językiem podstawowym w sposób naturalny. Dzieje się tak dlatego, że wartościami wyrażeń języka OQL takich jak select­from-where, które omawialiśmy dotychczas, są obiekty. A zatem zmiennym języka podstawowego, których typ został określony w odpowiedni sposób, można przypisać wynik przetwarzania wyrażenia OQL.

PRZYKŁAD 8.16

Wynikiem wyrażenia OQL

SELECT m EROM Filmy m. WHERE mrok < 1920

jest zbiór wszystkich filmów wyprodukowanych przed rokiem 1920. Jego typ określa się jako Set<Film>. Jeśli staryFilm oznacza zmienną języka pod­stawowego tego samego typu, to możemy w programie w C++ (rozszerzonym o OQL) napisać następującą instrukcję:

staryFilm = SELECT DISTINCT m EROM Filmy m WHERE mrok < 1920

Po wykonaniu zapytania zmiennej staryFilm zostanie przypisany zbiór obiektówtypu Film.

0

8.4.2. Wydobywanie elementu z kolekcji

Ponieważ zarówno wyrażenia select-from-where, jak i wyrażenia group by tworzą kolekcje - zbiory lub wielozbiory - zatem jeśli chcemy mieć do czynienia z konkretnym, pojedynczym elementem kolekcji, trzeba wykonać dodatkowe działania. Nawet jeśli kolekcja zawiera dokładnie jeden element, to i tak, aby się nim posłużyć, trzeba go z tej kolekcji wydobyć. W języku OQL ten cel można osiągnąć stosując operator ELEMEI~IT, który ze zbioru lub wielozbioru wydobywa jeden element. Operator ten można stosować na przy­kład do wyniku zapytania, jeśli na pewno wiadomo, że w tym wyniku wystę­puje dokładnie jeden element.

8.4. TWORZENIE I PRZYPISYWANIE OBIEKTÓW W JĘZYKU OQL, 49S

PRZYKŁAD 8.17

Zalóżmy, że wartościązmiennej pzw, której typem jest Film (gdzie Film jest klasą), jest obiekt reprezentujący film Przeminęło z wiatrem. Z kolei wyni­kiem następującego zapytania:

SELECT m EROM Filmy m

WHERE m.tytuł = „Przeminęło z wiatrem"

jest wielozbiór zawierający dokładnie jeden obiekt. Nie można tego wielo­zbioru bezpośrednio przypisać zmiennej pzw, ponieważ prowadzi to do wy­stąpienia błędu niezgoc!ności typów. Jednak jeśli do zapytania zastosujemy operator ELEMENT

pzw = ELEMENT SELECT m EROM Filmy m

WHERE m.tytuł = „Przeminęło z wiatrem"

to typy zmiennej i wyrażenia będą zgodne i przypisanie zostanie wykonane bez blędu.

O

8.4.3. Wydobywanie poszczególnych elementów kolekcj i

Dostęp do poszczególnych elementów zbioru lub wielozbioru jest trud­niejszy niż poprzednio, ale i tak prostszy niż korzystanie z algorytmów ze wskaźnikami, które zastosowano w języku SQL. Najpierw zbiór lub wielo­zbiór przeksztalca się w listę. To dzialanie jest możliwe przez dopisanie do zapytania klauzuli ORDER BY. W punkcie 8.2.8 pisaliśmy już, że klauzula ORDER BY przeksztalca kolekcje w listę obiektów lub wartości.

PRZYKŁAD 8.18

Utwórzmy listę obiektów klasy Film. Do określenia porządku użyjemy tytulu i (aby uniknąć nieokreśloności) roku produkcji filmu, jest to poprawny porządek, ponieważ atrybuty tytuł i rok tworzą klucz w klasie Film. Poniższa instrukcja:

listaFilmów = SELECT m EROM Filmy m

ORDER BY m.tytuł, mrok;

przypisuje zmiennej wyższego poziomu m listę wszystkich obiektów Film, uporządkowanych według tytułu i roku produkcji.

O

496 8. ZORIENTOWANE OBIEKTOWO JĘZYKI ZAPYTAŃ

Gdy mamy już listę, uporządkowaną lub nie, wówczas do każdego ele­mentu listy możemy się dostać przez określenie numeru jego pozycji na liście, np. i-ty element listy L uzyskujemy, specyfikując L[i - 1]. Przypomnijmy tutaj, że zgodnie z konwencją języków C i C++ numeracja elementów list i tablic rozpoczyna się od zera (0).

PRZYKŁAD 8.19

Naszym celem będzie teraz utworzenie funkcji, która dla każdego filmu drukuje jego tytuł, rok oraz długość. Szkic tej funkcji został przedstawiony na rys. 8.10.

1) listaFilmów = SELECT m

EROM Filmy m

ORDER BY m.tytuł, mrok; 2) liczbaFilmów = COUNT(Filmy);

3) for(i = 0; i < liczbaFilmów, i++) { 4) film=listaFilmów[i];

5) cout « film.tytuł « „ " « film. rok << „ " 6) « film. długość « „\n";

~) )

RYSUNEK 8.10

Sprawdzanie i drukowanie poszczególnych filmów

W wierszu 1) następuje uporządkowanie obiektów klasy Film i zapisa­nie listy wynikowej w zmiennej listaFilmów, której typem jest List<Film>. W wierszu 2) wylicza się liczbę filmów, stosując w tym celu operator CoUNT z języka OQL. Następnie w wierszach od 3) do 6) zapisano pętlę for, w której zmienna kontrolowana i przebiega wszystkie pozycje utworzonej listy. Dla wygody i-ty składnik listy jest przypisywany zmiennej film. W końcu, w wierszach 5) i 6) następuje wydrukowanie odpowiednich atrybutów filmu.

0

8.4.4. Tworzenie nowych obiektów

Wiemy już, że dzięki wyrażeniom OQL typu select-from-where można tworzyć nowe obiekty. Tworzy się je, przetwarzając obiekty istniejące. Można także tworzyć nowe obiekty przez zespolenie stałych lub innych wyrażeń w struktury lub kolekcje. Wiadomo już, że dokonują tego konstruktory typów zastosowane do wartości. W takich przypadkach wartości, z których tworzy się obiekty, otacza się nawiasami okrągłymi, w odróżnieniu od nawiasów trójkątnych, stosowanych przy konstruowaniu typów. W przykładzie 8.7 wy­stępowała instrukcja

SELECT DISTINCT Struct(gwiazdal: sl, gwiazda2: s2)

8.4. TWORZENIE I PRZYPISYWANIE OBIEKTÓW W JĘZYKU OQL 497

która służyła do określenia, że w wyniku zapytania powstaje zbiór obiektów typu Struct{gwiazdal: Gwiazda, gwiazda2: Gwiazda}. Polom tej struktury nadano nazwy gwiazdal i gwiazda2, ponieważ w ten sposób jest łatwo uchwycić odpowiedniość pomiędzy typami pól i zmiennych s 1 i s 2.

PRZYKŁAD 8.20

Nowe kolekcje można także tworzyć w ten sposób, że do obiektów tego sa­mego typu stosuje się któryś z konstruktorów typów: set, Bag, List lub Array. Rozważmy na przykład następujący ciąg przypisań:

x = Struct(a:l, b:2);

y = Bag(x, x, Struct(a:3, b:4));

W pierwszym wierszu określa się wartość zmiennej x, której typem jest

Struct(a: integer, b: integer)

czyli struktura o dwóch polach całkowitych a i b. Wartości takiego typu moż­na traktować jako krotki o składowych całkowitych, odpowiadających polom a i b. Wówczas wartość x można przedstawić jako (l, 2). W drugim wierszu występuje definicja zmiennej y, która została tam określona jako wielozbiór, którego z kolei elementami są struktury zdefiniowane tak samo jak zmienna x. W tym wielozbiorze dwa razy występuje para (1, 2) oraz jeden raz para (3, 4).

0

Jeśli zostanie określony pewien typ, a zapytanie tworzy kolekcję obiek­tów tego typu, to wówczas nazwy typu można używać zamiast jawnego wyra­żenia. W przykładzie 8.7 widać było, w jaki sposób utworzyć jawnie zbiór par. Po słowie kluczowym SELECT wystąpiło tam wyrażenie, w którym zasto­sowano konstruktor Struct do utworzenia obiektów o dwóch polach - ich wartościami były obiekty reprezentujące gwiazdy filmowe.

Zdefiniujmy teraz typ ParyGwiazd jako:

Struct{gwiazdal: Gwiazda, gwiazda2: Gwiazda}

Wówczas można użyć tego typu w klauzuli SELECT w instrukcji z przykła­du 8.7 w następujący sposób:

SELECT DISTINCT ParyGwiazd(gwiazdal: sl, gwiazda2: s2) EROM Gwiazdy sl, Gwiazdy s2

WHERE sl.adr = s2.adr AND sl.nazwisko < s2.nazwisko

Jedyna zmiana polega na tym, że inaczej niż w przykładzie 8.7 typem wyniku określa się teraz jako Set<ParyGwiazd>. Taki wynik można przypisać zmiennej z języka podstawowego, której typ jest określony w ten sam sposób.

498 8. ZORIENTOWANE OBIEKTOWO JĘZYKI ZAPYTAŃ

Zastosowanie nazwy typu w przypadku argumentów jest korzystne zwłaszcza wtedy, kiedy nazwa typu jest klasą. Istnieje kilka różnych postaci funkcji konstruujc~cych dla klas, a postacie te zależą od typu właściwości, które mają być zainicjowane lub którym nadaje się wartości domniemane w sposób jawny. Na przykład z pewnością nie inicjuje się metod, większości atrybutów można przypisać wartości początkowe, a związki można określić jako puste. Nazwa każdej funkcji konstruującej jest taka sama jak nazwa klasy, a rozróżnia się je dzięki nazwom pól, które występują jako argumen­ty. Sposób definiowania funkcji konstruujących zależy od języka podsta­wowego.

PRZYKŁAD 8.21

Rozważmy potencjalną funkcję konstruującą dla obiektów klasy Film. Pobie­ra ona wartości atrybutów tytuł, rok, długość i należyDo, a następnie tworzy obiekt z określonymi wartościami czterech pól oraz pustym zbiorem gwiazd. Zatem, jeśli mgm oznacza zmienną, której wartością jest obiekt klasy studio reprezentujący wytwórnię MGM, to obiekt reprezentujący flm Przeminęło z wiatrem można utworzyć w następujący sposób:

pzw = Film (tytuł: „Przeminęło z wiatrem", rok: 1939,

długość: 239, należyDo: mgm);

Przetworzenie tej instrukcji daje dwojaki wynik:

1. Tworzy się nowy obiekt klasy Film, który staje się częścią zasięgu Fi lmy.

2. Obiekt ten jest wartościązmiennej pzw.

0

8.4.5. Ćwiczenia do podrozdziału 8.4

Ćwiczenie 8.4.1. Zmiennej x z języka podstawowego nałeży przypisać następujące stałe:

*a) Zbiór {l, 2, 3}.

b) Wielozbiór {l, 2, 3, 1}. c) Listę {I, 2, 3, 1}.

d) Strukturę, której pierwsza składowa a jest zbiorem {I, 2}, a druga składo­wa, nazwana b, jest wielozbiorem { 1, 1 } .

e) Wielozbiór struktur, z których każda ma dwa pola o nazwach a i b. War­tości w trzech poszczególnych parach stanowiących struktury są następują ce: (I, 2), (2, I) i (I, 2).

8.5. OBIEKTY KROTKOWE W J~ZYKU SQL3 499

Ćwiczenie 8.4.2. Korzystając ze schematu z ćwiczenia 8.1.1 oraz rys. 8.2, należy w C++ (lub innym języku obiektowym) napisać instrukcje rozszerzone o OQL, które mają działać w następujący sposób:

*a) Zmiennej x z języka podstawowego należy przypisać obiekt PC, który ma numer 1000.

b) Zmiennej y z języka podstawowego należy przypisać zbiór obiektów typu laptop, które mają co najmniej 16 megabajtów RAM.

c) Zmiennej z języka podstawowego należy przypisać wartość średnią szybko­ści PC sprzedawanych poniżej 1500 $.

!d) Wyszukać wszystkie drukarki laserowe, wydrukować listę ich numerów mo­deli wraz z cenami, a na końcu wskazać numer modelu o najniższej cenie.

!!e) Należy wydrukować zestawienie obejmujące wszystkich producentów i najniższe oraz najwyższe ceny ich produktów.

Ćwiczenie 8.4.3. W bieżącym ćwiczeniu posłużymy się schematem bazy danych wprowadzonym w ćwiczeniu 8.1.2 i na rys. 8.3. Zakładamy, że dla wszystkich czte­rech klas schematu zostały zdefiniowane funkcje konstruujące z nazwami takimi samymi, jak nazwy klas, które określają wartości wszystkich atrybutów oraz związ­ków jednowartościowych, a związki wielowartościowe są określane jako puste. Przy związkach jednowartościowych do innych klas można korzystać ze zmiennych języka podstawowego, których wartością jest obiekt bieżący. Należy utworzyć obiekty, zde­finiowane poniżej oraz przypisać je odpowiednim zmiennym języka podstawowego.

*a) Okręt Colorado z klasy Maryland, wodowany w 1923 r. b) Okręt Graf Spee z klasy Lutzow, wodowany w 1936 r.

c) Wynik bitwy malajskiej, w której został zatopiony okręt Prince of Wales. d) Bitwa malajska odbyła się 10 grudnia 1941 r.

e) Klasa Hood okrętów brytyjskich ma 8 dział 15-calowych oraz wyporność 41 000 ton.

8.5. Obiekty krotkowe w języku SQL3

W języku OQL nie wyróżnia się pojęcia relacji, jest to po prostu zbiór (lub wielozbiór) struktur. Jednakże w języku SQL pojęcie relacji jest na tyle istotne, że obiekty w SQL3 podtrzymują tę centralną rolę pojęcia relacji. I dlatego obiekty w SQL3 są dwojakie:

1. Obiekty wierszowe, które są po prostu krotkami.

2. Abstrakcyjne typy danych (abstract data types w skrócie ADT lub w niektórych dokumentach SQL3 określane jako wartości ADT), któ­re sąw istocie obiektami używanymi jako składowe krotek.

W bieżącym podrozdziale omówimy obiekty wierszowe, a typy ADT zostaną przedstawione w podrozdziale 8.6.

500

8.5.1. Typ wiersza

8. ZORIENTOWANE OBIEKTOWO JĘZYKI ZAPYTAŃ

W języku SQL3 można zdefiniować typ knotek, który w dużym uprosz­czeniu odpowiada pojęciu klasy obiektów. Deklaracja typu wierszy składa się z:

1. Słowa kluczowego CREATE ROW TYPE. 2. Nazwy typu.

3. Listy par atrybutów i ich typów, którą otacza się nawiasami.

A zatem można przedstawić postać tej deklaracji w następujący sposób:

CREATE ROW TYPE T(<deklaracja składowych>)

PRZYKŁAD 8.22

Utworzymy typ wiersza, który posłuży do reprezentowania gwiazd filmo­wych, o właściwościach podobnych do klasy Gwiazda opisanej w przykła­dzie użycia OQL na rys. 8.4. Jednakże w tym przypadku nie ma możliwości bezpośredniego reprezentowania zbioru filmów jako pola w krotce Gwiazda. Dlatego na początku knotka Gwiazda będzie zawierać tylko składowe opisu­jące nazwisko i adres.

Zauważmy, że typ określający adres, przedstawiony na rys. 8.4, sam jest typem krotkowym, o składowych ulica oraz miasto. Czyli musimy określić dwa dodatkowe typy - jeden do reprezentowania adresu, a drugi do repre­zentowania gwiazdy. W języku SQL3 dopuszcza się, aby w typie wiersza określać typ składowej, jako inny typ wiersza, albo relację. Na rysunku 8.11 przedstawiono omawiane definicje.

CREATE ROW TYPE TypAdres ulica CHAR (50), miasto CHAR(20)

);

CREATE ROW TYPE TypGwiazda nazwisko CHAR(30), adres TypAdres

);

RYSUNEK 8.11

Dwie definicje typu wiersza

Knotka typu TypAdres ma dwie składowe, atrybutami są tutaj ulica i adres. Typami tych atrybutów są ciągi znaków o długościach równych odpowiednio 50 i 20. Knotka typu TypGwiazda ma także dwie składowe. Pierwsza określa wartość atrybutu nazwisko, którego typ zdefiniowaono jako 30-znakowy tekst, a typem drugiego atrybutu jest TypAdres, tzn. knotka o składowych ulica oraz miasto.

D

8.5. OBIEKTY KROTKOWE W JĘZYKU SQL3 SO 1

8.5.2. Deklarowanie relacji z typem wiersza

Po zadeklarowaniu typu wiersza można określić jedną lub więcej relacji, których knotki mają taki typ. Postać deklaracji relacji jest podobna do postaci przedstawionej w p. 5.7.2, ale zamiast określanej w deklaracji tabel SQL listy atrybutów i ich typów, tutaj umieszcza się następującą frazę:

O F T Y PE <nazwa typu mersza>

PRZYKŁAD 8.23

Można zadeklarować relację GwiazdaFilmowa w taki sposób, żeby jej knot­kom przypisać typ TypGwiazda. Wygląda to następująco:

CREATE TABLE GwiazdaFilmowa OF TYPE TypGwiazda;

W efekcie tabela GwiazdaFilmowa ma dwa atrybuty: nazwisko i adres. Zauważmy, że typ tego drugiego atrybutu jest typem wiersza, co w wersjach języka SQL wcześniejszych niż SQL3 nie było dopuszczalne.

0

Całkiem zrozumiałe wydaje się to, że dla każdego typu wiersza istnieje rela­cja, którą rozumie się tak samo jak jest rozumiany zasięg (w rozumieniu p. 8.1.3) klasy odpowiadającej temu typowi knotek, ale można także określić wiele relacji o jednym typie krotkowym albo nie zdefiniować żadnej relacji tego typu.

8.5.3. Dostęp do składowych typu wiersza

Ponieważ składowe w języku SQL3 mogą być strukturami, trzeba dys­ponować mechanizmami dostępu do składowych tych struktur. W SQL3 w tym celu korzysta się z notacji podwójnej kropki, która jest bliskim odpo­wiednikiem notacji kropkowej z języków OQL i C.

SELECT GwiazdaFilmowa.nazwisko,

gwiazdaFilmowa.adres.ulica

EROM GwiazdaFilmowa

WHERE GwiazdaFilmowa.adres..miasto = `Beverly Hills';

RYSUNEK 8.12 Dostęp do składowych

PRZYKŁAD 8.24

Zapytanie z rys. 8.12 służy do odszukania nazwiska oraz adresu tych gwiazd, które mieszkają w Beverly Hills. Użyjemy pełnego opisu kropkowego atry­butów GwiazdaFilmowa.nazwisko i GwiazdaFilmowa.adres po to,

SOZ 8. ZORIENTOWANE OBIEKTOWO J)rZYKl ZAPYTAŃ

by zilustrować różnicę między pojedynczą a podwójną kropką. Ale ponieważ nazwy adres i nazwisko w tym miejscu są jednoznaczne, więc tak napraw­dę GwiazdaF'ilmowa i kropka nie sąpotrzebne.

0

8.5.4. Referencje

Identyczność obiektów z języków obiektowych w języku SQL3 jest za­pewniona dzięki pojęciu referencji (odniesienia). Typem skladowej w typie wiersza może być referencja do innego typu wiersza. Jeśli T jest typem wiersza, to REF(7~ oznacza typ, który jest referencją do krotki typu T. Gdy traktujemy krotkę jako obiekt, wówczas referencją do tego obiektu jest jego ID.

PRZYKŁAD 8.25

Nada) nie ma sposobu na zapisanie w tabeli GwiazdaFilmowa zbioru wszystkich filmów, w których dana gwiazda brała udział, ale spróbujmy tam umieścić jej najlepszy flm. Najpierw jednak trzeba zdefiniować relację Film, a jednocześnie można zdefiniować typ wiersza tej relacji. Najprostszy typ dla filmów, bez uwzględniania teraz związków z gwiazdami, studiami i produ­centami, definiuje się w następujący sposób:

CREATE ROW TYPE TypFilm( tytuł CHAR(30),

rok INTEGER, czyKolor BIT(1)

Relację krotek tego Typu definiuje się następująco:

CREATE TABLE Film OF TYPE TypFilm;

Potem trzeba zmodyfikować typ krotek GwiazdaFilmowa tak, aby obejmo­wał referencję do najlepszego filmu danej gwiazdy'. Nowa definicja typu TypGwiazda wygląda następująco:

CREATE ROW TYPE TypGwiazda( nazwisko CHAR(30), adres TypAdres, najlepszyFilm REF(TypFilm) );

D

` W języku SQL3 nie występuje operator ALTER TABLE ani żadna podobna instruk­cja, która umożliwiłaby zmianę istniejącej definicji typu. Dlatego w praktyce gdybyśmy chcieli zmieniać definicję typu wiersza, trzeba by usuwać definicje zmienianego typu oraz tabele z nim związane, a potem powtórnie zdefiniować ten typ oraz zrekonstruować tabele.

8.5. OBIEKTY KROTKOWE W JĘZYKU SQL3 SO3

PRZYKŁAD 8.26

Teraz spróbujmy określić standardowy związek typu wiele do wiele między gwiazdami a filmami: jedna gwiazda występuje w wielu filmach, a w jednym filmie może brać udział wiele gwiazd. W języku ODL można określić jako składową filmu cały zbiór gwiazd i odwrotnie, jednak w języku SQL3 nadal obowiązuje mechanizm związków, który przewija się przez cały nasz pod­ręcznik . A więc związki wiele do wiele można reprezentować jako oddzielne relacje, zlożone z par wielkości tworzących ten związek.

CREATE ROW tytuł rok czyKol~ );

CREATE ROW ulica miasto );

TYPE TypFilm( CHAR(30) INTEGER,

~r BIT ( 1 )

TYPE TypAdres( CHAR (50), CHAR(20)

CREATE ROW TYPE TypGwiazda( nazwisko CHAR(30), adres TypAdres

); CREATE ROW TYPE TypGwiazdyW( gwiazda REF(TypGwiazda), film REF(TypFilm)

);

CREATE TABLE Film OF TYPE Typ Film;

CREATE TABLE GwiazdaFilmowa OF TYPE TypGwiazda CREATE TABLE GwiazdyW OF TYPE TypGwiazdyW;

RYSUNEK 8.13

Gwiazdy, filmy i związki między nimi

Na rysunku 8.13 przedstawiono jeden ze sposobów określenia związku gwiazdy-w między filmami a gwiazdami. W standardach wcześniejszych niż SQL3 związki wiele do wiele reprezentuje się przez pary kluczy klas tworzą­cych związek, natomiast w języku SQL3 można bezpośrednio odwoływać się do obiektów (a dokładnie krotek) przez atrybuty, których typ jest określony

W niektórych projektach SQL3 dopuszcza się kolekcje (tzn. zbiory lub relacje) wystę­pujące w charakterze typu atrybutu, jednakże jest bardziej prawdopodobne, że stosowanie typów kolekcji zostanie odłożone do następnego standardu SQL4.

SO4 8. ZORIENTOWANE OBIEKTOWO JĘZYKI ZAPYTAŃ

jako referencja. Rozpoczynamy od zdefiniowania typów TypF'ilm oraz TypGwiazda odpowiednio dla relacji Film i GwiazdaFilmowa. Powrócili­śmy tu do początkowej wersji typu wiersza TypGwiazda, w którym nie okre­śla się najlepszego filmu. Typ wiersza o nazwie TypGwiazdyw jest definio­wany dla relacji Gwiazdyw i zawiera pary referencji, każda para obejmuje odniesienie do gwiazdy oraz do filmu, w którym ta gwiazda występowała.

Po definicjach typów występują deklaracje trzech tabel: Film, Gwiaz­daFilmowa oraz Gwiazdyw, w których korzysta się z uprzednio zdefiniowa­nych typów wierszy. Zauważmy, że w definicjach tabel nie korzysta się z typu wiersza TypAdres. Jest on używany w określaniu atrybutu adres w definicji typu wiersza TypGwiazda.

Porównamy teraz zdefiniowaną ostatnio relację Gwiazdyw z relacją o tej samej nazwie, którą deklarowano w schemacie bazy danych w podrozdzia­le 3.9. W tej ostah~iej relacji zamiast referencji do krotek filmu występowaly atrybuty tytulFilmu i rokFilmu, a zamiast referencji do krotki gwiazdy Występowa atrybut nazwiskoGwiazdy.

0

8.5.5. Następstwo referencji

Po wprowadzeniu pojęcia referencji i dopuszczeniu, by określała ona typ składowej krotki, rozszerzenie SQL o operator de~eferencji jest zupelnie natu­ralne. W SQL3 operator ten jest oznaczany ~ i ma on takie samo znaczenie jak w języku C. A zatem, jeśli x jest referencją do krotki t, a jest atrybutem t, to x --ł a oznacza wartość atrybutu a w krotce t. W wielu zapytaniach SQL3 ten operator jest bardzo użyteczny, ponieważ można nim zastępować pewne złączenia, które okazały się tak potrzebne w języku SQL2.

Dziedziny i typ wiersza

W punkcie 5.7.6 poznaliśmy dziedziny, które są rodzajem definicji typów. Między dziedzinami a typami wierszy są dwie istotne różnice. Po pierwsze dziedzina określa typ skkadowej, a typ wierszowy jest typem krotki.

Ale istnieje subtelniejsza różnica. Jeśli dwie różne dziedziny definiują ten sam typ, to wartości z tych dziedzin są nierozróżnialne. Rozważmy dwa różne typy wierszy TI i TZ, które zostały jednakowo zdefiniowane. Nie można pomylić krotek pochodzących z relacji określonych tymi typa­mi. Na przykład atrybut, którego typ jest referencją do T,, nie może mieć wartości, która jest referencją do krotki typu TZ.

PRZYKŁAD 8.27

Skorzystajmy ze schematu z rys. 8.13 po to, by wyszukać wszystkie tytuły filmów, w których występował Mel Gibson. Sprawdzamy po kolei pary rela­

8.5. OBIEKTYKROTKOWE W JĘZYKI) SQL3 SOS

cji GwiazdyW. Jeśli referencja gwiazda wskazuje Mela Gibsona, to tytuł wskazywanego filmu dołącza się do wyniku. Stosowne zapytanie w SQL3 ma następującą postać:

SELECT film-.tytuł EROM Gwiazdyt~7

WHERE gwiazda--.nazwisko = `Mel Gibson';

Zapytanie ma następującą interpretację. Tak samo jak w zapytaniach se­lect-from-where w języku SQL po kolei rozważamy krotki z relacji wymie­nionej w klauzuli FROM, nazwijmy je (s, m). s oznacza tutaj referencję do krotki gwiazdy, a m do krotki %lmu. W klauzuli WHERE sprawdza się, czy skladowa nazwisko krotki relacji GwiazdaFilmowa, wskazywanej przez s, jest równa `Mel Gibson'. Jeśli tak jest, to wartość składowej tytuł krotki Filmu, wskazywanej przez m, zostaje dołączona do wyniku zapytania.

0

Dereferencja i wydobywanie składowych

Jedna z różnic między SQL3 a OQL polega na sposobie traktowania ope­ratorów -> i kropki. Jak pamiętamy z p. 8.2.3 operatory kropka i --ł są w OQL synonimami. Każdy z nich zastosowany do obiektu OQL, który jest krotką, zwraca w wyniku wartość wskazywanej składowej. Lecz w ję­zyku SQL3, podobnie jak w C, działania tych dwóch operatorów są różne. Operator --> można tu stosować tylko w przypadku referencji do krotki, a w zmiennych krotkowych można stosować tylko operator kropkowy (w SQL3 zapisywany jako dwie kropki). Tak samo jak w języku C, jeśli r jest referencją do krotki t, to r -~ a oznacza tę samą wartość co t . . a.

8.5.6. Zakres referencji

Aby uzyskać wynik zapytania takiego jak w przykładzie 8.27, system baz danych SQL3 musi umieć interpretować wyrażenia określające referencję, czyli musi na przykład umieć odczytać, że gwiazda~nazwisko oznacza wartość w polu nazwisko w określonej relacji. Jeden z prostszych sposobów polega na przeglądaniu wszystkich krotek relacji Gwiazdyw, następnie wszystkich wskazywanych tam krotek gwiazd i sprawdzanie, czy wskazana krotka ma w nazwisku `Mel Gibson'. Jeśli jednak relacja Gwiazdyw jest du­ża, to taki sposób okazuje się bardzo czasochłonny.

Można by zastosować lepszy sposób, jeśli w systemie DBMS można utworzyć indeks dla atrybutu nazwisko w pewnej relacji R, który od warto­ści `Mel Gibson' poprowadzi nas do tych krotek relacji Gwiazdyw, które wskazują na krotkę w R z wartością nazwisko równą `Mel Gibson'. Ale

SOC) 8. ZORIEIv .OWANE OBIEKTOWO JĘZYKI ZAPYTAŃ

która relacja jest relacją R? W rozpatrywanym przykladzie atrybut gwiazda w krotkach Gwiazdyw ma określoną wartość, która wskazuje pewną krotkę, a ta z kolei występuje w relacji typu TypGwiazda. Istnieje tylko jedna taka relacja, GwiazdaFilmowa, a więc można oczekiwać, że jest to nasza poszu­kiwana relacja.

Ale mogą być zadeklarowane także inne relacje, których typ zostanie określony jako TypGwiazda, a jeśli tak się stanie, to wówczas trzeba prze­szukiwać indeksy każdej z nich po to, by znaleźć nazwisko `Mel Gibson'. Takie przeszukiwanie może być również czasochłonne, jeśli z powodów zna­nych projektantowi schematu wszystkie referencje doprowadzałyby w końcu do jednej relacji, co zdarza się najczęściej. Dlatego w języku SQL3 udostęp­nia się mechanizm, który umożliwia wyspecyfikowanie relacji, do której od­woluje się atrybut. Do relacji, której atrybut jest referencją, można dolączyć następującą klauzulę:

SCOPE FOR <atrybut> Is <relacja>

Ta instrukcja oznacza, że nazwany atrybut, który jest referencją, wskazuje zawsze knotki relacji wymienionej w tej instrukcji.

PRZYKŁAD 8.28

Aby udzielić gwarancji, że gwiazda w relacji GwiazdyW zawsze jest referen­cjąpewnej knotki w relacji GwiazdaFilmowa, a film referencjąpewnej knotki relacji Film można deklarację relacji GwiazdyW zapisać tak, jak to przedsta­wiono na rys. 8.14.

O

CREATE ROW TYPE TypGwiazdyW( gwiazda REF(TypGwiazda), film REF(TypFilm)

): CREATE TABLE GwiazdyW OF TYPE TypGwiazdyW SCOPE FOR gwiazda IS GwiazdaFilmowa, SCOPE FOR film IS Filmy

RYSUNEK 8.14

Deklarowanie zakresu atrybutów referencji

8.5.7. Identyfikatory obiektów jako wartości

W językach zorientowanych obiektowo obowiązuje zasada, że ID obiektów są wewnętrznymi elementami systemu, do których nie ma dostępu z poziomu języka zapytań. W języku OQL na przykład ta reguła jest prze­strzegana. Ale tak naprawdę to zupełnie nie widać powodu, dlaczego tak się

8.5. OBIEKTY KROTKOWE W JĘZYKU SQL3 SO7

dzieje, że nie ma bezpośredniego dostępu do identyfikatorów obiektów i dla­tego w SQL3 otwarto taką możliwość. Deklarując relację lub jej typ wiersza, można określić atrybut, którego wartością jest referencja krotki definiowane­go typu. Jeśli do deklaracji typu wiersza lub tabeli dołączymy następującą klauzulę:

VALUES FOR <atrybut> ARE SYSTEM GENERATED

to wartością wymienionego atrybutu będzie referencja do krotki, która ją za­wiera. W ten sposób taki atrybut może pełnić funkcje klucza i jednocześnie być identyfikatorem obiektu krotki.

PRZYKŁAD 8.29

Przekształćmy rys. 8.13 w ten sposób, że zarówno GwiazdaFilmowa, jak i Film zostaną wyposażone w dodatkowe atrybuty oznaczające ID ich obiektów; nazwiemy te atrybuty odpowiednio gwiazda_id oraz film_id. Zmodyfikowany schemat został przedstawiony na rys. 8.15. Poniżej przed­stawiamy opis zmian, które trzeba było zrobić:

1. Do typu wiersza TypFilm został dołączony atrybut film_id.

2. Do typu wiersza TypGwiazda został dołączony atrybut gwiazda_id. 3. Do deklaracji tabeli Film została dołączona klauzula informująca o tym, że wartość atrybutu film_id jest generowana przez system.

4. Do deklaracji tabeli GwiazdaFilmowa została dołączona klauzula in­formująca o tym, że wartość atrybutu gwiazda-id jest generowana przez system.

5. Powtórzono deklarację SCOPE z przykładu 8.28.

Teraz już dysponujemy bardziej konwencjonalnym sposobem utworzenia zapytania omawianego w przykładzie 8.27, które powoduje wyszukanie fil­mów z udziałem Mela Gibsona. Można bowiem w klauzuli WHERE przyrów­nać odniesienie z relacji GwiazdyW do atrybutu wskazującego ID obiektów w relacjach GwiazdaFilmowa oraz Film, które są referencjami własnych krotek. Zapytanie ma następującą postać:

SELECT Film.tytuł

EROM GwiazdyW, GwiazdaFilmowa, Film

WHERE GwiazdyW.gwiazda = GwiazdaFilmowa.gwiazda_id AND GwiazdyW.film = Film.film_id AND GwiazdaFilmowa.nazwisko = `Mel Gibson';

Na podstawie klauzuli FROM wiadomo, że będą tworzone trójki z kolejnych krotek relacji: Gwiazdyw, GwiazdaFilmowa i Film. Pierwszy warunek klauzuli wHERE zapewnia, że knotka z relacji Gwiazdyw musi odpowiadać

508

CREATE ROW film_id tytuł rok czyKolor ):

CREATE ROW ulica miasto ):

8. ZORIENTOWANE OBIEKTOWO JĘZYKI ZAPYTAŃ

TYPE Typ Film( REF(TypFilmu), CHAR(30), INTEGER, BIT(1)

TYPE TypAdres CHAR (50), CHAR (2 0 )

CREATE ROW TYPE TypGwiazda ( gwiazda_id REF(TypGwiazda), nazwisko CHAR(30),

adres TypAdres );

CREATE ROW TYPE TypGwiazdyW( gwiazda REF(TypGwiazda), film REF(TypFilm) );

CREATE TABLE Film OF TYPE TypFilm VALUES FOR film id ARE SYSTEM GENERATED;

CREATE TABLE GwiazdaFilmowa OF TYPE TypGwiazda VALUES FOR gwiazda id ARE SYSTEM GENERATED;

CREATE TABLE GwiazdyW OF TYPE TypGwiazdyW SCOPE FOR gwiazda IS GwiazdaFilmowa, SCOPE FOR film IS Film;

RYSUNEK 8.15

Dołączenie identyfikatorów obiektów do relacji

(w składowej gwiazda) krotce z relacji GwiazdaFilmowa. Z kolei drugi warunek określa, że krotka z relacji GwiazdyW musi także odpowiadać (w skladowej film) krotce z tabeli Film. Te warunki razem oznaczają, że krotki ztabel GwiazdaFilmowa oraz Film zawierają odpowiednio gwiazdę oraz film, które stanowią parę w relacji GwiazdyW.

Trzeci warunek określa, że poszukiwaną gwiazdą filmową jest Mel Gib­son, a klauzula sELECT - że w wyniku będzie zapisany tytuł poszukiwanego filmu. W dalszym ciągu stosujemy referencję w zapytaniu, tak samo jak w przykładzie 8.27. Tamto rozwiązanie jest co prawda trochę prostsze, ale

8.5. OBIEKTY KROTKOWE W JĘZYKU SQL3 SO9

rozwiązanie przedstawiane ostatnio jest przede wszystkim ilustracją możliwo­ści stosowania identyfikatorów obiektów.

0

8.5.8. Ćwiczenia do podrozdziału 8.5

Ćwiczenie 8.5.1. Należy napisać deklaracje typu dla następujących typów:

a) TypNazwisko, którego składowe oznaczają imię, drugie imię, nazwisko oraz tytuł.

*b) TypOsoba, który zawiera nazwisko osoby oraz referencję do matki i ojca tej osoby. Należy skorzystać z typu zdefiniowanego w punkcie a).

c) TypMałżeństwo zawiera datę ślubu oraz referencje do męża i żony.

Ćwiczenie 8.5.2. Należy powtórnie zaprojektować schemat bazy danych produktów z ćwiczenia 4.1.1, przy czym tam, gdzie jest to wygodne, należy zastosować typ wier­sza oraz odniesienie. A na pewno trzeba, aby atrybut model w relacjach PC, Laptop i Drukarka był odniesieniem krotki relacji Produkt.

Ćwiczenie 8.5.3. Korzystając z ćwiczenia 8.5.2 należy napisać następujące zapytania. Wszędzie, gdzie to jest właściwe, należy użyć referencji:

a) Należy wyszukać producentów PC, które są wyposażone w dyski twarde o pojemności powyżej 2 gigabajtów.

b) Należy wskazać producentów drukarek laserowych.

!c) Należy utworzyć tabelę, w której obok każdego laptopa umieszcza się także model laptopa tego samego producenta, i który ma największą szybkość procesora.

!Ćwiczenie 8.5.4. W ćwiczeniu 8.5.2 sugerowaliśmy, że numery modeli w tabelach PC, Laptop oraz Drukarka mogłyby być referencjami do knotek tabeli Produkt. Czy atrybut model w tabeli Produkt także mógłby być referencją do knotek w relacji opisującej dany typ produktu. Dlaczego tak albo dlaczego nie?

*Ćwiczenie 8.5.5. Należy powtórnie zaprojektować schemat bazy danych okrętów wojennych z ćwiczenia 4.1.3, przy czym tam, gdzie jest to wygodne, należy zastoso­wać typ wiersza oraz referencji. Schemat z ćwiczenia 8.1.2 zawiera wskazówki, w których miejscach są użyteczne atrybuty referencji. Należy szukać związków typu wiele do jeden i starać się reprezentować je za pomocą atrybutów typu referencje.

Ćwiczenie 8.5.6. Korzystając ze schematu z ćwiczenia 8.5.5, należy napisać nastę­pujące zapytania. Tam gdzie jest to wygodne, należy użyć referencji oraz należy uni­kać złączeń (tzn. podzapytań lub więcej niż jednej zmiennej krotkowej w klauzuli FROM).

*a) Należy odszukać te okręty, których wyporność przekracza 35 000 ton. b) Należy wskazać bitwy, w których zatonął co najmniej jeden okręt.

S I O 8. ZORIENTOWANE OBIEKTOWO JĘZYKI ZAPYTAŃ

!c) Należy wskazać klasy z co najmniej jednym zatopionym okrętem.

!!d) Należy wskazać bitwy, w których został uszkodzony co najmniej jeden okręt floty USA.

8.6. Abstrakcyjne typy danych w języku SQL3

Typy wiersza oraz referencje do typów wiersza zapewniają w języku SQL3 spełnianie tych samych funkcji, które w języku OQL są dostępne dzięki obiektom. Poza tym można dzięki nim w wygodny sposób modyfikować „obiekty", za pomocą operatorów wstawiania i usuwania z SQL. W języku OQL natomiast takie modyfikacje są dostępne wyłącznie z poziomu języka programowania zorientowanego obiektowo, np. C++.

Niestety typy wiersza nie zapewniają hermetyzacji, która jest dostępna w językach programowania zorientowanych obiektowo. Przypomnimy tutaj to, o czym była mowa w p. 1.3.1, a mianowicie, że „hermetyzacja" klasy za­pewnia, że obiekty tej klasy mogą być zmieniane tylko za pomocą stałego zestawu operatorów, które definiuje się wraz z klasą. „Hermetyzacja" jest używana profilaktycznie przeciw błędom programowania powstającym w wyniku takiego korzystania z danych, którego projektant bazy danych nie przewidział albo nie zamierzał dopuścić.

Typy wiersza nie podlegają hermetyzacji, działania na krotkach typu wier­sza wykonuje się, stosując wyrażenia dostępne w języku SQL3. Interfejsy z ODL (czyli klasy) także nie zawierają pełnej hermetyzacji (kapsułkowania), ponieważ składowe obiektów są osiągalne poprzez zapytania OQL. Z drugiej strony pobieranie wartości wewnętrznych struktury obiektu w zasadzie jest mniej niebezpieczne niż nieoczekiwana zmiana obiektu. W języku OQL nie można aktualizować obiektów inaczej niż korzystając z metod (patrz p. 8.1.2). Te metody, najczęściej napisane w konwencjonalnym obiektowym języku pro­gramowania, np. w C++~ można wykonywać tylko na obiektach danej klasy.

W języku SQL3 istnieją inne definicje „klas", które zapewniają hermety­zację, są to abstrakcyjne typy danych (abstract data type), w skrócie ADT. Obiektów rodzaju ADT używa się jako składowych krotek, a nie jako krotek. Mają one jednak strukturę krotki, tak samo jak obiekty w języku ODL zazwy­czaj mają strukturę zawierającą składowe.

8.6.1. Definiowanie abstrakcyjnych typów danych

Postać definicji ADT została przedstawiona na rys. 8.16. W wierszu 1) występuje instrukcja create, w której umieszcza się nazwę typu ADT. W wierszu 2) umieszczono listę nazw atrybutów i ich typów.

8.6. ABSTRAKCYJNE TYPY DANYCH W JĘZYKU SQL3 S 1 I

1) CREATE TYPE <nazwa typu(

2) lista atrybutów oraz ich typów

3) opcjonalna deklaracja funkcji = and < dla

deklarowanego typu

4) deklaracja funkcji (metod) typu 5) );

RYSUNEK 8.16

Definiowanie abstrakcyjnych typów danych

W wierszu 3) na rys. 8.16 występują opcjonalne deklaracje operatorów porównania = i <. Funkcja równości ma następującą postać:

EQUALS <nazwa funkcji implementującej równość obiektów>

Funkcja < jest deklarowana w podobny sposób, tylko zamiast słowa kluczowe­go EQUALS występuje slowo kluczowe LESS THAN`. Zauważmy, że pozostałe cztery operatory porównania można definiować za pomocą dwóch wymienio­nych, a zatem nie muszą być jawnie definiowane. Na przykład „<_" oznacza „_ lub <", z kolei > oznacza „nie <". Gdy zdefiniuje się operatory = i <, to wartości z ADT można porównywać w klauzulach wHERE tak samo jak wartości kon­wencjonalnych typów SQL, takie jak np. wartości całkowite lub teksty.

W wierszu 4) określono możliwość deklarowania innych funkcji (czyli metod) dla typów ADT. Poza tym w SQL3 istnieją dla typów ADT „wbudo­wane" funkcje standardowe, których nie trzeba ani definiować, ani deklaro­wać. Należą tam:

1. Konstruktor - funkcja, która tworzy nowy obiekt danego typu. Wszystkie atrybuty tego obiektu mają na początku wartości NULL. Je­śli T jest nazwą typu ADT, to T() oznacza konstruktor tego typu.

2. Obserwator - funkcja, która dla każdego atrybutu zwraca jego war­tość. Jeśli A jest nazwą atrybutu, a X jest zmienną, której wartością jest pewien obiekt typu ADT, to A(X) oznacza wartość atrybutu A obiektu X. W tym samym celu można także stosować bardziej kon­wencjonalną notację XA.

3. Modyfikator - funkcja, która poszczególnym atrybutom przypisuje nowe wartości. Można z nich korzystać po lewej stronie instrukcji przypisania w sposób opisany w p. 8.6.2. Aby zapewnić hermetyzację, trzeba zablokować dostęp do tych funkcji. W SQL3 przyjęto, że do wykonania tych funkcji trzeba mieć określone prawo ExECUTE. To prawo może być nadawane lub odbierane na takich samych zasadach jak sześć praw w języku SQL2, co omówiono w p. 7.4.1.

` W miarę ewolucji standardu SQL3 prawdopodobnie te funkcje nie będątraktowane spe­cjalnie, ale będą definiowane i używane tak samo jak inne funkcje typów.

S 12 8. ZORIENTOWANE OBIEKTOWO JĘZYKI ZAPYTAŃ

Pozostałe funkcje można definiować albo wewnątrz, albo poza instrukcją CREATE TYPE. Jednak, gdy występują one poza tą instrukcją, to można w nich i tak korzystać tylko z funkcji definiowanych wewnątrz, włącznie z wymienionymi powyżej funkcjami wbudowanymi.

PRZYKŁAD 8.30

W przykładzie 8.22 zdefiniowano adres jako typ wiersza, złożony ze składo­wych ulica i miasto. Taka sama struktura może stanowić definicję typu ADT. Jednak w tym przypadku uzyskamy efekt hermetyzacji, nie będzie bowiem dostępu do składowych miasto i ulica, chyba że do funkcji obserwatora i mo­dyfikatora zostanie nadane prawo wykonania.

1) CREATEyTYPE AdresADT 2) ulica CHAR (50), 3) miasto CHAR(20), 4) EQUALS adrR,

5) LESS THAN adrMN

tutaj deklaruje się inne funkcje

RYSUNEK 8.17 Definiowanie adresu ADT

Na rysunku 8.17 przedstawiono definicję typu AdresADT, ale bez defini­cji funkcji i metod związanych z tym typem ADT. W wierszu 1) określono nazwę typu.

W wierszach 2) i 3) zdefiniowano składowe krotek, o nazwach ulica i miasto. Typy składowych są takie same jak w przykładzie 8.22: teksty o długościach odpowiednio SO oraz 20.

W wierszach 4) i S) ustalono nazwę operatora równości obiektów typu AdresADT jako adrR, a operator c w tym przypadku nazwano adrMN. Nie wiemy jednak, jaka jest treść tych funkcji, ponieważ nie podano ich definicji. W przykładzie 8.32 podaliśmy pewną ich wersję. Następnie adresy zostaną uporządkowane leksykograficznie, najpierw według nazwy miast, a potem według ulicy.

0

Następny przykład stanowi doskonałą ilustrację siły tkwiącej w typach ADT, które umożliwiają programowanie takich działań w języku SQL, ja­kich nie można było przewidzieć w założeniach dotyczących klasycznych systemów zarządzania bazami danych. W bazach danych można teraz prze­chowywać bardzo duże jednostki danych, takie jak obrazy, ścieżki dźwię­kowe lub filmy. Ale działania na tych obiektach znacznie odbiegają od kla­sycznych funkcji SQL takich jak: porównywania, drukowanie, agregowanie

8.6. ABSTRAKCYJNE TYPY DANYCH W JĘZYKU SQL3

513

itd. Teraz trzeba umieć wyświetlić te obiekty na ekranie, często stosując złożone algorytmy dekodowania, a w przyszłości zapewne będzie można także wykonywać skomplikowane porównania obrazów lub identyfikować ich właściwości.

PRZYKŁAD 8.31

Zdefiniujemy teraz typ ADT o nazwie Mpeg, potrzebny do zapisywania filmów kodowanych w standardzie MPEG (jest to standardowy sposób kompresowania Elmu, szczegóły tego formatu opisano w ramce). Od strony technicznej MPEG jest ciągiem znaków, a więc można by tutaj stoso­wać podstawowy typ VARCHAR. Jednakże filmy zakodowane w postaci MPEG są często bardzo długie (gigabajty) i trudno jest działać na tak dłu­gich tekstach.

Aby umożliwić dostęp do bardzo dużych jednostek danych, takich jak wideo, we współczesnych bazach danych wprowadza się specjalny typ da­nych, nazwany BLOB (binary large Object - binarne wielkie obiekty), czyli specjalny rodzaj ciągów bitowych, które mogą być bardzo długie, dopuszcza się nawet ich wielkość mierzoną w gigabajtach.

1) CREATE TYPE Mpeg

2) wideo BLOB, 3) długość INTEGER,

4) copyright VARCHAR(255), 5) EQUALS DEFAULT,

6) LESS THAN NONE

tu umieścić definicje funkcji );

RYSUNEK 8.18

Definiowanie typu ADT MPEG

W naszym przykładzie zakładamy, że do systemu baz danych tryb BLOB został wbudowany. Stosowną definicję typu ADT dla formatu MPEG przed­stawiono na rys. 8.18.

W wierszu 2) został określony typ BLOK atrybutu wideo. Jego warto­ściami są bardzo duże filmy zakodowane w formacie MPEG. W wierszach 3) oraz 4) zadeklarowano dwa „zwykłe" atrybuty: długość (czyli czas trwania filmu) oraz copyright. W wierszu 5) określono, że operatorem równości w tym przypadku jest standardowa, przyjmowana przez domniemanie, iden­tyczność. Oznacza to, że dwa obiekty typu MPEG uważa się za równe, jeśli bitowe reprezentacje ich wszystkich atrybutów są identyczne. Można by się zastanawiać nad bardziej złożoną definicją równości, w której dwa filmy

S I4 8. ZORIENTOWANE OBIEKTOWO JĘZYKI ZAPYTAŃ

traktowano by jako równe, jeśli wyglądałyby tak samo po zdekodowaniu na ekranach o zbliżonej rozdzielczości, ale nie możemy więcej uwagi poświęcać temu zagadnieniu. Z instrukcji w wierszu 6) wiadomo, że operator < nie jest określony dla wartości typu Mpeg. Oznacza to, że jeśli A i B są wartościami typu Mpeg, to wyrażenie A < B jest niedopuszczalne.

0

Kod filmowy MPEG

Ponieważ filmy zajmują w pamięci wiele przestrzeni, więc zazwyczaj przechowuje się je w postaci kodowanej, przy czym istnieje wiele rożnych standardów kompresowania. Najbardziej popularny standard - MPEG ­korzysta przy tworzeniu kodu z faktu, że bezpośrednio kolejne klatki fil­mowe są bardzo do siebie podobne. Zatem można klatki podzielić na ob­szary i obszar z jednej klatki traktować jako wskaźnik do obszaru z po­przedniej klatki. Obszar w klatce poprzedniej może znajdować się w tym samym położeniu, jeśli jest to fragment tła, albo w innym (jeśli jest to fragment obiektu ruchomego).

Mimo że, stosując standard MPEG, uzyskuje się dużo lepsze wyniki niż przy zwykłych kompresjach tekstowych, to i tak po skompresowaniu godzinny film zajmuje około 1 gigabajta pamięci. A dodatkowo, ponieważ dopuszcza się niewielkie różnice w odpowiadających sobie obszarach, więc zarazem obraz traci na jakości. Proces dekompresji filmu jest także dość skomplikowany. Jednakże mimo tych kłopotów standard MPEG sta­nowi niezły kompromis między jakością obrazu, objętością pliku oraz mo­cą obliczeniową potrzebną do przetwarzania.

8.6.2. Definiowanie metod dla typów ADT

Tuż za listą atrybutów w deklaracji typu ADT można umieścić dowolną listę deklaracji funkcji. Postać deklaracji funkcji jest następująca:

FUNCTION <nazwa> (<argumenty>) RETURNS <typ>;

Każdy argument składa się z nazwy zmiennej oraz typu zmiennej. Argumenty oddziela się przecinkami.

Funkcje mogą być wewnętrzne oraz zewnętrzne. Funkcje zewnętrzne za­pisuje się w języku podstawowym, a w definicji typu ADT umieszcza się tylko ich sygnaturę. Funkcje zewnętrzne zostaną omówione w p. 8.6.3. Funk­cje wewnętrzne pisze się w rozszerzonym SQL. Poniżej przedstawiamy kilka możliwości rozszerzenia zarówno SQL2, jak i zapytań SQL3.

:= jest stosowany jako operator przypisania.

8.6. ABSTRAKCYJNE TYPY DANYCH W JĘZYKU SQL3

515

Zmienne lokalne funkcji deklaruje się, umieszczając po dwukropku nazwę i typ zmiennej.

Operator kropka udostępnia wartości składowych struktur. Wyrażenia logiczne są takie same jak w klauzuli wHERE.

Treść procedury stanowią instrukcje ujęte w nawiasy BEGIN i END.

Wielkie obiekty binarne

Wartości typu BLOB mogą wyglądać dla użytkownika jak wielkie ciągi bitów, ale ich implementacja jest dużo bardziej skomplikowana niż im­plementacja tekstów o długości nie przekraczającej 255 bajtów. Nie ma na przykład sensu przechowywanie obszernych tekstów jako składowych krotek, ponieważ i tak trzeba je przechowywać poza bazą danych, najczę­ściej w systemie plików.

I jeszcze inny przykład: w modelu klient-serwer, opisanym w p. 7.3.4, zakłada się, że krotki mają umiarkowane rozmiary i serwer przesyła do klienta w odpowiedzi całą krotkę. Ale w przypadku wartości typu BLOB nie ma sensu natychmiastowe przesłanie całej jednostki danych. Jeśli na przykład do serwem zostało skierowane polecenie przesłania filmu, to serwer może go przesyłać w niewielkich porcjach, po kilka sekund wy­świetlania. Klient może wówczas od razu to wideo wyświetlać i nie musi pamiętać całego filmu lokalnie, ani czekać zanim zostanie przesłana całość filmu.

PRZYKŁAD 8.32

Powrócimy do przykładu 8.30, gdzie zdefiniowano adres ADT. Na rysun­ku 8.19 przedstawiono kilka funkcji, które można dołączyć za pomocą in­strukcji tworzenia typu z rys. 8.17.

W wierszach od 1) do 6) zamieszczono definicję konstruktora obiektów typu adresADT. Przypomnijmy, że w języku SQL istnieje zero - argumento­wy wbudowany konstruktor obiektów dla typów ADT, w tym przypadku jest to funkcja o nazwie AdresADT. Jednakże my chcemy, aby konstruktor miał dwa argumenty, których wartości określą miasto i ulicę w tworzonym adresie. Można te funkcje nazwać jakkolwiek, ale jest dopuszczalne, a nawet wskaza­ne, użycie nazwy konstruktora tej samej co nazwa klasy.

Deklaracja nowego konstruktora została zamieszczona w wierszu 1). Ko­rzysta się tu z dwóch argumentów s i c, które oznaczają odpowiednio ulicę oraz miasto. Są one oba napisami o długościach odpowiednio 50 i 20 znaków. Wartość funkcji ma typ AdresADT. W wierszu 2) deklaruje się zmienną lo­kalnąatypu AdresADT.

Treść funkcji została zapisana w wierszach od 3) do 6). W wierszu 3) tworzy się nowy obiekt typu AdresADT i staje się on wartością zmiennej : a. Nie da się pomylić konstruktora wbudowanego z konstruktorem bieżąco two­

S I C) 8. ZORIENTOWANE OBIEKTOWO J~ZYKI ZAPYTAŃ

1) FUNCTION AdresADT(:s CHAR(50), :c CHAR(2) RETURNS AdresADT;

2) :a AdresADT; BEGIN 3) :a := adresADT(); 4) :a.ulica :_ :s; 5) :a.miasto :_ :c; 6) RETURN :a;

END;

7) FUNCTION adrR(:al AdresADT, :a2 AdresADT) RETURNS BOOLEAN;

8) RETURN (:al.ulica = :a2.ulica AND :al.miasto = a2.miasto);

9) FUNCTION adrMN(:al AdresADT, :a2 AdresADT) RETURNS BOOLEAN;

10) RETURN ((:al.miasto < a2.miasto) OR (:al.miasto = :a2.miasto)

AND :al.ulica < a2.ulica));

11) FUNCTION całyAdres(:a AdresADT) RETURNS CHAR(82); 12) :z CHAR(10);

BEGIN 13) :z = znajdźKod(:a.ulica, :a.miasto); 14) RETURN(:a.ulica II ~ ~ II

:a.miasto II ~ ~II :z): END;

RYSUNEK 8.19

Niektóre funkcje adresu ADT

rzonym, ponieważ mają one inne argumenty. A więc nie można zinterpreto­wać wywołania w wierszu 3) jako wywołania rekurencyjnego. W wierszu 4) wartość pierwszego argumentu zostaje przypisana składowej ulica obiektu a, a w wierszu 5) określa się wartość składowej miasto obiektu a jako przypi­sanie wartości drugiego argumentu. W końcu, w wierszu 6) obiekt a staje się wynikiem działania funkcji.

W wierszach 7) i 8) definiuje się operator równości dla obiektów typu AdresADT. Na rysunku 8.17 w wierszu 4) określono, że ten operator ma się nazywać adrR, a więc stosujemy tutaj tę nazwę. Treść jego jest prosta: zwra­ca on wartość TRUE wtedy i tylko wtedy, gdy są identyczne wartości obu składowych w dwóch obiektach. Jest on zatem taki sam jak równość do­mniemana i można w związku z tym zdefiniować go tak samo, jak zdefinio­wano równość w przykładzie 8.31.

8.6. ABSTRAKCYJNE TYPY DANYCH W JĘZYKU SQL3 S I %

W wierszach 9) i 10) zapisano deklarację operatora <, czyli funkcji adrMN. Określa się tutaj, że pierwszy adres jest mniejszy od drugiego, jeśli miasto z pierwszego adresu poprzedza leksykograficznie miasto z drugiego adresu (w znaczeniu alfabetycznym). Jeśli miasta są te same, to porównuje się ulice.

W wierszach od 11) do 14) definiuje się funkcję całyAdres, która na podstawie obiektu typu AdrADT zwraca pełny adres, tzn. ulicę, miasto oraz 9 - cyfrowy kod pocztowy (wraz z myślnikiem). W wierszu 12) została zade­klarowana zmienna lokalna : z, w której przechowuje się kod pocztowy. W wierszu 13) jest wywołana funkcja znaj dźKod. Jest to funkcja zewnętrz­na, która ma dwa argumenty: ulicę i miasto. Postać deklaracji tej funkcji ze­wnętrznej zostanie omówiona w p. 8.6.3.

Funkcja znaj dźKod zwraca wartość kodu pocztowego określonego ad­resu, a procedura może być bardzo skomplikowana, z przeszukiwaniem innej bazy danych włącznie. Nie będziemy pisać definicji tej procedury. W końcu, w wierszu 14) konkatenujemy adres zapisany w obiekcie : a z kodem pocz­towym zapisanym w : z. Między składowymi adresu umieściliśmy spacje, po to by je od siebie odseparować.

0

8 .6.3 . Funkcj e zewnętrzne

Typy ADT mogą zawierać również metody, które zapisuje się w pew­nym języku podstawowym, niekoniecznie w SQL3. W takich sytuacjach w definicji typu ADT umieszcza się tylko sygnaturę funkcji oraz informację o języku, w którym jest ona określona. Postać deklaracji zewnętrznej jest następująca:

DECLARE EXTERNAL <nazwa funkcji> <sygnatura> LANGUAGE <nazwajęzyka>

PRZYKŁAD 8.33

Aby skorzystać z funkcji znajdźKod, którą zdefiniowano w przykła­dzie 8.32, trzeba ją zadeklarować w definicji typu AdresADT. Funkcja ta ma dwa parametry typu CHARACTER o długościach odpowiednio: SO i 20, a zwra­ca jeden wynik, który jest napisem o długości 10. Poniżej przedstawiamy poprawnądeklarację funkcji:

DECLARE EXTERNAL znajdźKod CHAR(50) CHAR(20) RETURNS CHAR(10)

LANGUAGE C;

Określenie języka jako C oznacza, że adres argumentu będzie przekazany do funkcji znajdźKod w postaci właściwej dla programów zapisanych w C.

a

S I S 8. ZORIENTOWANE OBIEKTOWO JĘZYKI ZAPYTAŃ

8.6.4. Ćwiczenia do podrozdzia~u 8.6

*Ćwiczenie 8.6.1. Należy zdefiniować abstrakcyjny typ danych „PC", którego obiekty reprezentująkomputery osobiste, przez zapis szybkości ich procesorów, wiel­kości RAM i dysku twardego, szybkości CD-ROM-u oraz ceny.

Ćwiczenie 8.6.2. Dla typu określonego w ćwiczeniu 8.6.1 należy zapisać poniższe funkcje w konwencji funkcji wewnętrznych rozszerzonego SQL3:

*a) Funkcja konstruująca o nazwie nowyPC, która pobiera wartości pięciu atrybutów typu ADT PC oraz tworzy nowy obiekt tego typu. Przypomina­my, że w tej funkcji trzeba korzystać z konstruktora wbudowanego PC ( ) .

*b) Funkcja wartość pobiera obiekt PC jako argument i zwraca ,jakość" PC, czyli liczbę rzeczywistą, która określa, jak „dobry" jest ten komputer. For­muła wyliczenia jakości jest następująca: szybkość procesora plus pięcio­krotna wielkość RAM (w megabajtach) plus pięćdziesięciokrotna pojem­ność twardego dysku (w gigabajtach) plus 10 razy szybkość CD-ROM-u.

c) Funkcja lepszy pobiera obiekt typu PC jako argument i zwraca inny PC z dwa razy większymi: szybkością procesora, RAM, twardym dyskiem, szybkością CD-ROM-u, ale w tej samej cenie. Można skorzystać z kon­struktora nowyPC, utworzonego w punkcie a).

d) Funkcja równyPC, która testuje, czy komputery sątakie same. Uznaje ona, że dwa PC są równe, jeśli mają taką samą szybkość procesora i wielkość dysku twardego, inne parametry nie są brane pod uwagę.

e) Funkcja mnPC, która określa relację mniejszości abstrakcyjnego typu PC. Uznaje ona, że p,< p2, jeśli ,jakość" komputera pl jest niższa od „jakości" komputerap2, gdzie ,jakość" jest zdefiniowana w punkcie b).

Ćwiczenie 8.6.3. Należy zdefiniować abstrakcyjny typ Okręty, w którym będą opisywane okręty, przez zapis ich: nazw, dat wodowania, liczby dział, średnicy dział, wyporności oraz zakodowanych w formacie MPEG wideoklipów okrętów w akcji oraz postskriptowego dokumentu opisującego historię okrętu.

Ćwiczenie 8.6.4. Należy utworzyć deklaracje i definicje następujących funkcji odno­szących się do typu ADT Okręty z ćwiczenia 8.6.3:

a) Funkcja siłaBojowa, której argumentem jest obiekt typu Okręt, a która zwraca „siłę bojową", czyli iloczyn liczby dział i sześcianu średnicy.

b) Funkcja odtwórzWideo, której argumentem jest obiekt typu Okręt, a która powoduje wyświetlenie na ekranie wideoklipu tego okrętu, korzysta­jąc w tym celu z funkcji zewnętrznej (trzeba ją zadeklarować), nazywanej odtwórzMpeg, która wyświetla film zapisany w formacie MPEG.

c) Funkcja konstruująca nowyOkręt, której argumentem jest nazwa (innych składowych się nie pobiera), a która tworzy nowy obiekt typu Okręt o podanej nazwie.

d) Funkcja równy0kręt, która testuje, czy okręty są takie same. Uznaje ona, że dwa okręty są równe, jeśli mają taką samą nazwę i rok wodowania, inne parametry nie są brane pod uwagę.

8.7. PORÓWNANIE KONCEPCJI SYSTEMÓW ODL/OQL 1 SQL3 S 19

e) Funkcja mnOkręt, która określa relację mniejszości abstrakcyjnego typu Okr~t. Uznaje ona, że sI< sz, jeśli nazwa okrętu s, jest alfabetycznie wcze­śniejsza niż nazwa okrętu s2, a jeśli nazwy są takie same, to „mniejszy" jest statek o wcześniejszej dacie wodowania.

8.7. Porównanie koncepcji systemów ODL/OQL i SQL3

Po zapoznaniu się z dwoma głównymi standardami proponowanymi dla zorientowanych obiektowo systemów baz danych: ODL/OQL oraz SQL3 określimy, na czym polegają główne różnice między nimi. Prawdą jest, że podobieństwa przeważają nad różnicami i mimo że te standardy pochodzą z dwóch bardzo różnych modeli: języków programowania zorientowanych obiektowo oraz języków relacyjnych baz danych, to w każdym ze standardów w sposób efektywny adaptowano pojęcia z obcego mu modelu.

W bieżącym podrozdziale wymienimy podstawowe różnice oraz sposób, w jaki w tych dwóch koncepcjach ustalono rożne rozwiązania kompromiso­we. Poza tym wskażemy, czym różnią się typy krotkowe i abstrakcyjne typy danych z SQL3 od interfejsów (czyli klas) w języku ODL. A tak naprawdę porównamy trzy koncepcje obiektowości: ODL/OQL, typy krotkowe SQL3 oraz typy wartości w SQL3.

1. Środowisko programistyczne. W systemie OQL zakłada się, że jego instrukcje są osadzane w języku programowania, który ma wspólne z nim środowisko programowania i model danych. Zakłada się rów­nież, że jest to język programowania zorientowany obiektowo, np. C++, Smalltalk lub Java. Natomiast w SQL3 przyjmuje się, że jego obiekty nie należą do środowiska zewnętrznego języka podstawowe­go. Tak samo jak w pozostałych implementacjach SQL, tak i tu ist­nieje wąski interfejs, który umożliwia przekazywanie wartości między strukturami danych SQL a zmiennymi środowiska. W uzupełnieniu zwykłego interfejsu między SQL a językiem podstawowym, który opisywaliśmy w podrozdziale 7.1, powstała w systemie SQL3 kon­cepcja funkcji zewnętrznych abstrakcyjnych typów danych.

2. Rola relacji. W modelu danych SQL3 pojęcie relacji odgrywa glów­ną rolę. Typy wiersza w rzeczywistości są opisem relacji, a typy ADT służą temu, by można byto w sposób bardziej elastyczny defi­niować typy atrybutów relacji. Natomiast w języku OQL główną rolę odgrywają zbiory i wielozbiory obiektów lub struktur ze wzglę­du na funkcje pełnione w instrukcjach typu select-from-where. Ko­lekcje struktur w systemie ODL/OQL są bardzo zbliżone do pojęcia relacji SQL3.

520

8. ZORIENTOWANE OBIEKTOWO JĘZYKI ZAPYTAŃ

3. Hermetyzacja (kapsulkowanie). Typy wiersza nie zapewniają herme­tyzacji. W języku SQL dopuszcza się, aby relacje, krotki i składowe z danego typu wiersza były odczytywane i modyfikowane na wszelkie możliwe sposoby. Natomiast abstrakcyjne typy danych w SQL3 umożliwiają hermetyzację w zwykłym znaczeniu. W zakresie herme­tyzacji pojęcie ADT z SQL3 jest zbliżone do pojęcia klasy w języku ODL.

4. Zasięg klasy. W języku OQL zakłada się, że dla każdej klasy określa się jej zasięg. Referencje (czyli związki w terminach OQL) wskazują elementy z zasięgu. W języku SQL3 można by pełnić podobną kon­trolę nad zasięgiem typu wiersza, czyli relacją, która zawiera wszyst­kie istniejące w bazie krotki tego typu, chociaż nie ma takiej koniecz­ności. Jeśli nie określi się zasięgu typu wiersza, to mogą pojawić się kłopoty ze znalezieniem relacji, do których krotki odwołują się po­przez referencje (odniesienia) zapisane w składowych, pisaliśmy o tym w p. 8.5.6 poświęconemu zakresowi referencji.

5. Hutowalność obiektów. Obiekt nie jest mutowalny, jeśli po utworze­niu nie da się zmienić żadnej jego części. W tym znaczeniu obiekty typu elementarnego nie są mutowalne. Jeśli jednak można przy za­chowaniu identyfikatora obiektu zmieniać jego składowe, to o takim obiekcie mówi się, że jest mutowalny. Klasy w języku ODL i typy wiersza z SQL3 są definicjami dla obiektów mutowalnych, tyle że w systemie ODL/OQL zakłada się, że modyfikacja obiektu może na­stąpić wyłącznie w języku podstawowym, a nie za pośrednictwem OQL. Typy abstrakcyjne ADT w SQL3 nie są tak całkiem niemuto­walne. Funkcja modyfikująca tworzy nowe wartości tych obiektów, które mogą zastąpić wartości istniejące, podobnie jak to się dzieje w przypadku użycia funkcji UPDATE w języku SQL, która np. może spowodować w krotce zastąpienie pewnej wartości typu integer inną wartością tego samego typu.

6. Identyfikator obiektu. Zarówno w języku ODL, jak i w typach ADT w SQL3 stosuje się konwencjonalną interpretację identyfikatora obiektu: jest to jednostka tworzona przez system, do której użytkow­nik nie ma dostępu. Ta zasada nie stosuje się tylko do typu wiersza w SQL3. Użytkownik może np. utworzyć kolumnę, w której prze­chowuje się identyfikator krotki, do której ta wartość należy, tak jakby była to zwykła wartość. W efekcie identyfikatory krotek mogą pełnić funkcje klucza relacji. Można w tym doszukać się pewnego sensu, mimo że w wyniku tego rozwiązania mogą się pojawić konsekwencje typu krotek wiszących, powstających po wykonaniu instrukcji update lub delete. Jeśli jednak nie dopuszcza się takiej możliwości, to relacje mają zazwyczaj określone dwa klucze: identyfikator obiektu oraz inną sztuczną wartość, taką jak np. numer ubezpieczenia społecznego lub „certyfikat" z naszego przykładu filmowego.

8.8. PODSUMOWANIE

8.8. Podsumowanie

521

1 Metody w ODL (methods in ODL): Prócz atrybutów i związków, które poznawaliśmy w rozdziale 2, w języku ODL można także jako część specyfikacji interfejsu deklarować metody. Definiuje się tylko sygna­tury metod, tzn. typy parametrów wejściowych i wyjściowych. Same metody definiuje się w programie zewnętrznym i zapisuje w zorien­towanym obiektowo języku podstawowym.

1 System typów w języku OQL (OQL type systems): Typy konstruuje się z nazw klas oraz typów atomowych (np. integer). Konstruktorami ty­pów są StrUCt, tworzący struktury, oraz operatory tworzące kolek­cje: zbiory, wielozbiory, listy oraz tablice. System typów jest taki sam jak w języku ODL, ale w języku OQL nie ma ograniczeń na poziom zagnieżdżenia konstruktorów typu.

1 Instrukcje select Erom-where w języku OQL (select from-where­statements in OQL): W języku OQL jest dostępna instrukcja select­-from-where, która przypomina takie samo wyrażenie z SQL. W klau­zuli EROM deklaruje się zmienne, które przebiegają kolekcje zawiera­jące zarówno zasięgi klas (czyli odpowiedniki relacji), jak i kolekcje, które są wartościami atrybutów obiektów.

1 Typowe operatory OQL (common OQL operators): W języku OQL występują kwantyfikatory: ogólny i egzystencjalny, operatory zN, su­my, przecięcia, różnice oraz agregaty. Wszystkie one są podobne do operatorów z SQL. Agregowanie jednak zawsze dotyczy kolekcji, a nie kolumn relacji.

1 Group by w języku OQL: Klauzula GROUP BY instrukcji select-from­-where jest także dostępna w języku OQL i jej znaczenie jest podobne do znaczenia z SQL. W języku OQL kolekcje obiektów w poszcze­gólnych grupach są dostępne za pośrednictwem pola o nazwie parti­tion.

1 Wyodrębnianie elementów kolekcji w języku OQL (extracting elements from OQL collections): Można uzyskać dostęp do pojedynczego ele­mentu kolekcji, stosując operator ELEMENT, pod warunkiem że kolek­cja nie zawiera innych elementów. Aby uzyskać dostęp do elementów kolekcji o większej liczbie elementów, należy najpierw przekształcić kolekcję w listę, stosując w tym celu klauzulę ORDER BY w instrukcji select-from-where, a potem pobierać kolejne elementy w pętli prze­biegającej tę listę.

1 Obiekty w języku SQL3: Istnieją dwa typy obiektów: typy wiersza oraz abstrakcyjne typy danych. Typy wiersza są przeznaczone do definio­wania krotek, a typy ADT do definiowania składowych krotek.

1 Identyfikator obiektu i typ wiersza (object identity for row types): Dla każdego typu wiersza jest określony typ referencji, a jego wartościami są ID obiektów krotek. SQL3 dopuszcza, aby typem atrybutu była re­

522

8. ZORIENTOWANE OBIEKTOWO JĘZYKI ZAPYTAŃ

ferencja do typu wiersza jego własnej relacji, a także aby wartością atrybutu było ID obiektu krotki, w której ta wartość występuje, w ten sposób powstaje możliwość korzystania z ID obiektu w charakterze klucza.

Abstrakcyjne typy danych w języku SQL3 (abstract data types in SQL3): Można zadeklarować typ ADT, korzystając z instrukcji CRE­ATE TYPE. Wartościami ADT są struktury z jedną lub wieloma skła­dowymi oraz z metodami.

Metody w typach ADT w języku SQL3: Dla typów ADT można defi­niować funkcje (metody). Można je programować albo w językach zbliżonych do SQL, albo'jako funkcje zewnętrzne, zapisywane w ję­zyku podstawowym.

8.9. Literatura do rozdziaiu 8

Literatura do języka OQL jest ta sama co do języka ODL: [1]. Dokumentacja dotycząca SQL3 została opisana w notce bibliograficznęj do rozdziału 5. Poza tym w [3] znajduje się opis obiektów typu wiersza, a [2] stanowi jedno z pierwszych opracowań na temat abstrakcyjnych typów danych w języku SQL3, z którego powstawały ich nowe koncepcje.

1. Cattel R.G.G. (ed.): The Object Database Standard: ODMG-93 Release 1.2, Morgan­-Kaufmann, San Francisco, 1996

2. Melton J., Bauer J., Kulkami K.: Object ADT's (with improvements for value ADT's), ISO WG3 report X3H2-91-083, kwiecień 1991.

3. Kulkarni K., Carey M., DeMichiel L., Mattos N., Hong W., Ubell M.: introducing reference types and cleaning up SQL3's object model, ISO WG3 report X3H2-95-456, listopad 1995.



Wyszukiwarka

Podobne podstrony:
Podstawy zarządzania wykład rozdział 05
2 Realizacja pracy licencjackiej rozdziałmetodologiczny (1)id 19659 ppt
Ekonomia rozdzial III
rozdzielczosc
kurs html rozdział II
Podstawy zarządzania wykład rozdział 14
7 Rozdzial5 Jak to dziala
Klimatyzacja Rozdzial5
Polityka gospodarcza Polski w pierwszych dekadach XXI wieku W Michna Rozdział XVII
Ir 1 (R 1) 127 142 Rozdział 09
Bulimia rozdział 5; część 2 program
05 rozdzial 04 nzig3du5fdy5tkt5 Nieznany (2)
PEDAGOGIKA SPOŁECZNA Pilch Lepalczyk skrót 3 pierwszych rozdziałów
Instrukcja 07 Symbole oraz parametry zaworów rozdzielających
04 Rozdział 03 Efektywne rozwiązywanie pewnych typów równań różniczkowych
Kurcz Język a myślenie rozdział 12
Ekonomia zerówka rozdział 8 strona 171
28 rozdzial 27 vmxgkzibmm3xcof4 Nieznany (2)
Meyer Stephenie Intruz [rozdział 1]
04 Rozdział 04

więcej podobnych podstron