8
Zorientowane obiektowo j ęzyki zapytań
W bieżącym rozdziale przedstawimy dwie koncepcje wprowadzenia programowania 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 paradygmatami 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 wykonuje się latwiej w jednym języku, a inne w drugim. Dlatego zaraz po opisaniu zorientowanych obiektowo wlaściwości standardu SQL3 dokonamy porównania 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 obiektó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 rozszerzony 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 funkcji, która odnajduje w nich pewne słowa kluczowe. Jeśli z kolei obiektami są mapy lub inne obrazy, to warto mieć możliwość wyświetlania ich w określony sposób. Nawet na danych o konwencjonalnej strukturze rekordów, takich jak dane w przykładzie filmowym, można wykonać dodatkowe 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ólnych 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 przekazuje się, korzystając z mechanizmu opisanego w podrozdziale 7.1.
Powiązanie definicji ODL z językiem podstawowym jest łatwiejsze. Zakł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łumaczyć z ODL. A ponadto zmienne z języka podstawowego, które odpowiadają 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 deklaracjami 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 podrozdziale 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 implementowania schematu w języku programowania można automatycznie sprawdzać, 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ż sprawdzać 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ęzyku C, przy czym jest ona rozszerzona o dwa elementy.
1. Parametry funkcji określa się jako in, out lub inout, co oznacza odpowiednio 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 wyliczonych 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 mechanizmach komunikowania się funkcji poprzez parametry lub wartość powrotu. Wyjątek na ogól oznacza nietypowe lub nieoczekiwane warunki, które są obsługiwane przez pewne funkcje także odpowiedzialne za ich występowanie (najczęściej bezpośrednio lub w ciągu wywolań). 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 kluczom 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 sygnaturami 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 relacji 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) umieszczono deklarację długośćWGodzinach. Zakładamy, że służy ona do utworzenia wartości określającej długość filmu, którego obiekt wywołuje, ale wyrażonej nie w minutach (jak to się dzieje w przypadku atrybutu długość), a skonwertowanej do liczby zmiennopozycyjnej, określającej jej wartość wyraż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 niewlaś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 zwracany 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 nazwiskoGwiazdy. 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 odpowiadają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: inneFilmy. 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 zasięg, czyli nazwę zbioru obejmującego bieżące obiekty tej klasy. Deklaracja obejmuje slowo kluczowe extent, po którym podaje się nazwę zasięgu. Musi ona wystąpić bezpośrednio po deklaracji nazwy interfejsu (klasy).
W pewnym sensie zasięg klasy jest odpowiednikiem nazwy relacji, a definicja samej klasy jest podobna do deklaracji typu atrybutów tej relacji. Przekonamy 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ępowanie obiektu podklasy PC, którego atrybut typ ma wartość „laptop" lub „druinterface 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 zadawania zapytań.
Ponieważ atrybut typ dziedziczy się z klasy Produkt do podklasy Drukarka, 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 odpowiednie 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 uruchomienie 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 uruchamia 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 atrybutach 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 instrukcje 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ęzyku SQL, albo w typowych obiektowych językach programowania.
W języku OQL, inaczej niż w konwencjonalnych językach programowania, 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 instrukcjach języków programowania, np. w C. W założeniu OQL ma być rozszerzeniem 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 zapytań 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 podrozdziale 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 definicję klasy Film. Natomiast definicje klas studio oraz Gwiazda zostaly zaczerpnię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 deklaracji ODL (patrz p. 2.1.7). Ale w języku OQL nie ma ograniczeń na głębokość zagnieżdżania konstruktorów typów.
Przy okazji omawiania systemu typów obowiązującego w językach programowania trzeba było odróżniać deklarację typu zmiennej (czasami nazywaną obiektem mutowalnym) od opisu wartości stałej (nazywaną również obiektem niemutowalnym). W instrukcjach OQL korzysta się albo ze zmiennych zadeklarowanych 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 przedstawiamy 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 logiczny. 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 odpowiednich 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 wielozbió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 oznacza 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 dostę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 rozwią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 przypadkach 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ójFilm. 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 zapytania 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 zapytanie jest takie jak w języku SQL. Nie jest tylko oczywiste, czy można oczekiwać, 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 Filmy; 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 zmienIlyCh.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 wyraż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 dopuszczają występowanie podzapytań w klauzuli FROM.
3. Slowo kluczowe WHERE i wyrażenie logiczne. W tym wyrażeniu, podobnie 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. Najpierw 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 rozpatruje 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 nastę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 wartość 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ęzyka 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 polecenie 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. Poniż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ładzie 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 warunkiem 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ć zapytanie 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 gwiazdy w porządku alfabetycznym, dzięki czemu unikamy tworzenia par złożonych 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 struktury 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 deklaracjach ODL.
O
Niekiedy można uzyskać taki sam efekt jak w przykładzie 8.7, nie definiują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ładu 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ę stanowią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 stanowią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 nawet 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 podzapytania 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 przypadku klauzuli WHERE.
O
8.2.8. Porządkowanie wyniku
Wynikiem wyrażenia select-from-where w języku OQL jest albo wielozbió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 wynosi 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 megabajtó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 zapytanie 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 dziewięć 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 wodowania, 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 wystę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 filmów, w których dana gwiazda s wystąpiła. Następnie w wierszu 4) sprawdzamy, 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 arytmetycznych, jak np. typ integer, a z kolei MIN i MAx można zastosować do kolekcji 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 zawierający dlugości wszystkich filmów. Zauważmy, że nie należy w tym przypadku 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ępują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 sposó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śnienie 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ływać 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 wyprodukowanych 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ść wyprodukowanych 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. Widzimy, że zmienna m przebiega zbiór obiektów klasy Film. A więc m odgrywa 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ą strukturę, 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 odnosi 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ługoś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 same 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 klauzula 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. Warunek 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 wyprodukował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 SELECT, 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ównujemy 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ą reprezentowane 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 produkowane 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 wierszach 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 wyniku podzapytania tylko jeden raz albo wcale, a więc wynik nie zależy od użycia słowa DISTINCT. Jednakże typ wyniku jest inny. Gdy korzysta się z DISTINCT, to typem wyniku jest set<Film>, jeśli opuścimy DISTINCT w jednym 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 najmniej 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 wodowanego 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 przekazywać wartości między krotkami bazy danych a zmiennymi języka podstawowego, 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 selectfrom-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 podstawowego 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 przykł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 wynikiem 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 wielozbioru bezpośrednio przypisać zmiennej pzw, ponieważ prowadzi to do wystą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 trudniejszy 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 wielozbió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 elementu 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 zapisanie 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 wystę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 samego 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ę obiektó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 zastosowano 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ładu 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 argumenty. Sposób definiowania funkcji konstruujących zależy od języka podstawowego.
PRZYKŁAD 8.21
Rozważmy potencjalną funkcję konstruującą dla obiektów klasy Film. Pobiera 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ładowa, nazwana b, jest wielozbiorem { 1, 1 } .
e) Wielozbiór struktur, z których każda ma dwa pola o nazwach a i b. Wartoś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 modeli 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 czterech 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ązkó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, zdefiniowane 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 uproszczeniu 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 filmowych, o właściwościach podobnych do klasy Gwiazda opisanej w przykładzie 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 opisują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 reprezentowania 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 knotkom 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 relacja, 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 dysponować mechanizmami dostępu do składowych tych struktur. W SQL3 w tym celu korzysta się z notacji podwójnej kropki, która jest bliskim odpowiednikiem 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 atrybutó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 naprawdę 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 zapewniona 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 producentami, 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 obejmował 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 instrukcja, 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 podrę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 definiowany 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, GwiazdaFilmowa oraz Gwiazdyw, w których korzysta się z uprzednio zdefiniowanych 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 podrozdziale 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 naturalne. 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 typami. 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 select-from-where w języku SQL po kolei rozważamy krotki z relacji wymienionej 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 operatoró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 poszukiwana 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 przeszukiwać 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 znanych projektantowi schematu wszystkie referencje doprowadzałyby w końcu do jednej relacji, co zdarza się najczęściej. Dlatego w języku SQL3 udostępnia się mechanizm, który umożliwia wyspecyfikowanie relacji, do której odwoluje 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 referencjąpewnej knotki w relacji GwiazdaFilmowa, a film referencjąpewnej knotki relacji Film można deklarację relacji GwiazdyW zapisać tak, jak to przedstawiono 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 przestrzegana. 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 dlatego 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 definiowanego 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ą zawiera. 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 przedstawiamy 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 informują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 filmów z udziałem Mela Gibsona. Można bowiem w klauzuli WHERE przyrównać 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 Gibson, 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 wiersza 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 zastosować 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 unikać 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 zapewnia, ż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 wiersza 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 programowania, np. w C++~ można wykonywać tylko na obiektach danej klasy.
W języku SQL3 istnieją inne definicje „klas", które zapewniają hermetyzację, 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 zazwyczaj 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 kluczowego EQUALS występuje slowo kluczowe LESS THAN`. Zauważmy, że pozostałe cztery operatory porównania można definiować za pomocą dwóch wymienionych, 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 konwencjonalnych 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 „wbudowane" funkcje standardowe, których nie trzeba ani definiować, ani deklarować. 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 wartość. 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 konwencjonalną 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 specjalnie, 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ładowych 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 modyfikatora 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 definicji 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, jakich 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 przechowywać bardzo duże jednostki danych, takie jak obrazy, ścieżki dźwiękowe lub filmy. Ale działania na tych obiektach znacznie odbiegają od klasycznych 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 stosować 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ługich tekstach.
Aby umożliwić dostęp do bardzo dużych jednostek danych, takich jak wideo, we współczesnych bazach danych wprowadza się specjalny typ danych, 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 przedstawiono 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, identyczność. 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 filmowe są bardzo do siebie podobne. Zatem można klatki podzielić na obszary i obszar z jednej klatki traktować jako wskaźnik do obszaru z poprzedniej 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 stanowi niezły kompromis między jakością obrazu, objętością pliku oraz mocą 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 zapisuje 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. Funkcje 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ż implementacja 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 rysunku 8.19 przedstawiono kilka funkcji, które można dołączyć za pomocą instrukcji 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 - argumentowy 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 wskazane, użycie nazwy konstruktora tej samej co nazwa klasy.
Deklaracja nowego konstruktora została zamieszczona w wierszu 1). Korzysta 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ą lokalną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 zinterpretować 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 przypisanie 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: zwraca 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ść domniemana i można w związku z tym zdefiniować go tak samo, jak zdefiniowano 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 zadeklarowana zmienna lokalna : z, w której przechowuje się kod pocztowy. W wierszu 13) jest wywołana funkcja znaj dźKod. Jest to funkcja zewnętrzna, która ma dwa argumenty: ulicę i miasto. Postać deklaracji tej funkcji zewnętrznej zostanie omówiona w p. 8.6.3.
Funkcja znaj dźKod zwraca wartość kodu pocztowego określonego adresu, 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 pocztowym 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 pewnym 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ładzie 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 zwraca 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, wielkoś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. Przypominamy, ż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. Formuła wyliczenia jakości jest następująca: szybkość procesora plus pięciokrotna wielkość RAM (w megabajtach) plus pięćdziesięciokrotna pojemność 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 konstruktora 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 odnoszą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, korzystają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 kompromisowe. 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ównież, ż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 podstawowego. Tak samo jak w pozostałych implementacjach SQL, tak i tu istnieje 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 koncepcja funkcji zewnętrznych abstrakcyjnych typów danych.
2. Rola relacji. W modelu danych SQL3 pojęcie relacji odgrywa glówną rolę. Typy wiersza w rzeczywistości są opisem relacji, a typy ADT służą temu, by można byto w sposób bardziej elastyczny definiować 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. Kolekcje 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ą hermetyzacji. 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 hermetyzacji 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ą kontrolę nad zasięgiem typu wiersza, czyli relacją, która zawiera wszystkie istniejące w bazie krotki tego typu, chociaż nie ma takiej koniecznoś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ę poprzez 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 utworzeniu nie da się zmienić żadnej jego części. W tym znaczeniu obiekty typu elementarnego nie są mutowalne. Jeśli jednak można przy zachowaniu 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 nastąpić wyłącznie w języku podstawowym, a nie za pośrednictwem OQL. Typy abstrakcyjne ADT w SQL3 nie są tak całkiem niemutowalne. 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żytkownik 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 przechowuje 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 sygnatury metod, tzn. typy parametrów wejściowych i wyjściowych. Same metody definiuje się w programie zewnętrznym i zapisuje w zorientowanym 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 typów są StrUCt, tworzący struktury, oraz operatory tworzące kolekcje: 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-wherestatements in OQL): W języku OQL jest dostępna instrukcja select-from-where, która przypomina takie samo wyrażenie z SQL. W klauzuli EROM deklaruje się zmienne, które przebiegają kolekcje zawierają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, sumy, 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 poszczególnych grupach są dostępne za pośrednictwem pola o nazwie partition.
1 Wyodrębnianie elementów kolekcji w języku OQL (extracting elements from OQL collections): Można uzyskać dostęp do pojedynczego elementu kolekcji, stosując operator ELEMENT, pod warunkiem że kolekcja 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 przebiegają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 definiowania 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 CREATE TYPE. Wartościami ADT są struktury z jedną lub wieloma składowymi oraz z metodami.
• Metody w typach ADT w języku SQL3: Dla typów ADT można definiować 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.