Bazy danych i inżynieria oprogramowania Wykład 11: Wprowadzenie do standardu ODMG, część 5: OQL Kazimierz Subieta Instytut Podstaw Informatyki PAN, Warszawa Polsko-Japońska Wyższa Szkoła Technik Komputerowych, Warszawa
Plan wykładu (część 5) Nazwane zapytania Binarne zapytania i operatory Kwantyfikatory Operatory zagregowane Zdanie Select From Where Grupowanie Uporządkowanie, operatory na tablicach i listach Operacje na zbiorach Reguły zakresu Podsumowanie OQL
Swobodne kombinowanie operatorów Podaj nazwę ulicy, gdzie mieszkają pracownicy posiadający najmniejszą średnią płacę w stosunku do innych ulic. define Pracownicy() as select (Pracownik) q from Osoby as q where “pracuje” in q.co_robi Zdefiniowanie ekstensji Pracownicy define UlicaŚrZarobek() as select ulica, średni_zar: avg( x.p.zarobek from partition as x) from Pracownicy() as p group by ulica: p.adres.ulica Zdefiniowanie wielozbioru par (ulica, średni_zar) Trochę niejasne group_by, KS. Posortowanie, czyli zdefiniowanie listy par (ulica, średni_zar) define UlicaŚrZarobekPosortowany() as select z from UlicaŚrZarobek() as z order by z.średni_zar Pobranie pierwszego elementu tej listy, odnawigowanie do jego składowej first( UlicaŚrZarobekPosortowany() ).ulica
Całość zapytania w jednym kawałku first( select z from ( select ulica, średni_zar: avg( x.p.zarobek from partition as x) from ( select (Pracownik) q from Osoby as q where “pracuje” in q.co_robi ) as p group by ulica: p.adres.ulica) as z order by z.średni_zar). ulica Zapytanie jest skomplikowane i niełatwo jest ustalić jego znaczenie. Powstaje problem jego dekompozycji, która może być skutecznie załatwiona wyłącznie poprzez pojęcia perspektywy, metody funkcyjnej i obliczeń pośrednich. Niestety, ODMG nie definiuje tych pojęć. Nie jest jasne, jak ODMG rozumie podane na poprzednim slajdzie definicje: czy to są makrodefinicje, czy to są funkcje, czy to jest jeszcze coś innego.
Nazwane zapytania define id(x1, x2, ..., xn) as e(x1, x2, ..., xn) delete definition id define wiek(x) as select osob.wiek from Osoba as osob where osob.nazwisko = x delete definition wiek define Kowalscy as select x from Osoba as x where x.nazwisko = “Kowalski” select k.imię, wiek( k ) from Kowalscy as k Definicja obowiązuje i jest przechowywana jako trwała dana od momentu kiedy użytkownik ją wprowadził. Definicja realizuje własność określaną zwykle jako perspektywa (view). Czy definicja jest makro-definicją, czy też definicją procedury funkcyjnej? Czy dopuszczalna jest rekursja? Jaka jest metoda przekazywania parametrów? Czy parametr może być dowolnym zapytaniem, czy też jest prostą wartością? W jakim środowisku jest przechowywana ta definicja? Wirtualne atrybuty? Czy jest dostępna dla wszystkich użytkowników? Problem zaśmiecenia! Dlaczego definicja nie podlega obowiązkowej specyfikacji typów? Czy definicja ma samodzielne znaczenie pojęciowe? Aktualizacja perspektyw? Wątpliwości:
Binarne porównania i operatory Operatory arytmetyczne na liczbach integer +, -, *, /, mod Operatory arytmetyczne na liczbach float +. -, *, / Operatory porównania =, !=, <, <=, >, >= Operatory boolowskie and, or Operatory porównania znaków i stringów || (+), c in s, s[i], s[low;up], like Porównanie obiektów =, != count(Osoby) - count( Studenci) ‘przyjemny ciąg znaków’ like ‘%jemny%znak?w’ Wyrażenie zwraca true. ‘przyjemny ciąg znaków’[5;15] Zwraca string ‘emny ciąg z’ Jan = element( select s from Studenci where x.imię = “Jan” )
Selekcja atrybutu, przechodzenie wzdłuż związku e - wyrażenie zwracające obiekt p - nazwa atrybutu, związku, lub operacji na obiekcie e.p e->p Rudzki jest obiektem typu Profesor, który posiada metodę liczba_studentów: Rudzki -> liczba_studentów Maciura jest obiektem typu Student, który posiada metodę uczestniczy_w kursie posiadającej dwa parametry: nazwa kursu i obiekt typu Profesor; metoda zwraca obiekt typu Kurs, który ma atrybut nazwa. Maciura -> uczestniczy_w kursie( “matematyka”, Rudzki ) -> nazwa
Kwantyfikatory Uniwersalny for all x in e1 : e2(x) for all x in Students: x.id_studenta > 0 Zapytanie zwraca true, jeżeli dla wszystkich studentów identyfikatory są dodatnie. Egzystencjalny exists x in e1 : e2(x) Czy Maciura uczęszcza na jakikolwiek kurs prowadzony przez Rudzkiego? Kurs Student exists x in Maciura . uczęszcza : x . jest_prowadzony . nazwisko = “Rudzki” Profesor
Operatory (funkcje) zagregowane Identyczne jak w SQL: min, max, count, sum, avg <operator> ( <zapytanie> ) max( select zarobek from Profesorowie ) count( Osoby ) Minimalna średnia ocena studentów (ocena jest atrybutem studenta typu bag<integer>) min( select avg(select y from x.ocena as y ) from Studenci as x )
Zdanie Select From Where select [distinct] f( x1, x2, ..., xn, xn+1, xn+2, ..., xn+p ) from e1(xn+1, xn+2, ..., xn+p ) as x1, e2(x1, xn+1, xn+2, ..., xn+p ) as x2 , e3(x1, x2, xn+1, xn+2, ..., xn+p ) as x3 , ... en(x1, x2, xn-1, xn+1, xn+2, ..., xn+p ) as xn [where p( x1, x2, ..., xn, xn+1, xn+2, ..., xn+p )] [order by f1(x1, x2, ..., xn+p ), f2(x1, x2, ..., xn+p ), ... , fq(x1, x2, ..., xn+p ) ] xn+1, xn+2, ..., xn+p są wolnymi zmiennymi x1, x2, ..., xn są zmiennymi zdefiniowanymi w tym zdaniu (który są wolne w wyrażeniach f, p, fi i ei ) as można opuścić. Alternatywnie, można użyć x in e(...) Składnia jest podobna do SQL, lecz semantyka jest zasadniczo różna. Po słowie from znajduje się nie iloczyn kartezjański, lecz wyrażenie określane jako zależne złączenie (dependent join). Zależne złączenie oznacza, że każdy następny fragment korzysta z definicji zmiennych ze wszystkich poprzednich fragmentów. Duży syntaktyczny zlepek, sprzeczny z zasadą ortogonalności Nieprecyzyjna definicja zależnego złączenia (zakresy nazw) Ogólna nieporadność, tępota stylu definicji Podana definicja budzi kontrowersje:
Przykład Select From Where Osoba nazwisko x z Student Studenci Wykładowca stopień_naukowy y uczęszcza Kurs jest_wykładany_przez select para( student: x.nazwisko, profesor: z.nazwisko ) from Studenci as x, x.uczęszcza_na as y, y.jest_wykładany_przez as z where z . stopień_naukowy = “profesor” Zależne złączenie znacznie upraszcza zapytania (w porównaniu do SQL). Niestety, ODMG dopuszcza stosowanie tego operatora wyłącznie w kontekście zdania select, co wprowadza nieortogonalność i redukuje możliwości wyszukiwawcze operatora.
Operator grupowania Kalka z SQL o bardzo kontrowersyjnej, mętnej składni i semantyce. <zapytanie select> group by <atrybuty dzielące> [ having <predykat> ] Semantyka: Dla każdego elementu z wyniku <zapytania select> ustalana jest wartość wyrażenia <atrybuty dzielace>. Następnie wynik jest dzielony na grupy zgodnie z identyczną wartością zwracaną przez <atrybuty dzielace>. <predykat> może odrzucić niektóre z tych grup. Wyrażenie po select jest obliczane dla każdej z grup. select departament, śr_zarobków: avg( select x.p.zarobek from partition as x ) from Pracownicy as p group by departament: p.nr_depart having avg( select x.p.zarobek from partition as x) > 3000 Wyjaśnienie semantyki tego operatora jest mętne i niespójne (nie da się zrozumieć). Obecność frazy group_by zmienia semantykę poprzedzającej ją frazy select, co jest nie do przyjęcia Reguły migracji wolnych zmiennych do i od wyrażenia < atrybuty dzielące> przeczą zasadzie modularności semantyki. Obecność specjalnego słowa kluczowego partition spowoduje trudności z zagnieżdżaniem zapytań
Uporządkowanie, operatory na tablicach i listach Rezultat zapytania można uporzadkować, tj, zmienić jego typ z set lub bag na list. <zapytanie_select> order by e1, e2, ..., en select os from Osoby as os order by os.wiek, os.nazwisko select * from Osoby as os order by os.wiek desc, os.nazwisko asc, nr_działu <lista>[i] Zwraca i+1 -szy element listy, np. list(a, b, c, d )[1] zwraca b. <lista>[i : j] Zwraca podlistę, np. list(a, b, c, d )[1:2] zwraca list(b,c). Podaj trzeci poprzednik kursu projektowania o numerze 101: element( select x from Kursy as x where x.nazwa = “projektowanie” and x.numer = “101” ) . poprzedniki[2] Naiwny stosunek do problemu sortowania, nie uwzględniający narodowych konwencji. Listy są numerowane od zera: atawizm z arytmetyki wskaźników języka C.
Operacje na zbiorach Suma, przecięcie, różnica, porównanie zbiorów o dość standardowym znaczeniu. Suma, przecięcie, różnica, porównanie wielozbiorów o dość standardowym znaczeniu. (Możliwe jest znacznie więcej operatorów.) Studenci z wyjątkiem tych, którzy są jednocześnie asystentami Student except Asystent bag( 2, 2, 3, 3, 3 ) union bag( 2, 3, 3 ) Zwraca bag( 2, 2, 2, 3, 3, 3, 3, 3 ) set( 1, 2, 3 ) < set( 3, 4, 2, 1 ) Zwraca true set( 1, 2, 3 ) < set( 3, 2, 1 ) Zwraca false Przy dowolnych operacjach na zbiorach lub wielozbiorach konieczne jest określenie identyczności elementów. Taka identyczność nie zawsze jest jednoznaczna dla obiektów. ODMG ignoruje ten problem. set( 1, 2, 3 ) <= set( 3, 2, 1 ) Zwraca true distinct( list( 1, 4, 2, 3, 2, 4, 1, 3 ) ) Zwraca set( 1, 2, 3, 4 )
Reguły zakresu Ustalają, w jakim zakresie obowiązuje pewna powołana zmienna (kiedy jej definicja przestaje obowiązywać) i jaki jest priorytet wiązania do zmiennych jeżeli ich nazwy są takie same. Jest to kluczowy problem, szczególnie w sytuacji, kiedy pewien użytkownik może skorzystać z zapytania (lub definicji) skonstruowanego przez innego użytkownika, włączając to zapytanie jako składową większego zapytania. Dlaczego jest to problem? Może się zdarzyć, że po zagnieżdżeniu w większe zapytanie, zagnieżdżane zapytanie zmieni swoją semantykę. Może się także zdarzyć, że zagnieżdżone zapytanie zmieni semantykę kontekstu (większego zapytania). Reguły zakresu dla języków programowania są oparte o regułę stosu, który dyscyplinuje kontekst, w którym obowiązują definicje/deklaracje pewnych nazw. Ta reguła musi także obowiązywać dla języków zapytań. Propozycja ODMG w kilku miejscach łamie regułę stosu, np. definicje wewnątrz frazy from obowiązują wewnątrz frazy group by, która wprowadza definicje obowiązujące globalnie. ODMG w ogóle nie wspomina o regule stosu; zamiast tego wprowadza mętne i niespójne dywagacje, które pachną totalną niekompetencją w zakresie tego tematu.
Podsumowanie OQL Umożliwia formułowanie zapytań dla złożonego modelu obiektowego Posiada składnię i semantykę przypominającą SQL (zaleta dla pewnych ludzi) Zakłada mocną kontrolę typu. Zalety: Ograniczenia modelu obiektowego (brak możliwości deklarowania wielu interfejsów do tego samego obiektu) Jest mało ortogonalny, bazuje na dużym zlepku syntaktycznym Posiada bardzo nieprecyzyjną semantykę, szczególnie w zakresie deklaracji pomocniczych nazw i reguł zakresu Jako skutek, ograniczenia w zakresie optymalizacji zapytań Nie jest zintegrowany z konstrukcjami aktualizacyjnymi i abstrakcjami programistycznymi (perspektywa, funkcja, procedura, metoda) Nie zajmuje się danymi nietrwałymi (nie spełnia warunku ortogonalnej trwałości) Ma wadliwie skonstruowane dziedziny semantyczne (brak referencji) Wykazuje naiwny, nieprecyzyjny stosunek do takich zagadnień jak wartości zerowe, unie, grupowanie, uporządkowanie, mocne typowanie, wielodziedziczenie, ekstensje, i innych. Wady: