Optymalizator jezyka SBQL

background image

POLSKO-JAPOŃSKA WYŻSZA SZKOŁA TECHNIK

KOMPUTEROWYCH

PRACA MAGISTERSKA

Nr ................

Optymalizator zapytań obiektowego języka SBQL

Student

Tomasz Łazarczyk

Nr albumu

1538

Promotor

prof. Dr hab. Kazimierz Subieta

Specjalność

In ynieria Oprogramowania i Baz Danych

ż

Katedra

Systemów Informacyjnych

Data zatwierdzenia tematu

YY-YY-YYYY

Data zakończenia pracy

XX-XX-XXXX

Podpis promotora pracy

Podpis kierownika katedry

....................................

.........................................

background image

Streszczenie

Autor pracy jako cel postawił sobie zaimplementowanie optymalizatora zapytań języka

SBQL. Wybór powyższego celu zbiegł się w czasie z zakończeniem prac nad wspomnianym
w streszczeniu prototypem języka. Istniała potrzeba jego wzbogacenia, tak aby w szybki
sposób można było odpytywać repozytorium Office Object Portal.

Prototyp ten nie posiadał wbudowanych metod optymalizacji. Istotnym więc było

wyposażenie go w wybrane rozwiązania optymalizacyjne, tak aby ewaluacja zapytań
odbywała się w akceptowalnym czasie wykonania.

Autor przystępując do realizacji przyjął następujące założenia dotyczące optymalizatora:

zaimplementować metodę opartą na przepisywaniu niezależnych pod-zapytań
(ang. rewriting independent subqueries)

zintegrować optymalizator z istniejącą strukturą prototypu SBQL'a

optymalizować wszelkie konstrukcje językowe (m. in. różne operatory)

Wybraną przez autora metodę optymalizacji jak i budowę samego optymalizatora

opracował Kazimierz Subieta i opisał w książce “Teoria i konstrukcja obiektowych języków
zapytań” [Subieta 2004].

W związku z wymogiem integracji z prototypem SBQL'a optymalizator został napisany

w języku Java.

Wynikiem pracy jest w pełni działająca optymalizacja zapytań języka SBQL. Zapytania,

zadawane do repozytorium Office Object Portal są teraz optymalizowane, co w przypadku
systemu inteligentnego zarządzania wiedzą nie pozostaje bez znaczenia.

Przejrzysta budowa oraz logiczne działanie optymalizatora zapytań jest dowodem na to,

że optymalizacja języka mającego mocne podstawy teoretyczne, wcale nie musi być zbiorem
reguł matematycznych.

background image

Spis treści

1. Wstęp .................................................................................................................................

2. Stan sztuki .........................................................................................................................

2.1 ICONS ..........................................................................................................................
2.2 OfficeObjects® Portal ..................................................................................................
2.3 Interpreter Języka Zapytań SBQL dla Office Object Portal .........................................
2.4 Co to jest optymalizacja zapytań ? ...............................................................................

2.4.1 SQL .......................................................................................................................
2.4.2 XQuery ..................................................................................................................
2.4.3 Optimization Methods in Object Query Languages ..............................................

2.5 Metody optymalizacji ...................................................................................................

2.5.1 Metoda niezależnych pod-zapytań – drugi sposób ...............................................
2.5.2 Metody niskopoziomowe ......................................................................................

2.5.2.1 Indeksowanie .................................................................................................
2.5.2.2 Bezpośrednia nawigacja ................................................................................

2.5.3 Metody optymalizacji wyrażeń ścieżkowych .......................................................

2.5.3.1 „Wyciąganie” wspólnych wyrażeń ścieżkowych .........................................
2.5.3.2 Relacje wsparcia dostępu ..............................................................................

2.5.4 Metody optymalizacji zapytań wywołujących perspektywy .................................

2.5.4.1 Usuwanie zbędnych pomocniczych nazw .....................................................
2.5.4.2 Zastąpywanie złączenia operatorem projekcji ...............................................

3. Język SBQL (Stack-Based Query Language) ................................................................

3.1 Składnia SBQL .............................................................................................................
3.2 Stosy .............................................................................................................................

3.2.1 Stos środowisk (ENVS) ........................................................................................

3.2.1.1 Wiązanie (ang. binding) ................................................................................
3.2.1.2 Tworzenie nowej sekcji na stosie środowisk .................................................
3.2.1.3 Funkcja nested ...............................................................................................

3.2.2 Stos rezultatów (QRES) ........................................................................................

3.3 Rezultaty zapytań .........................................................................................................
3.4 Ewaluacja zapytań ........................................................................................................

4. Optymalizacja zapytań języka SBQL .............................................................................

4.1 Metoda niezależnych pod-zapytań – pierwszy sposób .................................................

4.1.1 Omówienie ............................................................................................................
4.1.2 Przykład ................................................................................................................

4.2 Budowa optymalizatora ................................................................................................

4.2.1 Meta-baza ..............................................................................................................
4.2.2 Stosy statyczne ......................................................................................................
4.2.3 Drzewo syntaktyczne ............................................................................................

4.3 Analiza statyczna zapytania ..........................................................................................

4.3.1 Algorytm ...............................................................................................................

background image

4.3.2 Funkcja static_nested ............................................................................................
4.3.3 Funkcja static_eval ...............................................................................................
4.3.4 Przykład ................................................................................................................
4.3.5 Wyznaczanie niezależnych pod-zapytań ...............................................................

5. Implementacja ..................................................................................................................

5.1 Metabaza ......................................................................................................................
5.2 Drzewo .........................................................................................................................
5.3 Optymalizator ...............................................................................................................

5.3.1 Sygnatury ..............................................................................................................
5.3.2 Stosy ......................................................................................................................

5.4 Przykłady ......................................................................................................................

6. Podsumowanie ..................................................................................................................

7. Literatura ..........................................................................................................................

Dodatek A ...............................................................................................................................

background image

1. Wstęp

Niniejsza praca przedstawia implementację optymalizatora zapytań języka SBQL,

opartego na teorii stosów. Stanowi ona integralną część implementacji języka SBQL (co było
tematem pracy magisterskiej autorstwa Bogdana Boguckiego i Marcina Michalaka – patrz
rozdział 2.3) stworzonej dla Office Object Portal – Systemu Inteligentnego Zarządzania
Zawartością, tworzonego w ramach piątego ramowego projektu (ICONS).

“Wstęp” przedstawia ogólne założenia oraz cel pracy.

Rozdział “Stan sztuki” ukazuje ogólne spojrzenie na kwestię optymalizacji zapytań na

przykładzie kilku wybranych języków.

W rozdziale dotyczącym języka SBQL zostaje on dokładnie przedstawiony wraz z

zasadą jego działania.

Rozdział „Metody optymalizacyjne” dokonuje przeglądu metod optymalizacyjnych

polegających na przepisywaniu.

„Optymalizacja zapytań języka SBQL” stanowi centralny punkt pracy, który to opisuje

nie tylko budowę, działanie optymalizatora, ale również skupia się na metodzie niezależnych
pod-zapytań i statycznej analizie.

Przedostatni rozdział dotyczy rozwiązań implementacyjnych tj. przedstawia budowę

struktur danych (metabaza, stosy i optymalizator). Dodatkowo znajdują się tutaj zrzuty
ekranów oraz przykłady pokazujące efekty optymalizacji.

W ostatnim rozdziale znajduje się podsumowanie pracy. Autor opisuje tam trudności

jakie autor, wnioski wypływające z implementacji oraz możliwości dalszej rozbudowy
optymalizatora zapytań.

background image

2. Stan sztuki

Zadaniem poniższego rozdziału jest przedstawienie czytelnikowi pojęcia optymalizacji

na przykładzie części najbardziej popularnych rozwiązań dotyczących optymalizacji zapytań
ponieważ obszar związany z optymalizacją zapytań jest tak olbrzymi, że stworzenie pełnego
ich przeglądu jest niemożliwe.

Dodatkowo 2.4.3 autor krótko przedstawia pracę doktorską poświęconą tematowi

optymalizacji.

Na początku jednak zostanie krótko opisany projekt ICONS, system OfficeObjects®

Portal, praca magisterska p. Bogdana Boguckiego i Marcina Michalaka, jako że praca ta jest
ich składową.

2.1 ICONS

Projekt ICONS (Inteligent CONtent Management System) ma za zadanie połączenie

zaawansowanych rezultatów badawczych, technologii i standardów w dziedzinie reprezentacji
wiedzy i istniejących źródeł informacji w jednorodną i internetową architekturę.
Wypracowane metody będą stanowiły podstawy do budowy prototypu systemu zarządzania
wiedzą i zawartością multimedialną. Integracja i rozszerzenie rezultatów z dziedzin sztucznej
inteligencji oraz baz danych połączone z zaawansowanymi cechami nowo powstających
technologii informatycznych da w rezultacie nowatorską architekturę systemu ICONS.

Badania w zakresie reprezentacji wiedzy będą pokrywały takie paradygmaty jak logika

(disjunctive Datalog), sieci semantyczne (semantyczne modele UML oraz standard RDF) oraz
wiedzę proceduralną o procesach działalności opartą o grafy skierowane zgodnie z
zaleceniami koalicji (WfMC).

Prototyp systemu ma zarządzać multimedialnym repozytorium zawartości,

przechowywać złożone obiekty informacyjne i reprezentacje zewnętrznych informacji
znajdujących się w rozproszonych bazach danych.

ICONS ma zarządzać opartym na XML, multimedialnym repozytorium zawartości,

przechowywać złożone obiekty informacyjne i reprezentacje (proxy) zewnętrznych informacji
znajdujących się w heterogenicznych bazach, danych generowanych na wyjściach innych
systemów przetwarzających, stronach Web oraz odpowiednich ontologii dziedzinowych.
Portal Zarządzania Wiedzą o Projektach Funduszy Strukturalnych będzie dostępny poprzez
Internet i będzie najlepszą demonstracją wyników projektu ICONS.
[ICONS_1, ICONS_2]

2.2 OfficeObjects® Portal

OfficeObjects® Portal jest korporacyjnym portalem, dzięki któremu pracownicy,

management oraz partnerzy handlowi i klienci w kontrolowany sposób w pełni korzystają z
zasobów wiedzy firmy przez 24h na dobę. Wystarczy tylko dostęp do sieci Internet i
komputer, aby możliwym było korzystanie z firmowych “skarbów” we wszystkich oddziałach
rozproszonych w różnych częściach kraju, czy świata.

Z korporacyjnego punktu widzenia kluczowe znaczenia ma elastyczność w

dostosowaniu portalu do specyfiki firmy, jej charakteru (produkcja przemysłowa, handel,
usługi) oraz wielkości (wysoka skalowalność).

Poniżej wypunktowano główne cechy portalu:

pełne zintegrowanie wszystkich działających w firmie systemów informatycznych

background image

i powiązanie ze sobą rozdzielonych dotychczas zasobów danych za
pośrednictwem Internetu

pobieranie aktualnych danych z zewnętrznych źródeł, jak np. notowania giełdowe
czy informacje z agencji prasowych i ich integrowanie z zasobami wiedzy firmy

zarządzanie obiegiem dokumentów i wewnętrzną komunikacją w firmie (dzięki
pośrednictwu wewnętrznej sieci - intranetu)

porządkowanie i kategoryzacja danych wg ustalonych i precyzyjnych kryteriów

wymiana na bieżąco aktualizowanych informacji ze światem (z klientem,
partnerem handlowym, mediami)

nawiązywanie nowych kontaktów handlowych (z możliwością udzielenia
natychmiastowej odpowiedzi)

dokonywanie za pośrednictwem Internetu transakcji handlowych (zamówienia on
line
)

zapewnienie dobrego wizerunku firmy na zewnątrz oraz wśród pracowników
(działania z zakresu Public Relations)

dynamiczne wsparcie wszystkich działań promocyjnych i marketingowych firmy

2.3 Interpreter Języka Zapytań SBQL dla Office Object Portal

Implementacja języka SBQL była tematem pracy magisterskiej autorstwa p. Bogdana

Boguckiego i Marcina Michalaka. Zostaną tu opisane jej podstawowe założenia, zastosowane
narzędzia i techniki implementacyjne.

Tematem pracy było zaimplementowanie prototypu języka opartego o podejście stosowe

dla wspomnianego w poprzednim rozdziale repozytorium Office Object Portal. Celem
nadrzędnym było zaopatrzenie tego repozytorium (do tej pory nie posiadało ono sprawnego
języka zapytań) w język zapytań, który jest:

łatwo rozbudowywalny,

przystosowany do modelu obiektowego,

prosty w implementacji,

łatwy w użytkowaniu,

w pełni implementowalny

oraz ma mocne podstawy teoretyczne.

Następujące narzędzia zostały użyte w implementacji:

Java – język programowania użyty do napisania języka zapytań; ponieważ część
ooPortal została napisana właśnie w javie to oczywistym było wybranie javy jako
narzędzia; pozwoliło to na prostą integrację SBQL'a z ooPortal

JFlex – generator skanerów leksykalnych w javie; format specyfikacji leksykalnej został
oparty o specyfikację jaką przyjmowały generatory skanerów dla C/C++ (LEX orz
FLEX)

CUP – generator parserów LALR napisany w języku java; specyfikacja gramatyki jest
oparta na specyfikacji z generatorów YACC oraz Bison; parsery wygenerowane przez
CUP'a łatwo się integruje ze skanerami leksykalnymi wygenerowanymi przez program
JFlex

Autorzy tej pracy wykonali postawione przed sobą zadanie. Ponadto magistranci

stworzyli interfejs graficzny pozwalający zadawać zapytania z poziomu strony WWW.
Stworzony został także moduł pozwalający na użycie SBQL dla ooPortal przez Structural
Knowledge Graph Manager (SKGM - modułu pozwalającego na przeglądanie zawartości

background image

repozytorium w sposób graficzny, nawigując po grafie). Poprzez swoją pracę pokazali, że
język mający mocne podstawy teoretyczne doskonale się sprawdza w przypadku dużych
systemów.

Zadaniem na przyszłość jest wzbogacenie języka o równania stało-punktowe, kontrolę

typów, optymalizację poprzez przepisywanie oraz indeksy.

2.4 Co to jest optymalizacja zapytań ?

Język zapytań jest to narzędzie występujące w systemach zarządzania bazami danych

(SZB) służące do wyszukiwania informacji wg zadanego przez użytkownika zapytania.

Współczesne SZB posiadają wiele wspaniałych cech, które potrafią uprzyjemnić życie

użytkownikowi, jednakże najbardziej pożądaną cechą jest szybkość. Priorytetem jest, aby
ewaluacja zapytań dokonywała się jak najbardziej efektywnie, a co za tym idzie szybko.
Końcowego użytkownika SZB nie interesuje jak jest zbudowany (czarna skrzynka); chce, aby
jego zapytanie wykonało się w akceptowalnym czasie.

Na szybkość wykonywania zapytań mają wpływ oczywiście takie czynniki jak szybkość

dostępu do dysku, przepustowość łącza internetowego (dla baz rozproszonych).

Jednak to optymalizator jest fundamentalną składową dowolnego języka zapytań. Bez

niego bezpośrednia ewaluacja zapytań doprowadziłaby do niewyobrażalnie długich czasów
wykonania i dużej konsumpcji pamięci.

Oto prosty przykład zaczerpnięty z [Subieta 2002].

Proste zapytanie w SQL (podaj nazwiska dostawców dostarczających części o nazwie

”zawór”):

select Dostawca.nazwisko from Dostawca, Produkt, DP
where Dostawca.NrDost = DP.NrDost and DP.NrProd = Produkt.NrProd
and Produkt.nazwa = ”zawór

wymaga wykonania produktu kartezjańskiego relacji wymienionych w klauzuli from.
Przyjmując (dość rozsądnie) 10000 dostawców, 10000 produktów, 100000 krotek w relacji
DP (wiążącej dostawców z produktami) i średnio 100 bajtów w każdej krotce tych relacji,
produkt kartezjański miałby 10

13

elementów i zajmowałby 10

15

bajtów, czyli ponad 930000

GB. Jeżeli sprawdzenie warunku w klauzuli where dla pojedynczej krotki trwałoby jedną
tysięczną sekundy, to wyselekcjonowanie z produktu kartezjańskiego właściwych krotek
trwałoby 10

10

sekund, czyli 317 lat.

Wykonanie takiego zapytania bez pomocy optymalizatora i operowanie na tak dużej

ilości wyników pośrednich w pamięci jest praktycznie niemożliwe. Natomiast wykorzystując
metody optymalizacyjne wykonanie potrwałoby zaledwie kilka sekund.

Dzielą się one na następujące rodzaje:

metody oparte na przepisywaniu (ang. rewriting) – metody te polegają na
przekształceniu zapytania (lub jego części) w równoważne zapytanie o mniejszym
czasie wykonania

zastosowanie specjalnych struktur danych lub organizacji danych – mówiąc
szczegółowiej indeksów, tablic wskaźników, kodowania mieszającego

pomijanie podczas ewaluacji specyficznych fragmentów zapytań – mowa tutaj
o takich fragmentach zapytań, które nie mają wpływu na końcowy wynik

zapamiętywanie wyników poprzednio obliczonych zapytań – zapytania, które
są często wykonywane są „materializowane” (zapamiętywane) i przez to już
później nie wykonywane

background image

równoległa optymalizacja kilku zapytań – sytuacja taka ma miejsce, gdy serwer
obsługuje jednocześnie wiele zapytań od użytkowników; istnieje wtedy
możliwość znalezienia części wspólnej tych zapytań i jednorazowej ich ewaluacji

wybór plan ewaluacji zapytania – w przypadku kiedy istnieje wiele dróg
ewaluacji zapytania trzeba oszacować czas wykonania każdej z nich i wybrać
najszybszą

Pierwsza spośród wyżej wymienionych metod jest metodą najlepiej optymalizującą w

większości przypadków (poza sytuacjami skrajnymi). Nigdy lub prawie nigdy nie pogarsza
ona czasu odpowiedzi na zapytanie. Z tego powodu autor zdecydował się na implementację
tej metody.

Pozostałe metody są również efektywne, ale tylko w specyficznych sytuacjach.

2.4.1 SQL

SQL (Structured Query Language) jest językiem współczesnych systemów relacyjnych

baz danych. Został opracowany w latach 70-tych w firmie IBM. Od tamtej pory stał się tak
popularny, iż stał się standardem w komunikacji z serwerami relacyjnych baz danych, takimi
jak IBM-owskie DB2 i SQL/DS, a także MySQL, PostgreSQL, Interbase SQL, Oracle,
Microsoft SQL Server, Microsoft Access, Sybase Adaptive Server Enterprise, Sybase SQL
Anywhere, Computer Associates Ingres, Informix, mSQL, First SQL i inne. Można
powiedzieć, że korzystanie z relacyjnych baz danych, to korzystanie z SQLa.

W systemach relacyjnych optymalizacja kładzie głównie nacisk na operatory złączenia

(ang. join operators).

Zapytanie traktowane jest jako algebraiczne wyrażenie σ - П - X. Przykładowo

zapytanie:

SELECT S.sname
FROM Reserves R, Sailors S
WHERE R.sid = S.sid
AND R.bid = 100 AND S.rating > 5

wyrażone jest w algebrze relacji jako:

zaś w postaci drzewa jako:

Rys. 2.1 Zapytanie w algebrze relacji rozpisane w postaci drzewa

background image

Optymalizacja relacyjnej algebry sprowadza się do dwóch rzeczy:

policzenia alternatywnych planów ewaluacji zapytania; najczęściej optymalizator bierze
pod uwagę tylko podzbiór wszystkich planów ze względu na ich ogromną ilość

oszacowania kosztu wykonania każdego z tych planów i wybrania najtańszego planu

W większości relacyjnych systemów optymalizator zaimplementowany jest na wzór

optymalizatora w Systemie R firmy IBM. Jego główne cechy to:

zapytania są optymalizowane podczas kompilacji a później tylko powtarzane

statystyki bazy danych są używane do obliczenia kosztu ewaluacji zapytania

model kosztu bierze pod uwagę zarówno CPU jak i I/O

głównym celem optymalizacji są niezagnieżdżone zapytania

dla operatora projekcji powtarzające się karotki nie są usuwane

przy tworzeniu alternatywnych planów ewaluacji dla zapytań typu join query
uwzględniane są tylko lewe poddrzewa

2.4.2 XQuery

W ciągu ostatnich paru lat XML zdobył ogromną popularność jako uniwersalny format

wymiany danych. Wraz z upływem czasu gdy stawał się standardem dla e-biznesu, rosła
potrzeba jego efektywnego odpytywania oraz przechowywania.

Na tą potrzebę odpowiedziały takie języki zapytań jak Lorel, Quilt, XQL, XML-QL,

XPath oraz XQuery. Najpopularniejszymi językami są XQuery oraz jego podzbiór XPath.

Dotychczasowe zainteresowanie optymalizowaniem XQuery i XPath było niewielkie.

Spowodowane to było faktem, iż XML był głównie używany do przechowywania niewielkich
zbiorów informacji. Optymalizowanie takich dokumentów nie ma najmniejszego sensu.

Rozwój XML'a zdaje się kierować w stronę bazodanowych paradygmatów, a co za tym

idzie coraz więcej starań jest czynionych, aby zapewnić efektywną ewaluację zapytań języków
XPath/XQuery.

XQuery to typowo funkcyjny język umożliwiający zarówno transformowanie jak i

odpytywanie XML'a. Pozwala również na wyciąganie danych za pomocą wyrażeń Xpath,
łączenie ze sobą dokumentów oraz tworzenie całkowicie nowych.

Poniżej przedstawiono różne podejścia do tematu optymalizacji XQuery.

przetwarzanie równoległe - budowanie drzewa oraz parser działają równolegle z
enginem transformacji; gdy arkusz stylów chce odwołać się do nie istniejącego w
drzewie węzła engine transformacji czeka dopóki parser nie dostarczy takowego

optymalizacja wg DTD (definicja typu dokumentu) – na podstawie DTD
można wyeleminować takie zapytania (ścieżki) Xpath, które są niemożliwe do
przejścia, napewno będą puste lub redundantne

formalne semantyki – przekształcenie zapytań wg formalnej semantyki na język
XQuery Core (niższego poziomu, słabo czytelny), umożliwia wykorzystanie wielu
technik dostępnych dla zwykłych języków bazodanowych

przepisywanie (ang. rewriting)

„leniwe” konstruowanie drzewa – węzeł drzewa (oraz całe poddrzewo pod nim)
może być reprezentowany jako zamknięcie (ang. closure) zawierające wyrażenie
oraz kontekst dla wykonania tego wyrażenia

2.4.3 Optimization Methods in Object Query Languages

Nazwa niniejszego rozdziału to tytuł pracy doktorskiej J. Płodzienia poświęconej

background image

tematowi optymalizacji, a wykorzystaną również jako źródło wiedzy ([Płodzień 2000]) do
stworzenia niniejszej pracy.

Autor opisuje ogólne strategie optymalizacyjne jakie występują w językach zapytań,

zarówno relacyjnych jak i obiektowych.

Praca została zakończona implementacją składającą się z następujących modułów:

parser lekko zmodyfikowanego języka SBQL

parser Loqis DDL

silnik do statycznej analizy

metoda niezależnych podzapytań oparta na przepisywaniu

Jak widać powyższe założenia są bardzo podobne do założeń niniejszej pracy.

Zasadnicza różnica tyczy się wykorzystanej bazy danych, tj. Loqis (w porównaniu do
ICONS).

W obu przypadkach zaimplementowano tą samą metodę optymalizacji.

2.5 Metody optymalizacji

Rozdział ten poświęcony metodom optymalizacji zapytań opartych na przepisywaniu.

Omówione zostaną:

przepisywanie niezależnych pod-zapytań

przepisywanie za pomocą niskopoziomowych technik

przepisywanie wyrażeń ścieżkowych

przepisywanie zapytań wywołujących perspektywy

2.5.1 Metoda niezależnych pod-zapytań – drugi sposób

Metoda ta ma swoje dwie odmiany: „wyciąganie” niezależnych pod-zapytań przed

operator niealgebraiczny (ang. factoring out) oraz „wypychanie” selekcji przed operator
niealgebraiczny (ang. pushing out). Ten rozdział poświęcony został tylko drugiemu
sposobowi. Pierwszy zaś zostanie omówiony w rozdziale 4.1, ponieważ zaimplementowany
optymalizator oparty jest na sposobie jego działania.

Zanim autor opisze drugi sposób należy przedstawić zasadę dystrybucyjności

operatorów, która to bardzo usprawnia jego działanie. Niealgebraiczny operator Θ jest
dystrybucyjny, jeśli dla składniowo poprawnych zapytań q1, q2, q3 oraz dla dowolnego stanu
bazy danych i stosu środowiskowego zachodzi:

(q1 union q2) Θ q3

jest semantycznie równoważne

(q1 Θ q3) union (q2 Θ q3)

Innymi słowy niealgebraiczny operator Θ jeśli dla każdego zapytania

(q1 Θ q3)

jego rezultat może być obliczony jako złączenie wszystkich rezultatów

r Θ q2

, gdzie r jest rezultatem q1. Końcowy rezultat jest zawsze złączeniem rezultatów
cząstkowych.

Zachodzą następujące reguły:

(q1.q2).q3 jest równoważne q1.(q2.q3)

background image

(q1 join q2) join q3 jest równoważne q1 join (q2 join q3)

(q1 join q2).q3 jest równoważne q1.(q2.q3)

(q1 join q2) where q3 jest równoważne q1 join (q2 where q3)

Operatory where, projekcji oraz złączenia są dystrybucyjne. Dystrybucyjnymi nie są
kwantyfikatory, operatory sortowania oraz grupowania.

„Wypychanie” selekcji przed złączenie było znane już w relacyjnych systemach baz danych.
W niniejszym podejściu została uogólniona jako „wypychanie” selekcji przed operator
niealgebraiczny: jeżeli predykat selekcji jest niezależny od where, wtedy selekcja nie powinna
być „wyciągnięta”, lecz „wypchnięta” przed ten operator.
Poniższe zapytanie pomoże w zrozumieniu ogólnej idei tej metody:

Pracownik.(Zarobek where Nazwisko = „Kowalski”)

(1,1) 2 (2,2) 3 (3,2)

Proszę zwrócić uwagę na nazwę Nazwisko występującą w tym zapytaniu jako cześć predykatu

Nazwisko = „Kowalski”

Wiązana ona jest w sekcji nr 2, pomimo iż rozmiar stosu wynosi 3. Ponieważ sekcja nr 3
otwierana jest przez operator where, predykat jest niezależny od tego operatora (jest zależny
od operatora projekcji). Uwzględniając właściwość dystrybucyjności tej selekcji i biorąc pod
uwagę semantykę tego operatora daje się zauważyć, że możliwe jest „wypchnięcie” predykat
tu przed where:

(Pracownik where Nazwisko = „Kowalski”).(Zarobek)
(1,1)

2 (2,2)

2 (2,2)

[P.2.1]

W większości przypadków bardziej opłaca się „wypchnąć” predykat przed selekcję niż

„wyciągnąć” go używając pierwszej odmiany metody niezależnych pod-zapytań (patrz
rozdział 4.1). Dzieję się tak dlatego, że „wypychanie” redukuje rozmiar ewaluacji
cząstkowych zapytań, co powoduje obniżenie kosztu ewaluacji całego zapytania. Ponadto po
„wypchnięciu” predykatu inna metoda optymalizacyjna może zostać użyta, np. indeksowanie
(ang. indexing). Dla P.2.1 naturalnym miejscem do założenia indeksu byłoby podzapytanie
Pracownik where Nazwisko = „Kowalski” (więcej na temat użycia indeksów w rozdziale
2.5.2).

W P.2.1 cały predykat został „wypchnięty” przed selekcję. Możliwe jest jednak

„wypchnięcie” tylko części predykatu. Pokazuje to niniejszy przykład P.2.2. Zapytanie zwraca
20 letnich pracowników razem z działami sprzedaży (znajdującymi się w Warszawie), w
których to pracują.
Pracownik join (PracujeW.(Dział where Nazwa = „sprzedaż” and Wiek = 20)
(1,1) 2 (2,2) 3 (3,3) 4 (4,4) (4,2)

[P.2.2]

Predykat selekcji nie jest niezależnym pod-zapytaniem jako całość, ponieważ nazwa Nazwa
wiązana jest w sekcji otwieranej przez operator where. Nazwa Wiek znajdująca się w
podpredykacie

Wiek = 20

[P.2.3]

background image

jest niezależna od where; jest również niezależna od operatora projekcji (jest za to zależna od
join). Zgodnie z właściwością dystrybucyjności operatora where, projekcji i join oraz faktem,
że podpredykat P.2.3 jest połączony z resztą predykatu selekcji poprzez and, warunek (P.2.3)
może być „wypchnięty” i przeniesiony do lewego pod-zapytania operatora join, tj. pod-
zapytania Pracownik. I tak zapytanie P.2.2 może być przepisane do następującej postaci:

(Pracownik where Wiek = 20) join (PracujeW.(Dział where Nazwa = „sprzedaż”))

(1,1)

2 (2,2)

2 (2,2) 3 (3,3) 4 (4,4)

Podsumowując, w metodzie tej dwie rzeczy są istotne. Po pierwsze, można „wypychać”
niezależny predykat, tylko jeżeli wszystkie niealgebraiczne operatory, wobec których jest
niezależny i przed które chcemy go „wypchnąć” są dystrybucyjne. W przeciwnym wypadku
takie przekształcenie jest niepoprawne. Po drugie, niezależny podpredykat może zostać
„wypchnięty” tylko jeżeli jest połączony z resztą predykatu za pomocą operatora logicznego
and (P.2.2) , inaczej może zostać tylko „wyciągnięty”.

2.5.2 Metody niskopoziomowe

Metody niskopoziomowe dowiodły swej skuteczności i przydatności zarówno w

relacyjnych jak i obiektowych systemach baz danych dzięki temu, że są używane na poziomie
fizycznym.

Poniżej autor zaprezentuje dwóch przedstawicieli tych metod: indeksowanie (ang.

indexing) oraz bezpośrednią nawigację (ang. direct navigation).

2.5.2.1 Indeksowanie

Pojedynczy index założony na atrybucie Atr obiektu Obiekt można zdefiniować jako

procedurę o sygnaturze

index_Object_Atr(par)

[P.2.4]

którą można wywoływać jako zwykłe (pod)zapytanie. Parametr par jest zapytaniem
zwracającym pojedynczą atomową wartość, której typ jest kompatybilny z typem Atr.

Procedura P.2.4 działa w następujący sposób: odczytuje z właściwego indeksu

wszystkie identyfikatory obiektów Obiekt, dla których atrybut Atr równy jest rezultatowi
zwracanemu przez par, a następnie wkłada te identyfikator na stos wyników. Z powodu
zastosowania indeksów, ewaluacja takiej procedury jest znacząco szybsza (czasem o parę
rzędów wielkości) niż ewaluacja odpowiadającego (pod)zapytania

Obiekt where Atr = par

Użycie indeksów zawsze polepsza wydajność. Jeśli Obiekt posiada indeksy założone na

paru atrybutach (przynajmniej dwóch) powinno się użyć tego indeksu, który jest bardziej
selektywny. Do tego służy właśnie model kosztów.

Pojęcie selektywności definiowane jest poprzez czynniki redukcji. Czynnik redukcji

danego predykatu selekcji jest to szacowana procentowa ilość wyników zwróconych ze stosu
rezultatów dla podpredykatu, dla którego predykat jest zastosowany. Indeks, który jest
bardziej selektywny ma liczbowo mniejszy czynnik redukcji i tego indeksu używamy.

Procedura P.2.4 działa tylko dla operatora równości. Można jednak zdefiniować ją

również dla innych operatorów relacji. Przykładowo dla operatora > procedura miałaby postać

background image

index_Obiekt_Atr_większy_od(par)

Analogicznie do P.2.4 wybiera ona identyfikatory tych obiektów Obiekt, dla których wartość
Atr jest większa od wartości zwróconej przez par.

Poniżej zawarto przykłady ilustrujące użycie indeksów w metodzie przepisywania zapytań.

(1) Pracownik where Nazwisko = „Kowalski”

zostaje przepisane do postaci (indeks założony na atrybucie Nazwisko)

index_Pracownik_Nazwisko(„Kowalski”)

(2) (Pracownik where Nazwisko = „Kowalski”) join PracujeW.Dział.Nazwa

zostaje przepisane do postaci (indeks założony na atrybucie Nazwisko)

index_Pracownik_Nazwisko(„Kowalski”) join PracujeW.Dział.Nazwa

(3) (Pracownik where Zarobek > ((Pracownik where Nazwisko = „Kowalski”).Zarobek)).
Nazwisko

zostaje przepisane do postaci (indeks założony na atrybucie Zarobek)

index_Pracownik_Zarobek_większy_od((Pracownik where name = „Kowalski”).Zarobek).
Nazwisko

(4) Pracownik where (Nazwisko = „Kowalski” and Urodzony > '1980-01-01' and Zarobek >
2500)

zostaje przepisane do postaci (indeks założony na atrybucie Nazwisko)

index_Pracownik_Nazwisko(„Kowalski”) where (Urodzony > '1980-01-01' and Zarobek >
3000)

Warto tutaj pokazać jak wyglądałoby przepisane zapytanie, jeśli indeks byłby założony na
atrybucie Urodzony:

index_Pracownik_Urodzony_większy_od('1980-01-01')

where

(Nazwisko =

„Kowalski” and Zarobek > 2500)

Jeśli na obu atrybutach założone by były indeksy o wyborze zadecydowałby ich parametr
selektywności.

2.5.2.2 Bezpośrednia nawigacja

Kolejnym sposobem optymalizowania zapytań jest wykonywanie pewnych operacji

bezpośrednio na obiektach, nie modyfikując przy tym stosu środowiskowego. Ponownie jak w
przypadku indeksowania operacja te wykonują się na niskim poziomie.

Bezpośrednia nawigacja (ang. direct navigation) dotyczy sytuacji kiedy nawiguje się po

atrybucie n obiektów Obiekt, których identyfikatory zostały zwrócone przez podzapytanie q:

q.n

[P.2.5]

Jak wiadomo z zasad działania SBQL procedura eval wykonuje P.2.5 następująco: oblicza
podzapytanie q w celu uzyskania identyfikatorów obiektów Obiekt, tworzy sekcje na stosie
środowiskowym zawierające atrybuty tych obiektów i w końcu przeszukuje te sekcje w

background image

poszukiwaniu identyfikatorów atrybutu n. Koszt takiej bezpośredniej ewaluacji jest znaczący.

Metoda bezpośredniej nawigacji dla zapytań typu P.2.5 nie musi modyfikować stosu

środowiskowego ani pobierać obiektów po to żeby w rezultacie otrzymać identyfikatory n.
Założenie jest takie, że identyfikator atrybutu obiektu może zostać wyznaczony poprzez
identyfikator obiektu oraz offset atrybutu w tym obiekcie (wartości takich offsetów powinny
być trzymane w bazie). Wprowadźmy specjalną procedurę bezpośredniej nawigacji:

dnav(offset)

[P.2.6]

Działa ona następująco: dla każdego identyfikatora obiektu ze szczytu stosu rezultatów
pobiera identyfikator tego atrybutu, którego offset równy jest parametrowi offset,, usuwa
najwyższą sekcję ze stosu a wkłada tam identyfikator atrybutu.

Zapytanie P.2.5 można przepisać stosując procedurę P.2.6 w taki oto sposób:

q dnav(offset_n)

,gdzie offset_n jest offsetem atrybutu n w obiektach Obiekt. Stosując to co przed chwilą
zostało napisane zapytanie

Pracownik.Urodzony

zostanie przepisane do postaci

Pracownik dnav(offset_Urodzony)

zaś zapytanie

(Pracownik where Zarobek >= 2000).PracujeW.Dział.Lokacja

do
(((Pracownik where Zarobek >= 2000) dnav(offset_PracujeW).Dział dnav(offset_Lokacja)
, gdzie offsety offset_PracujeW, offset_Lokacja, offset_Urodzony są offsetami
odpowiadających atrybutów w obiektach Pracownik.

2.5.3 Metody optymalizacji wyrażeń ścieżkowych

Jedną z podstawowych właściwości modelu obiektowego są wyrażenia ścieżkowe, tj.

(pod)zapytań postaci

n

1

.n

2

.n

3

. ... n

k-1

.n

k

gdzie n

i

(i należy do {1, 2, ..., k}) jest zewnętrzną nazwą obiektu lub jego atrybutu.

Przykładowo zapytanie

Pracownik.PracujeW.Dział

nawiguje od od pracowników do działów, w których oni pracują.

W tym rozdziale zostaną opisane dwie metody tego typu, które można zastosować do

zapytań zawierających wyrażenia ścieżkowe.

2.5.3.1 „Wyciąganie” wspólnych wyrażeń ścieżkowych

Znajdowanie wspólnego podzapytania w zapytaniu, tak aby wykonać je tylko raz a

później użyć raz obliczony rezultat jest popularną techniką optymalizacji (zazwyczaj
niskopoziomową). W rozdziale tym zostanie opisane znajdowanie oraz „wyciąganie”
wspólnych wyrażeń ścieżkowych. Proszę spojrzeć na poniższy przykład:

Wykład where (ProwadzonyPrzez.Profesor.PracujeW.Katedra.Nazwa = „Fizyka”) and

(„Warszawa” in ProwadzonyPrzez.Profesor.PracujeW.Katedra.Lokacja)

[P.2.7]

Da się zauważyć dwa wyrażenia ścieżkowe w klauzuli where, tj:

background image

ProwadzonyPrzez.Profesor.PracujeW.Katedra.Nazwa

[P.2.8]

oraz

ProwadzonyPrzez.Profesor.PracujeW.Katedra.Lokacja

[P.2.9]

są liczone oddzielnie. Można jednak z nich wyodrębnić cześć wspólną:

ProwadzonyPrzez.Profesor.PracujeW.Katedra

[P.2.10]

Ponieważ P.2.10 jest obliczane zarówno dla P.2.8 i P.2.9 w dokładnie tym samym środowisku
(tworzonym przez operator where, and nie ma wpływa, ponieważ jest operatorem
algebraicznym, a takowe nie modyfikują stosu środowiskowego), rezultat końcowy jest takim
sam w obu przypadkach. Wystarczy więc raz obliczyć P.2.10, a następnie obliczyć resztę z
P.2.8 i P.2.9:

Wykład where ProwadzonyPrzez.Profesor.PracujeW.Katedra.((Nazwa = „Fizyka”) and

(„Warszawa” in Lokacja))

2.5.3.2 Relacje wsparcia dostępu

Relacja wsparcia dostępu (ASR) jest specjalną strukturą indeksową, która znacznie

zwiększa wydajność poruszania po wyrażeniach ścieżkowych.

Jeżeli dane zapytanie zawiera wyrażenie ścieżkowe

n

1

.n

2

.n

3

. ... n

k-1

.n

k

[P.2.11]

oraz gdy istnieje ASR modelująca nawigację od encji n

1

(obiekt, podobiekt itp.) do encji n

k

(obiekt, podobiekt itp.) to wtedy wyrażenie P.2.11 można zastąpić wywołanej procedury

ASR_n1_nk

[P.2.12]

P.2.12 wykonuje się tak: poprzez użycie odpowiedniej ASR otrzymuje identyfikatory encji n

k

nawigowanych od wszystkich encji n

1

istniejących w bazie danych i umieszcza je na stosie

rezultatów. Przykładowo:

(Katedra.Zatrudnia.Profesor.Prowadzi.Wykład where Punkty > 5).Temat

[P.2.13]

zostanie przepisane do postaci

(ASR_Katedra_Wykład where credits > 5).Temat

zakładając, że istnieje ASR nawigująca od obiektów Katedra do obiektów Wykład.

Możliwym jest również, aby zamiast P.2.12 użyć takiej oto modyfikacji

ASR_QRES_n1_nk

[P.2.14]

Rożnica pomiędzy P.2.12 a P.2.12 jest taka, że ta ostatnia nie otrzymuje identyfikatorów encji
n

k

nawigowanych od wszystkich encji n

1

istniejących w bazie danych, lecz nawigowanych

tylko od tych n

1

, których identyfikatory są na szczycie stosu rezultatów. Dodatkowo przed

włożeniem identyfikatorów n

k

na stos rezultatów P.2.14.

Sugeruje się, aby używać P.2.14 w roli unarnego sufiksowego operatora algebraicznego:

q ASR_QRES_n1_nk

gdzie q jest podzapytaniem wkładającym na stos rezultatów identyfikatory encji n

1

.

background image

Różnicę pomiędzy tymi dwoma procedurami najlepiej pokazuje poniższy przykład. Jeżeli
zapytanie P.2.13 byłoby postaci
(((Katedra where Nazwa = „fizyka”).Zatrudnia.Profesor.Prowadzi.Wykład)

where Punkty > 5).Temat

nie możliwym byłoby użycie ASR_Wykład_Katedra, ponieważ pierwsza selekcja wyklucza
katedry inne niż fizyka. Natomiast użycie procedury postaci P.2.14 jak najbardziej na to
pozwala:
(((Katedra where Nazwa = „fizyka”).ASR_QRES_Katedra_Wykład)

where Punkty > 5).Temat

2.5.4 Metody optymalizacji zapytań wywołujących perspektywy

Ważnym aspektem optymalizacji zapytań jest optymalizacja zapytań wywołujących

perspektywy. Perspektywy znacznie podnoszę poziom abstrakcji (oraz modularność)
tworzenia aplikacji, niestety kosztem słabej wydajności. Powodem jest bezpośrednia
ewaluacja takich zapytań oraz brak dostępu do ciała perspektywy przez metody
optymalizacyjne. W relacyjnych bazach danych poradzono sobie z tym problemem ten
sposób, że takie zapytania są optymalizowane specjalną metodą: techniką modyfikacji
zapytania
. Głównym pomysłem jest tutaj połączenie zapytania zawierającego wołanie
perspektywy z definicją tej perspektywy. Zapytanie końcowe nie ma żadnych odwołań to
wywołań perspektywy (zamiast tego, posiada bezpośredni dostęp do jego definicji) i może
zostać zoptymalizowane przez przepisanie.

Zalety tej techniki są dwojakie. Po pierwsze, uniknąć można narzutów wydajności

związanych z przetwarzaniem perspektywy. Po drugie, o wiele bardziej ważne, umożliwia
zaaplikowanie metod optymalizacyjnych.

2.5.4.1 Usuwanie zbędnych pomocniczych nazw

Nazwy pomocnicze są bardzo przydatnym narzędziem w tworzeniu zapytań. Służą

przede wszystkim nazywaniu różnych części zapytania, tak aby później można było się
odwołać precyzyjnie do tych części do których chcemy. Dostęp do tych nazwanych części
następuje poprzez projekcje do nazwy pomocniczej. Szczegółowa analiza wykonania takiej
operacji pokazuje (tj. nazwania podrezultatu pomocniczą nazwą oraz wykonania projekcji do
tej nazwy), że często jest ona wykonywana niepotrzebnie (możemy odwołać się do
podrezultatu bezpośrednio). Oznacza to, że nazwy pomocnicze mogą zostać
zoptymalizowane, w szczególności poprzez ich usunięcie. Zagadnienie nie jest takie proste na
jakie brzmi. Usunięcie niektórych nazw z zapytania może zmienić rezultat całego zapytania.

Poniższa perspektywa pobiera profesorów, którzy zarabiają więcej niż dwa tysiące (i

nadaje im pomocniczą nazwę p):

view Psorzy
begin

return ((Profesor where Zarobek > 2000) as p)

end;

Rezultatem tej perspektywy są bindery nazwane p a wartościami identyfikatory odpowiednich
obiektów Profesor. Jeżeli chcemy otrzymać te identyfikatory poprzez perspektywę, trzeba ją
wywołać i dokonać projekcji:

background image

Psorzy.p

[P.2.15]

Poprzez wykonanie tej projekcji pozbywamy się binderów zwróconych przez perspektywę, tj.
rezultatem zapytanie P.2.15 są identyfikatory a nie bindery. Proszę zauważyć , że usunięcie
pomocniczej nazwy w P.2.15 jest zabronione, ponieważ taka modyfikacja zmieniałby rezultat
zapytania (w szczególności otrzymalibyśmy bindery, nie zaś identyfikatory). Usuwając
pomocniczą nazwę p musielibyśmy również usunąć jej definicję z ciała perspektywy.
Jednakże w zapytaniu P.2.15 nie możemy tego zrobić, gdyż nie mam dostępu do wnętrza
perspektywy, gdzie nazwa pomocnicza jest zdefiniowana.

Aby mieć dostęp do definicji nazwy pomocniczej w ciele perspektywy trzeba dokonać

wywołania perspektywy poprzez makro. Po tym zapytanie P.2.15 będzie postaci:

((Profesor where Zarobek > 2000) as p). p
(1,1)

2 (2,2)

2 (2,2)

[P.2.16]

Jak widać nazwa p spełnia tu dwie funkcje:

nazywa rezultat zapytania (Profesor where Zarobek > 2000)

nawiguje do tego rezultatu

Rezultat P.2.16 nie ulegnie zmianie, jeśli zostanie usunięta pomocnicza nazwa (poprzez
usunięcie nazwy rozumie się usunięcie jej wszystkich wystąpień w zapytaniu):

Profesor where Zarobek > 2000

Kolejnym przypadkiem jest przypadek, gdy nazwa pomocnicza jest używana do

dalszych projekcji. Przykładowo, używając perspektywy Psorzy mamy dostęp nie tylko do
obiektów, które zwraca, ale również ich atrybutów:

Psorzy.(p.Nazwisko, p.Wiek)

Pomocnicza zmienna p musi zostać użyta w podzapytaniu

p.Nazwisko, p.Wiek

w celu umożliwienia nawigacji „ .Nazwisko ” i „ .Wiek „. Dlatego też usunięcie p jest
niemożliwe. Jednakże po ponownym wywołaniu perspektywy poprzez makro :

((Profesor where Zarobek > 2000) as p).(p.Nazwisko, p.Wiek)

[P.2.17]

można dostać się do atrybutów Profesora bezpośrednio, czyli nazwy pomocniczej można się
pozbyć:

((Profesor where Zarobek > 2000)).(Nazwisko, Wiek)

Podsumowując w przypadku ogólnym pomocnicza nazwa może mieć parę zastosować

w jednym zapytaniu:

((Pracownik as p) where p.Zarobek < 1000).p

pierwsze p pozwala nawigować do atrybutu Zarobek, podczas gdy drugie użyte jest w
końcowej projekcji. p można zredukować:

Pracownik where Zarobek < 1000

We wszystkich powyższych przykładach nazwy pomocnicze były zbędne. Dla odmiany

poniższe przykłady pokazują sytuacje kiedy nazwy pomocnicze nie mogą być usunięte.

Student.(Uczęszcza.Wykład.ProwadzonyPrzez.(Profesor as p).(Nazwisko, p.Nazwisko))

background image

(1,1) 2 (2,2) 3 (3,3) 3 (3,3) 3 (3,3) 3 (3,2) (3,3)4 (4,4)

[P.2.18]

Nazwisko jest atrybutem zarówno obiektu Student jak i Profesor. W podzapytaniu
zwracającym Nazwisko:

(Nazwisko, p.Nazwisko)

pierwsze nazwa Nazwisko wiązana jest w sekcji otwieranej dla rezultatu podzapytania
Student, podczas gdy druga wiązana jest w sekcji otwieranej dla rezultatu podzapytania
Profesor as p. Jeżeli nazwa p zostanie usunięta:

Student.(Uczęszcza.Wykład.ProwadzonyPrzez.(Profesor).(Nazwisko, Nazwisko))

[P.2.19]

obie nazwy Nazwisko byłyby wiązane w tej samej sekcji, tj. otwartej dla rezultatu
podzapytania Profesor. Stąd zapytanie P.2.19 pobiera dla każdego studenta zdublowanie
nazwisko swojego profesora, nie zaś nazwisko studenta i nazwisko profesora. W
konsekwencji zapytanie P.2.18 i P.2.19 nie są semantycznie równoważne,co oznacza że
usunięcie nazwy p jest niemożliwe.

Pseudokod algorytmu znajdowania pomocniczych nazw, które można usunąć wygląda tak:

najpierw tworzona jest kopia drzewo składniowego zapytaniach

następnie nazwa usuwana jest z zapytaniach

uruchamiana jest statyczna analiza na pierwotnym drzewie składniowym oraz
zmienionym w celu sprawdzenia czy wszystkie nazwy wiązane są dokładnie w tym
samych sekcjach. Jeżeli tak to oznacza to, że usunięcie pomocniczej nazwy nie zmieniło
rezultatu zapytania. Nowa drzewo staje się drzewem pierwotnym.

Usunięcie pomocniczej nazwy n możliwe jest tylko wtedy gdy:

rezultat wykonania n użyty jest przez operator projekcji

bezpośrednim nie-algebraiczny operatorem n jest operator projekcji i rezultat wykonania
n nie jest konsumowany przez żaden inny operator

W przeciwnym wypadku nazwa n nie może być usunięta.

2.5.4.2 Zastąpywanie złączenia operatorem projekcji

Poza metodami optymalizacyjnymi, których zadaniem jest zmniejszenie czasu

wykonania zapytania istnieją takie, które mają za zadanie zmniejszenie ilości pamięci
potrzebnej w pewnych fazach wykonania zapytania. Główną ideą jest: jeśli żaden binder z
listy podsekcji (która jest częścią sekcji otwieranej przez operator projekcji) nie został użyty,
wtedy operator projekcji nie musi tworzyć takiej listy podczas otwierania swojej sekcji.
Tymsamym potrzeba mniej pamięci na przechowywanie stosu. Dodatkowo mniej sekcji
zużywa mniej czasu, co oznacza że metoda ta skraca także czas wykonania zapytania.

Przykład poniżej ilustruje ogólny pomysł. Perspektywa PracDział zwraca pracowników razem
z działami, w których pracują:

view PracDział
begin

return Pracownik join PracujeW.Dział

background image

end;

Perspektywa taka może być użyta do zdefiniowania zapytania zwracającego nazwy działów, w
których pracują ich pracownicy:

PracDział.Nazwa

,które po wywołaniu perspektywy poprzez makro wyglądałoby tak:

(Pracownik join PracujeW.Dział).Nazwa

Operator ostatniej projekcji otwiera sekcję składającą się z dwóch list podsekcji:

pierwsza z nich tworzona jest na potrzebę rezultatu podzapytania

Pracownik

druga zaś na potrzebę rezultatu podzapytania

PracujeW.Dział

Ponieważ nazwa Nazwa odnosi się do Dział (nie ma zaś znaczenia w kontekście Pracownika)
końcowa projekcja odnosi się tylko do prawej strony operatora zależnego złączenia. Oznacza
to, że podczas wykonywania zapytania niepotrzebny jest dostęp do rezultatu lewego operandu
tego złączenia. Wynika z tego, że podczas wiązania Nazwa wystarczy, aby stos środowiskowy
zawierał podsekcje utworzone dla wyników prawego operandu. Aby tego dokonać wystarczy
podmienić operatora złączenia z operatorem projekcji:

(Pracownik.PracujeW.Dział).Nazwa

W momencie gdy podzapytanie Nazwa jest wykonywane, szczytowa sekcja stosu
środowiskowego (otwarta dla rezultatu lewego podzapytanie zewnętrznego operatora
projekcji) zawiera tylko jedną listę podsekcji; jest to lista stworzona dla obiektów Dział.

Poniższy przykład jest bardziej skomplikowany. Perspektywa InfoOStudentach zwraca

studentów razem z wykładami, na które chodzą oraz profesorów, którzy prowadzą te wykłady
i katedry, w których pracują:

view InfoOStudentach
begin
return
Student join Uczęszcza.Wykład join ProwadzonyPrzez.Profesor join

PracujeW.Katedra

end;

Aby otrzymać nazwa tych katedr, na których wykłady uczęszczają studenci trzeba zadać takie
zapytanie:

InfoOStudentach.Nazwa

Po wywołaniu perspektywy poprzez makro otrzyma się

(Student join Uczęszcza.Wykład join ProwadzonyPrzez.Profesor join

PracujeW.Katedra).Nazwa

, które jest równoważne

(((Student join Uczęszcza.Wykład) join ProwadzonyPrzez.Profesor) join

PracujeW.Katedra).Nazwa

Podzapytanie Nazwa korzysta jedynie z rezultatu podzapytania PracujeW.Katedra, co oznacza
że można przepisać całe zapytanie do postaci:

(((Student join Uczęszcza.Wykład) join ProwadzonyPrzez.Profesor).

PracujeW.Katedra).Nazwa

background image

Ponieważ podzapytanie

PracujeW

korzysta z rezultatu podzapytania

ProwadzonyPrzez.Profesor, transformacji można dokonać jeszcze raz:

(((Student join Uczęszcza.Wykład).ProwadzonyPrzez.Profesor).

PracujeW.Katedra).Nazwa

I tak dalej. Ostateczną formą zapytanie będzie:

Student.Uczęszcza.Wykład.ProwadzonyPrzez.Profesor.PracujeW.Katedra.Nazwa

background image

3. Język SBQL (Stack-Based Query Language)

Język SBQL to obiektowy język zapytań podobny do SQL lub OQL (posiada

odpowiedniki pewnych konstrukcji językowych). W swej postaci podobny jest do algebry
relacji stanowiącej podstawę modelu relacyjnego, ale dodatkowo znacznie bardziej
koncepcyjnie przejrzysty i konsekwentny.

SBQL oparty jest na abstrakcyjnej składni i zasadzie kompozycyjności, tzn. brakuje

tutaj „cukru syntaktycznego”, zaś operatory są od siebie składniowo oddzielone.

3.1 Składnia SBQL

Składnię SBQL'a można opisać trzema poniższymi stwierdzeniami:

pojedyncza nazwa lub pojedynczy literał mogą już stanowić zapytanie, np.:
Pracownik, Nazwisko, Zarobek, 2500, „Tomek”

jeżeli q jest zapytaniem, zaś σ jest operatorem unarnym (np.: sum, count) to σ(q)
również jest zapytaniem

jeżeli q1, q2 to zapytania , zaś Θ jest operatorem binarnym (np.: where, union, +,
-, =, >, <, *, /) to q1 Θ q2 również jest zapytaniem

jeżeli q jest zapytaniem, zaś n

N to q group as n jest również zapytaniem; w

tym kontekście as to operator definiowania pomocniczej nazwy

jeżeli q jest zapytaniem, zaś n

N to q as n jest również zapytaniem; as to

unarny operator algebraiczny parametryzowany nazwą n

jeżeli q jest zapytaniem to (q) jest również zapytaniem; ta reguła definiuje
używanie nawiasów w sposób dowolny

Jeżeli n

N jest nazwą procedury, funkcji, lub metody posiadającej k

parametrów, zaś q

1

, q

2

, ... , q

k

są zapytaniami, wówczas n(q

1

, q

2

, ... , q

k

) jest

zapytaniem

Jeżeli n

N jest nazwą procedury, funkcji, lub metody nie posiadającej

parametrów, wówczas n() oraz n są zapytaniami. W większości przypadków
będziemy przyjmować, że takie zapytania są równoważne

Poniżej przedstawione są zapytania zgodne z tą składnią:

2500
Zarobek
Pracownik
1800 + 700
Zarobek > 1800
Pracownik where (Zarobek > 2500)
(Pracownik where (staż() > 3)).(Zarobek * Premia)

W języku SBQL ortogonalność operatorów jest zapewniona. Np.: Pracownik, Zarobek,

„Tomek” są pełnoprawnymi pojedynczymi zapytaniami i mogę być użyte do zbudowanie
bardziej skomplikowanych zapytań:

background image

(Pracownik where (Imie = „Tomek”)).Zarobek

Dla porównania to samo zapytanie w SQL wygląda tak:

select Zarobek from Pracownik where Imie = „Tomek”

zaś w OQL tak:

select p.Zarobek from Pracownik as p where p.Imie = „Tomek”

3.2 Stosy

3.2.1 Stos środowisk (ENVS)

Pojęcie stosu środowisk ujrzało światło dzienne jeszcze w latach 60-tych wraz z

językami Algolo-podobnymi. Od tamtej pory stos stanowi podstawowy element konstrukcji
większości znanych języków, takich jak Java, C/C++, Pascal.

ENVS (ENViromental Stack) jest strukturą danych istniejącą w pamięci, podzieloną na

sekcje. Ich kolejność jest istotna. Nowa sekcja tworzona jest zawsze na wierzchołku stosu,
poprzez wykonanie operatora nie-algebraicznego. Tylko operatory nie-algebraiczne
modyfikują ENVS.
Stos w języku SBQL zdefiniowany jest w ramach następujących założeń:

z punktu widzenia programisty maksymalny rozmiar stosu nie jest ograniczony ze
strony koncepcyjnej;

stos jest podzielony na sekcje, przy czym każda sekcja zawiera informacje o środowisku
czasu wykonania; rozmiar sekcji nie jest ograniczony koncepcyjnie

sekcje utworzone przez najbardziej lokalne środowiska umieszczone są na szczycie
stosu, zaś sekcja powstałe wcześniej odpowiednio niżej

na samym dole stosu istnieje sekcja globalna, która przechowuje identyfikatory
(bindery) obiektów znajdujących się w bazie danych; dzięki temu stos przechowuje
informację niezbędną do wiązania dowolnej nazwy jaka może wystąpić w zapytaniu

stos w jednakowy sposób traktuje dane ulotne (czasu wykonania, np.: dane
wywoływanych funkcji, metod i procedur) jak i trwałe przechowywane w bazie danych

Jak już wcześniej zostało wspomniane ENVS składa się z sekcji, te z kolei składają się

binderów. Formalnie rzecz opisując binder jest parą (n, x), gdzie n jest zewnętrzną nazwą, zaś
x jest referencją do obiektu. Binder zapisujemy w postaci: n(x). Podstawowym zadaniem
bindera jest zamiana nazwy wystepującej w zapytaniu (n) na wartość x, będącą bytem czasu
wykonania. Jeżeli dana nazwa nie ma swojego bindera, nie może zostać związana (jest po
prostu błędna).

Wartościami binderów może być wszystko co jest rezultatem (patrz rozdział 3.2.3).

Mówiąc ogólnie binder jest parą n(x), gdzie n dowolną nazwą zewnętrzną definiowaną przez
programistę, projektanta bazy danych itp., a x może być dowolnym rezultatem zwracanym
przez zapytanie.

3.2.1.1 Wiązanie (ang. binding)

Wiązanie to mechanizm, który pozwala na odnalezienie danego bindera na stosie

background image

ENVS. Odbywa się ono zgodnie z regułą stosu (czyli szukaj od góry). W celu związania
nazwy n mechanizm ten przeszukuje ENVS od góry w poszukiwaniu sekcji zawierającej
binder n(x) (który to przechowuje wartość x). Wynikiem wiązania jest właśnie x.

Czasem rezultat wiązania może być wielowartościowy. Jeżeli dana sekcja zawiera kilka

binderów o nazwie n: n(x1), n(x2), n(x3) , wtedy wszystkie one są wynikiem tego wiązania w
postaci kolekcji {x1, x2, x3}.

Zasady przeszukiwania stosu oraz wyznaczania rezultatu wiązania nazwy są

następujące:

Dla wiązania nazwy n, ENVS jest przeszukiwany aż do znalezienia sekcji, w której
znajduje się binder oznaczony nazwa n. Gdy taka sekcja zostanie znaleziona
przeszukiwanie zostaje zakończone.

Wszystkie bindery z tej sekcji oznaczone nazwą n wchodzą do rezultatu wiązania.

Rezultat wiązania uzyskuje się poprzez odrzucenie ze znalezionych binderów nazwy n i
pozostawienie wyłącznie wartości tych binderów.

Poniżej przedstawiona jest kolejność przeszukiwania ENVS podczas wiązania nazwy:


DDziaflweflwelflwflwofwemfwpemfwef

Rys. 3.1 Kolejność przeszukiwania stosu ENVS dla wiązania nazwy

Oto rezultaty wiązania dla stosu z rys. 3.1:

dla nazwy Pracownik rezultatem wiązania jest identyfikator i1

dla nazwy PI rezultatem jest wartość liczbowa 3.14

dla nazwy Autor rezultatem jest wartość stringowa „Tomek”

dla nazwy Wydział rezultatem wiązania jest zbiór identyfikatorów {i4, i5}

W przypadku nazwy Pracownik można zauważyć na rys. 3.1, iż bindery o takiej nazwie

występują w dwóch sekcjach. Sytuacje takie rozwiązują się same dzięki wspomnianej już
regule przeszukiwania stosu, która nakazuje przerwać przeszukiwanie na sekcji zawierającej
bindery z nazwą Pracownik, która znajduje się najbliżej wierzchołka stosu. Można wtedy
powiedzieć, że ten binder znajduje się w bardziej lokalnym środowisku i przesłania inne
bindery o tej samej nazwie, które to znajdują się w bardziej globalnym środowisku.

Pracownik (i1)

PI(3.14) Autor(“Tomek”)

...

Imie(u54) Nazwisko(i55)

Zarobek(i57) Dataurodzin(i58)

Pracownik(i1) Pracownik(i2) Pracownik(i3)

Wydział(i4) Wydział(i5)

...

...

Kierunek przeszukiwania stosu

background image

3.2.1.2 Tworzenie nowej sekcji na stosie środowisk

Tworzeniem nowej sekcji (inaczej otwieraniem nowego zakresu) na ENVS zajmują się

operatory nie-algebraiczne. Operatory te podobnie działają na stosie podobnie do wywołań
bloków programów. Przykładowo w zapytaniu:

Pracownik where (Imie = „Łazarczyk” and Zarobek > 2500)

część (Imie = „Łazarczyk” and Zarobek > 2500) ewaluowana jest w nowym środowisku
stanowiącym „wnętrze” obiektu Pracownik (atrybuty, metody itp.) aktualnie analizowanego
poprzez where. Na wierzchołku ENVS powstaje nowa sekcja, która zawiera bindery do
elementów „wnętrza” obiektu Pracownik i w takich warunkach obliczana jest część zapytania
występująca po where. Tuż po tym omawiana sekcja zostaje usunięta ze stosu, zaś jej miejsce
zajmuje następna sekcja zawierająca „wnętrze” kolejnego obiektu Pracownik.

3.2.1.3 Funkcja nested

Funkcja nested zwraca wewnętrzne środowisko obiektu (w szczególności zbiór

binderów) dla podanego argumentu jakim jest identyfikator (referencja) do obiektu. To
zwracane środowisko stanowi zawartość sekcji, która ma być umieszczona na ENVS.

Jeżeli zaś chodzi o argument funkcji nested to został on tak uogólniony, że może nim

być dowolny rezultat zapytania.

Oto założenia definicyjne dla funkcji nested:

dla wartości atomowych wynik funkcji jest zbiorem pustym

dla obiektu złożonego <i, n, {<i

1

, n

1

, v

1

>, <i

2

, n

2

, v

2

>, ..., <i

k

, n

k

, v

k

>}> wynikiem

jest nested(i) = {n

1

(i

1

), n

2

(i

2

), ... , n

k

(i

k

)}

dla każdego bindera n(x) zachodzi nested(n(x)) = {n(x)}

jeżeli i jest identyfikatorem obiektu pointerowego <i, n, i

1

> oraz istnieje obiekt <

i

1

, n

1

, v

1

>, wówczas nested(i) = {n

1

(i

1

)}

jeżeli argumentem funkcji nested jest struktura, wówczas wynik jest sumą teorio-
mnogościową rezultatów funkcji nested dla pojedynczych elementów struktury:
nested(struct{ x

1

, x

2

, x

3

, ...}) = nested(x ) X nested(x

2

) X nested( x

3

) X ...

3.2.2 Stos rezultatów (QRES)

Końcowe wyniki zapytań oraz pośrednie ich rezultaty odkładane są na stosie rezultatów

(QRES – Query REsult Stack). Najkrócej opisując stos ten jest strukturą danych podobną do
stosu arytmetycznego, który można znaleźć w implementacji języków programowania.
Rysunek 3.2 pokazuje działanie stosu na krótkiego wyrażenia arytmetyczne go:

(10 + 10 ) * 2

Rys. 3.2 Kolejno odkładane wyniki na QRES'ie dla zapytania (10+10) * 2

10

10

10

20

+

20

2

40

*

background image

Dozwolonymi operacjami na QRES są:

push (włóż nowy element na wierzchołek stosu)

pop (zdejmij element na wierzchołek stosu)

top (odczytaj wierzchołek stosu)

empty (sprawdź czy stos jest pusty)

3.3 Rezultaty zapytań

Na wstępie autor chciałby podkreślić, że w podejściu stosowym zapytania zwracają

identyfikatory obiektów, a nie je same. Naturalnie nadal możliwe jest otrzymanie jako rezultat
liczby, łańcucha tekstowego, struktury – wystarczy zastosować operator dereferencji.

Rezultat zapytania może być:

atomowa wartość

referencja do obiektu (inaczej identyfikator obiektu)

binder

strukturę struct{x

1

, x

2

, x

3

, ...} struct jest konstruktorem struktury, czyli pewnym dodatkowym

atrybutem (flagą) rezultatu

kolekcje wielo zbiorów (bag) bag{x1, x2, x3, ...} lub sekwencji (sequence) sequence
{x1, x2, x3, ...}, bag oraz sequence są konstruktorami kolekcji (flagami) podobnie jak
konstruktor struct

Przykłady rezultatów:

24, true, “Tomek”, i3, i23

struct {i3, i23}

sequence {i3, i23, i100}

bag {i3, i23, i100}

bag {struct {i1, i23}, struct {i2, i24}, struct {i3, i25}}

bag {i3, struct {Imie(“Tomek”), Zarobek(2500}}}

bag {struct {1500, 2000}, bag {struct{i1, i23}, struct{i3, i25}}, struct {bag {i2,
i24, i30}}}

3.4 Ewaluacja zapytań

Specjalna rekurencyjna funkcja eval jest używana do zdefiniowania semantyki

operacyjnej SBQL'a. Funkcja ta jako argument przyjmuje zapytanie, zaś zwraca jako wynik
rezultat tego zapytania umieszczony na wierzchołku QRES. Eval spełnia poniższe założenia:

eval w trakcie wykonywania zapytania q może zmieniać stan ENVS, ale po zakończeniu
wykonywania q stan ten będzie taki sam, jak na początku

eval w trakcie wykonywania zapytania q nigdy nie zmienia tej części stosu rezultatów,
którą zastała w momencie rozpoczęcia wykonywania; może wkładać na ten stos i
zdejmować wiele różnych elementów, ale po zakończeniu ewaluacji stos ten będzie

background image

posiadać dokładnie jeden element więcej niż przed ewaluacją (rezultat zapytania q)

Funkcja eval jest wspomagana przez parser, który to dokonuje rozbioru gramatycznego

zapytania na podzapytania oraz operatory łączące podzapytania. Następnie każde
podzapytanie zwraca swój rezultat, zaś rezultat całego zapytania jest obliczony na podstawie
rezultatów zwróconych przez podzapytania. Własność taka jest nazywana kompozycyjnością i
jest jedna z wielu bardzo mocnych cech SBQL. Inne języki zapytań nie posiadają tej cechy co
prowadzi do wielu trudności implementacyjnych oraz jest powodem bardzo dużych
podręczników użytkownika oraz dokumentacji technicznych.

Funkcja eval zbudowana jest z trzech części, każda z nich obsługuje inny przypadek

ewaluacji:

jeżeli zapytanie jest literałem to funkcja eval wkłada odpowiadającą mu wartość
atomową na wierzchołek QRES

jeżeli zapytanie jest nazwą to eval przeszukuje ENVS od góry do dołu oraz wkłada na
QRES do nowej sekcji rezultat wiązania tej nazwy

W przeciwnym wypadku funkcja eval rozpoczyna rozbiór gramatyczny w celu

wyodrębnienia podstawowych podzapytań oraz łączących ich operatów. Następnym
elementem jest obliczenie rezultatów tych podzapytań oraz ich połączenie w jeden rezultat
zgodnie wg operatora. Opisany tu proces jest w swej naturze rekurencyjny, tzn. zapytania są
rozkładane na podzapytania itd. aż do uzyskania bądź to literałów bądź nazw (czyli zapytań
elementarnych).

Operatory, które łączą zapytania dzielą się algebraiczne i nie-algebraiczne. Główna

różnica między nimi to fakt czy podczas ewaluacji modyfikują ENVS czy też nie. Te pierwsze
oprócz tego działają na QRES, odwołują się również do konstrukcji i operacji zachodzących
na ENVS. Drugie zaś operują wyłącznie na QRES (najczęściej operacje te dotyczą jednego
lub dwóch elementów.

background image

4. Optymalizacja zapytań języka SBQL

4.1 Metoda niezależnych pod-zapytań – pierwszy sposób

Pod-zapytania języka SBQL można nazwać niezależnymi, jeżeli mogą być wykonywane

na zewnątrz pętli iteracyjnych implikowanych przez operatory nie-algebraiczne. Takie
kwerendy są godnymi uwagi, gdyż drzemią w nich największe możliwości optymalizacyjne.

Metodą niezależnych pod-zapytań nazywamy taką metodę, która optymalizują zapytania

zawierające w sobie pewne niezależne części. Ogólnie rzecz ujmując polega to na przypisaniu
każdej nazwie występującej w zapytaniu numeru sekcji, w której jest wiązana. Działanie takie
jest motywowane następującą obserwacją: jeżeli żadna z nazw występujących w pod-
zapytaniu nie jest wiązana w sekcji otwartej przez aktualnie ewaluowany operator, to wtedy
pod-zapytanie może zostać wykonane wcześniej niż wskazuje na to jego tekstowa pozycja w
zapytaniu.

Podsumowując metoda ta oparta jest na przepisywaniu, czyli tak modyfikuje tekstową

postać zapytania (bez zmieniania jego końcowego wyniku), aby wszystkie pod-zapytania
mogły wykonać się jak najwcześniej, co w rezultacie skraca czas ewaluacji. Opis tej metody
można znaleźć w [Subieta 2004] oraz w rozprawie doktorskiej [Płodzień 2000].

4.1.1 Omówienie

Nawiązując do założeń języka SBQL, każdy operator nie-algebraiczny otwiera na stosie

środowiskowym swoją sekcję, zaś każda nazwa jest wiązana w pewnej sekcji tego stosu.
Określenie, w których sekcjach które nazwy będą wiązane nie jest możliwe, gdyż za każdym
razem zapytanie może zostać wywołane przy dowolnym stanie stosu środowiskowego. Dla
różnych wywołań sekcje te mogą być umieszczone na innych poziomach stosu, ponieważ
liczba i zawartość dolnych sekcji (tj. sekcji, które są na stosie środowiskowym w momencie
gdy rozpoczyna się ewaluacja zapytania) dla tego zapytania może być różna dla kolejnych
wywołań.

W celu wyznaczenia sekcji, w których nazwy zostaną związane, wystarczy wspomóc się

takim oto zabiegiem. Można przyjąć, iż wszystkie sekcje, która są na stosie w momencie
rozpoczęcia ewaluacji zapytania należą do sekcji numer 1. Dzięki temu, żądane sekcje zostaną
wyznaczone relatywnie w stosunku do sekcji dolnych (tj. sekcji numer 1). Uproszczenie to,
nie powoduje żadnych nieścisłości, ponieważ dla wykonującego się zapytania, istotna jest
informacja o tym, że niektóre z jego nazw zostaną związane w sekcjach dolnych.

Dzięki powyższemu założeniu, można ustalić dla każdej nazwy sekcję, w której jest

wiązana. Dokonuje tego statyczna analiza zapytania wg następujących założeń:

każdemu operatorowi nie-algebraicznemu przypisywany jest numer sekcji, która otwiera
na stosie środowiskowym (odtąd nos)

każdej nazwie przypisywane są dwa numery:

- ilość sekcji na stosie środowiskowym, w momencie wiązania tej nazwy (odtąd ss)
- numer sekcji, w której wiązana jest ta nazwa (odtąd bl)

Jak już zostało wspomniane, numery te są nadawane relatywnie w stosunku do dolnych

sekcji stosu. Nadanie tych numerów oraz późniejsza ich analiza (patrz rozdział 4.3.5 – metoda
wyznaczania niezależnych pod-zapytań) stanowi podstawę opisywanej w tym rozdziale
metody.

background image

4.1.2 Przykład

Jako przykład opisywanej tu metody niech posłuży następujące zapytanie:

[P.4.1]
Pracownik where Zarobek > ((Pracownik where Nazwisko = "Kowalski").Zarobek)

Zapytanie to zwraca pracowników, którzy zarabiają więcej niż pracownik o Kowalski. Należy
zwrócić szczególną uwagę na pod-zapytanie:

[P.4.2]
(Pracownik where Nazwisko = "Kowalski").Zarobek

Jest ono wywoływane dla każdego pracownika istniejącego w bazie danych (w szczególności
dla każdego obiektu zwróconego przez operator where). Jest to zbyteczne. Wystarczyłoby raz
je wykonać, gdyż zwraca ono za każdym razem ten sam rezultat.

Poniższy przykład przedstawia rozkład numerów (nos, ss, bl) nadanych podczas

statycznej analizy.

[P.4.3]
Pracownik where Zarobek > ((Pracownik where Nazwisko = "Kowalski").Zarobek)

(1,1) 2 (2,2) (2,1) 3 (3,3) 3 (3,3)

Jak widać nazwy występujące w pod-zapytaniu [P.4.2] wiązane są w sekcji nr 1 lub 3 stosu
środowiskowego, zaś nie w sekcji nr 2 otwartej przez zewnętrzny operator where. Oznacza to,
że pod-zapytanie to jest niezależne od swojego najbliższego operatora nie-algebraicznego. W
szczególności żadna nazw występujących w tym pod-zapytaniu nie jest wiązana w sekcji
utworzonej przez ten operator.

W związku z tym [P.4.2] można wykonać wcześniej, zanim operator where utworzy

nową sekcję na stosie. Oczywiście rezultat całego zapytania [P.4.1] pozostanie niezmieniony.
W tym celu [P.4.2] zostaje wyciągnięte przed operator where.

[P.4.4]

(((Pracownik where Nazwisko=”Kowalski”) .Zarobek) group as i).(Pracownik where Zarobek

= i)

(1,1) 2 (2,2) 2(2,2) 2 (2,1) 3 (3,3) (3,2)

W zapytaniu pojawiła się nowa unikalna nazwa i. Pod-zapytanie jest wykonywane na
początku, zaś jego wynik jest przechowywany pod nazwą i. Tekstowo wygląda to tak, że w
stare miejsce pod-zapytania wędruje nazwa i, pod-zapytanie przesuwane jest na sam początek,
dodawany jest operator group as oraz projekcji.

4.2 Budowa optymalizatora

Schematyczna budowa optymalizatora przedstawiono na rys. 4.1. Elementy pogrubione

(punkty [3], [4], [5]) oznaczają składowe wykonawcze, zaś elementy pochylone (punkty [4],
oraz rys. 4.2) struktury danych, które one wykorzystują lub tworzą. Klient ([1]) wywołuje

background image

zapytanie ([2]), które jest wejściem dla parsera zapytań ([3]). W wyniku jego działania
powstaje drzewo syntaktyczne ([4]). Na nim to działa optymalizator przepisujący ([5]),
modyfikując je (czyli przepisując) na równoważne drzewo. Tak zmodyfikowane drzewo
otrzymuje na koniec interpreter ([6]), dokonuje wykonania zapytania oraz zwraca jego rezultat
na QRES.

Na poniższym rysunku przedstawiono struktury danych, z których korzysta

odpowiednio optymalizator i interpreter. Zauważyć można występującą tutaj symetrię.
Interpreter korzysta ze składu danych, ENVS oraz QRES. Działanie optymalizatora opiera się
na meta-bazie (odpowiednik składu danych, ale), statycznym stosie środowiskowym S_ENVS
(odpowiednik ENVS), statycznym stosie wyników S_QRES (odpowiednik QRES).

[5]OPTYMALIZATOR

[6]INTERPRETER

meta-baza

skład danych

statyczny stos S_ENVS

ENVS

statyczny stos S_QRES

QRES

Rys. 4.2 Porównanie struktur danych dla optymalizatora i interpretera

Optymalizator podczas swego działania, korzysta z informacji, które są znane podczas

parsingu zapytania (nie odwołuje się do bazy danych). Konsekwencją tego jest zawieranie
przez statyczne stosy (S_ENVS i S_QRES) sygnatur obiektów oraz operowanie na nich w
sposób podobny do działania interpretera na rzeczywistych obiektach bazodanowych.
Operacje zachodzące na stosach statycznych i rzeczywistych strukturach są bardzo podobne,
jednakże interesuje nas nie konkretny wynik, a wyłącznie sygnatura tego wyniku; to ona jest
istotna dla działania optymalizatora.

4.2.1 Meta-baza

Główną różnicą pomiędzy wykonywaniem zapytania a jego optymalizacją jest taka, że

podczas optymalizacji nie korzysta się z prawdziwych obiektów (encji), lecz tylko na ich
definicjach ze schematu bazy. Schemat jest przedstawiony graficznie jako graf, który
modeluje statycznie skład danych. Węzły takiego grafu zawierają definicje encji z bazy
danych (obiektów, atrybutów, asocjacji). Krawędzie stanowią relacje pomiędzy
poszczególnymi encjami (węzłami). Metoda niezależnych pod-zapytań wyróżnia trzy takie

[1]KLIENT ------ [2]ZAPYTANIE ------ [3]PARSER ZAPYTAŃ

|
|

[4]DRZEWO SYNTAKTYCZNE

|
|

[5]OPTYMALIZATOR

|
|

[6]INTERPRETER

Rys. 4.1 Miejsce optymalizatora w schemacie ewaluacji zapytania

background image

relacje:

is_owner_of (ciągła linia, patrz rys. 4.3), pomiędzy bytem nadrzędnym (np. definicją
obiektu) oraz bytem w stosunku do niego podrzędnym (np. definicją atrybutu, pointera
lub metody)

points_to (przerywana linia), pomiędzy definicją pointera a definicją obiektu, do
którego taki pointer prowadzi

inherits_from (podwójna linia), pomiędzy definicją klasy obiektu oraz definicją jego
nad-klasy

Rys. 4.3 Graf schematu metabazy użytego w implementacji

Jak widać na poniższym rysunku pojedynczy węzeł takiego grafu składa się z trzech pól:

Rys. 4.4 Budowa węzła grafu

(a) nazwa danej encji, np.: Prac, Dział
(b) liczność encji, np.: 1, 0..1, 0..*, 1..*, 5..10
(c) typ, który w przypadku obiektów złożonych jest zestawem referencji do jego pod-
obiektów; dla węzłów pointerowych oznacza referencję do definicji obiektu, do którego
danych pointer prowadzi

Dodatkowo każdy węzeł grafu (nie jest to zaznaczone na powyższych rysunkach) jest
identyfikowany wewnętrznym identyfikatorem (odpowiednik wew. identyfikatora obiektu
czasu wykonania). W dalszej części pracy oznaczane będą jako i

nazwaWęzła

, gdzie nazwaWęzła

jest nazwą przypisaną do węzła (pole (a) na rys. 4.4), na którego wskazuje ten identyfikator.

4.2.2 Stosy statyczne

Głównym zadaniem stosów statycznych jest zasymulowanie działania stosów czasu

wykonania (a tym samym zasymulowanie działania ewaluacji zapytań). Do tego celu
potrzebne jest wprowadzenie pojęcie sygnatury.

Każdy typ elementarny należy do zbioru Sygnatura.

Każda referencja do węzła grafu schematu należy do zbioru Sygnatura.

Jeżeli x

Sygnatura, zaś n

N, wówczas para n(x)

Sygnatura. Taki rezultat

Pracownik

0..*

Nazwisko

1

string

Imie

1

string

Stan

1..*

string

PracujeW

1

Kieruje

0..1

Dzial

1

Nazwa

1

string

Lokacja

1

string

Urodzony

1

date

Zarobek

1

int

background image

(nazwaną sygnaturę) będziemy nazywać statyczny binder.

Jeżeli x

1

, x

2

, ...

Sygnatura, wówczas struct{ x

1

, x

2

, ...}

Sygnatura.

Jeżeli x

1

, x

2

, ...

Sygnatura, wówczas variant{ x

1

, x

2

, ...}

Sygnatura. Opcja ta jest

potrzebna w przypadku, gdy opisywany byt czasu wykonania może mieć wiele sygnatur,
zaś podczas kompilacji nie jesteśmy w stanie ustalić, która z nich będzie właściwa.
Przykładem takiej sytuacji są kolekcje z heterogenicznymi elementami.

Jeżeli x

Sygnatura, wówczas bag{x}

Sygnatura.

Jeżeli x

Sygnatura, wówczas sequence{x}

Sygnatura.

Zbiór Sygnatura nie ma innych elementów.

Sekcje na stosie S_ENVS przechowują statyczne bindery. Bindery te na stosie mogą

pojawić się pod następującymi formami:

n(typ) : statyczny binder odpowiada sytuacji, gdy na stosie ENVS ma pojawić się binder
n(v). W tym przypadku typ (powstały w wyniku analizy metabazy lub postaci zapytania)
jest typem wartości v

n(ref) : odpowiada sytuacji, gdy na stosie ENVS ma pojawić się binder n(i), gdzie i jest
identyfikatorem pewnego obiektu składu danych. W tym przypadku ref jest referencją
do metabazy, czyli identyfikatorem tego jej węzła, który w czasie wykonania odpowiada
identyfikatorowi i

n(sygn) : odpowiada to sytuacji, gdy na stosie ENVS ma pojawić się binder złożony n
(rezultat), gdzie rezultat jest wynikiem dowolnego zapytania.

Podobnie na stosie S_QRES: zamiast wartości przechowywany jest typ, zamiast

referencji i podstawiamy identyfikator węzła metabazy, zaś zamiast kolekcji bag i sequence
podstawiamy analogiczne słowa kluczowe z typem elementu lub identyfikatorem węzła
metabazy. Poniższy przykład ilustruje te różnice (zakładając oczywiście, że i

1

, i

3

identyfikatorami obiektów Prac, zaś i

2

jest identyfikatorem obiektu Dział):

struct{i

1

, i

2

}

struct{i

Prac

, i

Dział

}

sequence{ i

1

, i

3

}

sequence{ i

Prac

}

bag{ struct{i

1

, i

2

}, struct{i

3

, i

2

}}

bag{ struct{ i

Prac

, i

Dział

} }

bag{struct{n("Tomek"), Zarobek(2500), d

(i

2

)}}

bag{struct{n(string) Zarobek(int), d(i

Dział

)}}

[P.4.5]

4.2.2 Drzewo syntaktyczne

Drzewo syntaktyczne tworzone jest przez parser zapytań. Jest to struktura danych

składająca się z węzłów (semantycznie istotne elementy zapytania) i połączeń między nimi.
Zanim drzewo zostanie „skonsumowane” na wejściu intepretera powinno zostać
zoptymalizowane.

Na potrzeby metod optymalizacyjnych (w szczególności metody opartej na

przepisywaniu) drzewo w sensie struktury danych powinno być zaopatrzone w metody

background image

ułatwiające nawigację po nim oraz modyfikację. Dodatkowo pojedynczy węzeł powinien być
połączony wskaźnikiem z węzłem pod- i nad-rzędnym, tak aby odwiedzając dowolny węzęł
zawsze można było się odwołać do jego „rodzica” lub „dziecka”. Jest to niezbędne, gdzyż
przepisywanie polega na przesuwanie pod-drzewo, usuwaniu go lub modyfikowaniu. W
rzeczywistości przekształcenie drzewa to nic innego jak zmiana powiązań wskaźnikowych
pomiędzy węzłami. Przeniesienie drzewa o dowolnych rozmiarach polega na przełączeniu
jednego połączenia (dwa wskaźniki).

Rysunek 4.5 przedstawia drzewo syntaktyczne dla zapytania z przykładu [P.4.6]. Należy

zwrócić uwagę na pojedynczy węzeł, do którego to zostało dołożone pole z numerkami (autor
wspomniał już o nich w rozdziale dotyczącem metody niezależnych podzapytań), które
przechowują informację o wysokości stosu oraz numerach sekcji wiązania nazw. Dwustronne
strzałki pokazują to o czym autor pisał w ostatnim akapicie – nawigowaniu po wskaźnikach.

[P.4.6]
Pracownik where Zarobek > Zarobek * 0.5

Rys. 4.5 Drzewo syntaktyczne powstałe dla zapytania P.4.6

4.3 Analiza statyczna zapytania

Analiza statyczna jest podstawą opisywanej statycznej optymalizacji. Wykonywana jest

w czasie kompilacji i jej zadaniami są m. in.:

sprawdzanie typów : każda nazwa sprawdzana jest ze grafem schematu bazy danych; w
szczególności jeżeli dana nazwa nie może być związana, uznawana jest za błędną

tworzenie drzew syntaktycznych zapytań, które następnie są modyfikowane przez
metodę przepisywania

Analiza statyczna potrzebuje do swego działania trzech struktur danych: metabazy,

statycznego ENVS (S_ENVS), statycznego QRES (S_QRES). Poniższy rysunek przedstawia
odpowiadające sobie struktury czasu wykonania i statycznej analizy:

STATYCZNA ANALIZA

CZAS WYKONANIA

meta-baza

< ------ >

skład danych

statyczny stos S_ENVS

< ------ >

ENVS

statyczny stos S_QRES

< ------ >

QRES

Rys. 4.6 Statyczne i czasu wykonania struktury danych

START

op. niealg. WHERE | (2)

lewe pod-zapytanie

nazwa Prac | (1, 1)

prawe pod-zapytanie

op. bin. alg. >

nazwa Zar | (2, 2)

op. bin. alg. *

nazwa Zar | (2, 2)

literał 0.5

background image

Pomimo tego, iż struktury te były już opisywane w rozdziale dotyczącym budowy
optymalizatora autor opisze je bardzo ogólnie:

meta-baza : składa się głównie ze grafu schematu bazy danych; statycznie modeluje
skład danych; podczas analizy graf ten nie zmienia się

statyczny stos środowiskowy (S_ENVS) : statycznie modeluje operacje jakie zachodzą
na stosie ENVS, tj. wiązanie nazwa oraz otwieranie nowych sekcji

statyczny stos rezultatów (S_QRES) : statycznie modeluje proces obliczania wyników
pośrednich i końcowych na stosie QRES

4.3.1 Algorytm

Zanim algorytm zostanie opisany, należy podkreślić, że skład danych jest bliżej

nieznany (np. ilość instancji danego obiektu). Inaczej mówiąc nie wiemy co zrobi funkcja
eval oraz jakie sekcje otworzy funkcja nested. Zatem źródłem, z którego korzysta analiza jest
graf schematu bazy danych. Celem analizy jest przypisanie wszystkim operatorom
niealgebraicznym numerów sekcji, które otwierają (nos), zaś nazwom występującym w
zapytaniach par (ilość sekcji na stosie środowiskowym , numer sekcji w której wiązana jest
ta nazwa) – w skrócie (ss, bl). Późniejsza analiza tych numerów pozwoli na stwierdzenie czy
dany pod-zapytanie jest niezależne od najbliższego operatora niealgebraicznego i w
konsekwencji, wyciągniecie takiego pod-zapytania „przed nawias”, czyli przed ten operator
niealgebraiczny. Zostało to opisane w rozdziale 4.3.5. Zasady działania algorytmu statycznej
analizy są następujące:

wykonywane operacje działają na definicjach (opisach) obiektów, które to dostarcza
meta-baza

wszelkie pod-zapytania (normalnie zwracające wiele wyników) zwracają pojedynczy
rezultat jak swój wynik

dla literału występującego w zapytaniu, sygnaturą jest jego oczekiwany typ, np.
<integer>, <real>, <boolean>; sygnatury te wstawiane są na S_QRES

dla nazwy n w zapytaniu, jej sygnatura zwracana jest jako wynik przeszukiwania stosu
S_ENVS w poszukiwaniu bindera n(x), czyli ostatecznie <x>

jeżeli zapytanie ma postać q as n, a q ma sygnaturę <q_sygn>, to sygnatura całego
zapytania to <n(q_sygn)>, analogicznie dla q group as n

jeżeli zapytanie jest postaci q

1

q

2

, gdzie

to operator algebraiczny, to wtedy

sygnatura zapytania jest połączeniem sygnatur jego pod-zapytań (oczywiście biorąc pod
uwagę reguły arytmetyki sygnatur), np. jeżeli q

1

ma sygnaturę <integer>, q

2

<string>, a

operatorem jest + to sygnaturą całego zapytania jest <string>

w przypadku operatorów reguły są analogiczne, z tym że operacje odbywają się na
S_ENVS, a ich rezultatem jest otwieranie tam sekcji z rezultatem funkcji static_nested
(opisanej w kolejnym podrozdziale)

na początku analizy stos Q_RES jest pusty, zaś S_ENVS zawiera jedną sekcję ze
statycznymi binderami do definicji obiektów startowych, np. bag{Prac(i

Prac

)}

analiza odbywa się na drzewie syntaktycznym; węzły operatorów niealgebraicznych
tego drzewa powinny posiadać miejsce na zapisanie numerów (otwieranych sekcji), zaś
węzły nazw wiązanych powinny posiadać miejsce na dwa numery: aktualny rozmiar
stosu, oraz numer sekcji, w której jest wiązana dana nazwa.

background image

4.3.2 Funkcja static_nested

W statycznej analizie odpowiednikiem funkcji nested jest static_nested. Jak już

napisano wcześniej nested otwiera nowe sekcje na stosie ENVS i wkłada tam bindery.
Podobnie działa static_nested, z tym że operuje na statycznym ENVS.

Zasadnicza różnica jest taka, że static_nested korzysta z metabazy, a nie ze składu

obiektów. Dla każdego (pod)obiektu (w szczególności dla każdego węzła grafu schematu
metabazy) tworzy jeden statyczny binder niezależnie od jego atrybutu liczności. Sygnatury
nowych sekcji odkładanych na S_ENVS są tworzone w oparciu o sygnatury znajdujące się na
S_QRES. Przykładowo, jeżeli na S_QRES znajduję się sygnatura {i

Dział

} to nowo otwarta

sekcja na S_ENVS będzie zawierała statyczne bindery (odnoszące się do publicznych
właściwości obiektu) takie jak w przykładzie [P.4.6]. Poniżej przedstawiono główne zasady
działania funkcji static_nested:

dla sygnatury będącej referencją i

Nazwa

do węzła grafu schematu opisującego obiekt

Nazwa rezultat static_nested zawiera wszystkie statyczne bindery publicznych
właściwości, np.:
static_nested(i

Dział

) = {Nazwa(i

Nazwa

), Lokacja(i

Lokacja

), Zatrudnia(i

Zatrudnia

), Szef(i

Szef

)}

[P.4.6]

dla sygnatury będącej referencją i

Nazwa

do węzła grafu schematu opisującego pointer

Nazwa rezultat static_nested zawiera statyczny binder opisu obiektu do którego
prowadzi ten pointer, np.:

static_nested (i

Dział

) = {Prac(i

Dział

)}

[P.4.7]

dla sygnatury będącej statycznym binderem static_nested zwraca zbiór zawierający ten
sam binder

d

la statycznych struktur:

static_nested(struct{s

1

, ... , s

k

}) = static_nested(s

1

)

 ...  static_nested(s

k

)

4.3.3 Funkcja static_eval

W statycznej analizie odpowiednikiem funkcji eval jest static_eval. To ona dokonuje

statycznej analizy zapytań. Jedną z różnic jest to, że operatory nie-algebraiczne nie powodują
tutaj iteracji. Poniżej przedstawiono ogólny schemat tej funkcji w postaci pseudokodu:

procedure static_eval(query)

{

case query is l { . . .

// l jest literałem

}
case query is n {

// n jest nazwą

... wiązanie nazwy n na stosie S_ENVS ....
... odłożenie wyniku na S_QRES ...
... ustawienie numerów w węźle drzewa syntaktycznego (ss i bl) ...

}
case query is q

1

q

2

{

//

 jest oper. alg. binarnym

q1result, q2result : TypSygnatura;

// deklaracja sygnatur pomocniczych

background image

static_eval(q

1

); // stat. analiza q

1

oraz odłożenie rezultatu q

1

na S_QRES

static_eval(q

2

); // stat. analiza q

2

oraz odłożenie rezultatu q

2

na S_QRES

q2result := top(S_QRES);

// odczytanie i zapamiętanie wyniku q

2

pop(S_QRES);

// usunięcie rezultatu q

2

q1result := top(S_QRES);

// odczytanie i zapamiętanie wyniku q

1

pop(S_QRES);

// usunięcie rezultatu q

1

push(S_QRES, q1result

q2result);

// włożenie końcowego wyniku na S_QRES

}

case query is q

1

q

2

{ //

 jest oper. niealgebraicznym

q1result, finalresult : TypSygnatura;

// deklaracja sygnatur pomocniczych

static_eval(q

1

); // stat. analiza q

1

oraz odłożenie rezultatu q

1

na S_QRES

q1result := top(S_QRES);
push(S_ENVS, static_nested(q1result)); // otwarcie nowego zakresu na S_ENVS

.... zapełnij numer (nos) otwieranej sekcji w węźle drzewa syntaktycznego...

static_eval(q

2

); // stat. analiza q

2

oraz odłożenie rezultatu q

2

na S_QRES

final_result := combine(q1result, top(S_QRES));

// obliczenie końcowego wyniku

pop(S_ENVS);

// usunięcie najnowszej sekcji z S_ENVS

pop(S_QRES); // usunięcie rezultatu q

2

pop(S_QRES); // usunięcie rezultatu q

1

push(S_QRES, final_result); // włożenie końcowego wyniku na S_QRES

}

} // koniec static_eval

4.3.4 Przykład

Rys. 4.7 przedstawia przykładową analizę statyczną zapytania ([P.4.8]) rozpisaną na

poszczególne kroki (niektóre zostały pominięte) wraz ze stanami stosów.

(Pracownik where (Zarobek > 2000))

[P.4.8]

Stos S_ENVS został przedstawiony na szaro, zaś Q_RES na biało.

Stan początkowy

Pracownik

Pracownik

where

Pracownik where

(Zarobek

Pracownik

where (Zarobek

>

Nazwisko(i

Nazwisko

)

Nazwisko

(i

Nazwisko

)

Nazwisko

(i

Nazwisko

)

Zarobek(i

Zarobek

)

Zarobek(i

Zarobek

)

Zarobek(i

Zarobek

)

PracujeW(i

PracujeW

)

PracujeW

(i

PracujeW

)

PracujeW

(i

PracujeW

)

bag{Dział(i

Dział

)}

bag{Dział

(i

Dział

)}

bag{Dział(i

Dział

)}

bag{Dział(i

Dział

)}

bag{Dział(i

Dział

)}

bag{Pracownik

(i

Pracownik

)}

bag{Pracownik

(i

Pracownik

)}

bag{Pracownik

(i

Pracownik

)}

bag{Pracownik

(i

Pracownik

)}

bag{Pracownik

(i

Pracownik

)}

bag{i

Zarobek

}

2500

pusty

bag{i

Pracownik

}

bag{i

Pracownik

}

bag{i

Pracownik

}

bag{i

Pracownik

}

1

2

3

4

5

background image

Pracownik where

(Zarobek > 2000

Pracownik where

(Zarobek > 2000)

(Pracownik

where (Zarobek

> 2000))

Nazwisko(i

Nazwisko

)

Zarobek(i

Zarobek

)

Nazwisko(i

Nazwisko

)

PracujeW(i

PracujeW

)

Zarobek(i

Zarobek

)

bag{Dział(i

Dział

)}

PracujeW(i

PracujeW

)

bag{Pracownik

(i

Pracownik

)}

bag{Dział(i

Dział

)}

2000

bag{Pracownik

(i

Pracownik

)}

bag{Dział(i

Dział

)}

2500

TRUE

bag{Pracownik

(i

Pracownik

)}

bag{i

Pracownik

}

bag{i

Pracownik

}

bag{i

Pracownik

}

6

7

8

Rys. 4.7 Statyczna analiza krok po kroku dla zapytania P.4.8

Krok 1: stan początkowy przed rozpoczęciem ewaluacji

Krok 2: wiązana jest nazwa Pracownik co powoduje zapełnienie S_QRES oraz przypisanie
nazwie Pracownik pary numerków <ss: 1, bl: 1>

Krok 3: operator nie-algebraiczny where otwiera na S_ENVS sekcję o numerze 2, wobec
czego do węzła drzewa syntaktycznego reprezentującego ten operator jest wpisany numer
<nos: 2>

Krok 4: wiązana jest (na drugim poziomie) nazwa Zarobek co powoduje zapełnienie S_QRES
oraz przypisanie nazwie Zarobek pary numerków <ss: 2, bl: 2>

Krok 5: operator algebraiczny > powoduje dereferencję przez co na stosie S_QRES pojawia
się wartość „2500”

Krok 6: na stosie S_ QRES pojawia się literał „2000”

Krok 7: poprzez operator algebraiczny > zachodzi porównanie „2500 > 2000” co powoduje
odłożenie na S_QRES wyniku tego porównania jako TRUE

Krok 8: stan po zakończeniu ewaluacji zapytania

4.3.5 Wyznaczanie niezależnych pod-zapytań

Wyznaczanie niezależnych pod-zapytań opiera się na trzech procedurach, które

korzystają z „owoców” analizy statycznej w postaci nadanych węzłom drzewa numerów
(zostało to opisane w rozdziale 4.1 razem z ogólnym opisem metody). Te procedury to:

void optymalizujDrzewo (węzeł drzewo)

węzeł jestNiezależne (węzeł operNiealg)

void przenieśPoddrzewo (węzeł operNiealg, węzeł podDrzewo)

background image

Pierwsza z wymienionych procedur optymalizujDrzewo przebiega drzewo syntaktyczne
dopóki nie natknie się na operator niealgebraiczny. Po znalezieniu takowego uruchamiana jest
druga z procedur jestNiezależne, która na wejście dostaje węzeł tego operatora
niealgebraicznego, zaś na wyjściu zwraca węzeł największego pod-drzewa będącego w
obszarze działania tego operatora, lecz od niego niezależnego. Jeżeli węzeł takiego pod-
drzewa nie zostanie odnaleziony optymalizujDrzewo działa dalej dla następnego operatora
niealgebraicznego.

W przeciwnym wypadku trzecia z procedur przenieśPoddrzewo dokonuje modyfikacji

drzewa syntaktycznego – fizycznie przenosi znalezione poddrzewo PRZED operator
niealgebraiczny (wyłącza przed nawias). Odbywa się to wg następującej reguły

q

1

q

2

(q

3

)

 (q

3

group as nowaNazwa) . (q

1

q

2

(nowaNazwa))

, gdzie

 jest operatorem niealgebraicznym, q

2

(q

3

) jest zapytaniem, którego składową jest

pod-zapytanie q

3

, przy czym q

3

znajduje się bezpośrednio pod działaniem operatora

;

nowaNazwa jest nową, unikalną nazwą nadaną w sposób automatyczny. Przykłady [P.4.3]
oraz [P.4.4] ilustrują tą przemianę.

Procedura jestNiezależne w swym działaniu wykorzystuje numery nadane podczas

analizy statycznej. Przebiega ona po drzewie zaczynając od największego pod-drzewa z
prawej strony operatora niealgebraicznego i następnie schodząc rekurencyjnie w dół drzewa.
Procedura ta ignoruje prawe pod-zapytania innych operatorów nie-algebraicznych, ale nie
ignoruje ich lewych pod-zapytań. Porównuje ona numery przypisane nazwom z numerami
przypisanymi operatorom niealgebraicznym i na tej podstawie wyznacza niezależne pod-
zapytania. Jeżeli nos różni się od bl to dane pod-zapytanie jest niezależne od operatora
niealgebraicznego. W takim przypadku procedura zwraca węzeł takiego pod-drzewa. Analizie
nie podlegają pod-drzewa składające się z jednego literału lub z jednej nazwy (bo nie można
ich zoptymalizować), z wyjątkiem, kiedy nazwa oznacza wywołanie funkcji lub perspektywy.

Gdy procedura przenieśPoddrzewo zakończy swe działanie drzewo syntaktyczne jest

zmienione. Dalsze wywoływanie rekurencyjnych procedur pozbawione jest sensu. Należy
wywołać procedury static_eval oraz optymalizujDrzewo i zacząć optymalizację na
zmienionym drzewie. To ponowne wywołanie zapewni, że dane pod-zapytanie zostanie
przesunięte przed wszystkie operatory nie-algebraiczne, od których jest niezależne.

Proces ten powtarzamy do momentu gdy po wywołaniu optymalizujDrzewo nie nastąpi

wywołanie procedury przenieśPoddrzewo.

background image

5. Implementacja

5.1 Metabaza

W celu stworzenia prototypu optymalizatora koniecznym było zaimplementowanie

metabazy, tak aby optymalizator miał z czego korzystać – schematu metabazy. Potrzeba taka
była uwarunkowana budową ooPortal'u, który to nie przekazuje informacji o obiektach (nie
można ich uzyskać w prosty sposób); nawet jeżeli takowe są to bardzo ubogie. Interfejs do ich
odzyskiwania tych danych jest skomplikowany i sprawia wrażenie archaicznego.

Metabaza została stworzona specjalnie na użytek optymalizatora: stosowanie metod

staticNested, staticEval wymusza specyficzną konstrukcję metadanych, tak aby nie trzeba
było przeszukiwać całej bazy danych w poszukiwaniu skomplikowanych zależności (atrybuty
dla nazwy). Autor założył, że wszystko ma referencję. Dzięki temu można dostać się do
każdego obiektu (złożonego, atomowego, referencyjnego). Tytułem porównania w SQL'u nie
ma metadanych. Aby je mieć trzeba tworzyć dodatkowe tabele. Dostęp do metadanych
odbywa się poprzez wysyłania zapytań.

Po zaznajomieniu się z prototypem SBQL'a okazało się, że zastałem gotowe struktury

do manipulacji (komunikacją) ze składem: DataBaseWrapper połączony z Database i
ooPortal'em. Oczywiście trzeba je było przystosować do pracy z optymalizatorem. Owocem
tego są cztery dodatkowe metody w klasie DataBaseWrapper:

staticDerefention

staticNested

fillStaticENVSWithRootData

metaDataProducer

Rysunek 5.1 przedstawia strukturę metabazy:

Rys. 5.1 Struktury tworzące metabazę

Klasa MetaDataBase opisuje metabazę oraz zarządza wszystkimi metaobiektami (np.

MetaAtomObject, MetaComplexObject). Wszystkie one dziedziczą z MetaObject, dzięki
czemu można nimi w łatwy sposób zarządzać. Ponieważ dla każdego MetaObject'u tworzony
jest oddzielny obiekt MetaReference (unikalna referencja) wszystkie obiekty są jednoznacznie
identyfikowalne. MetaComplexObject może zawierać dowolną ilość metaobiektów, w

staticDereferention()
staticNested()
fillStaticENVSWithRootData()
metaDataProducer()

DataBaseWrapper

QueryOptimizer

DataBase

ooPortal

getSignature()
getName()

iterator()

MetaDataBase

MetaReference

MetaObject

MetaComplexObject

MetaAtomObject

MetaReferenceObject

1

1

1

1

*

1

1

1

*

1

*

1

background image

związku z czym zagnieżdżanie dowolnej liczby obiektów jest możliwe (w przeciwieństwie do
ooPortal).

QueryOptimizer łączy się bezpośrednio z DataBaseWrapper, który to następnie łączy

się z metabazą. Do komunikacji między optymalizatorem a wrapperem służą cztery
wspomniane powyżej metody.

Największą ułomnością tego rozwiązania jest wkompilowanie metdadanych, które to

następuje przez wywołanie metody metaDataProducer, w której to tworzone są metadane.
Gdy zmieni się skład trzeba przekompilować program z nowymi metadanymi. Ponieważ
ooPortal nie udostępnia takich informacji w prosty sposób, w związku z czym metadane
muszą być w jakiś sposób tworzone, opisywać stan i skład obiektów w bazie danych.

5.2 Drzewo

Klasy opisujące drzewo syntaktyczne i operatory zaimplementowane w prototypie

SBQL'a widoczne są na rys. 5.2. Tutaj podstawową modyfikacją było dodanie dwóch
zmiennych do klasy Name: stackSize oraz bindingLevel, zaś do klasy BinaryNonAlgebraic
zmiennej numberOfScope. Wszystkie one są potrzebne algorytmowi statycznej analizy.

Kolejna modyfikacja dokonała się w operatorach. W każdym z operatorów należało

zaimplementować metodę zdefiniowaną w klasie Operator: staticCombine. Odpowiada ona
za prawidłowe wyliczenia operatorów (liczy wynik działania).

Rys. 5.2 Struktury definiujące drzewo syntaktyczne

QueryCodes

Query

1

1

UnaryOperator

Value

Literal

stackSize
bindingLevel

Name

BinaryOperator

UnaryWithoutParam

UnaryWithParam

BinaryAlgebraic

numberOfTheScope

BinaryNonAlgebraic

1

1

staticCombine()

Operator

OperatorCodes

NonAlgebraicOperator

AlgebraicOperator

OperatorWHERE

OperatorDEPENDJOIN

OperatorCLOSEBY

UnaryAlgebraicOperator

BinaryAlgebraicOperator

OperatorLanguage

OperatorDEREF

OperatorASC

OperatorDESC

OperatorADD

OperatorDIV

OperatorMINUS

OperatorMUL

OperatorSUB

1

*

background image

5.3 Optymalizator

QueryOptimizer został zrobiony tak, że operuje na drzewie, przechodzi je (wszystko w

sposób rekurencyjny). Zgodnie z budową drzewa dla konkretnych gałęzi wykonuje
odpowiednie operatory i działania. Klasa QueryOptimizer zawiera metody: <wymienić>.

initWithMetadata

optimize

optymalizujDrzewo

jestNiezalezne

przeniesPoddrzewo

staticEval

staticNested

Pierwsza z nich initWithMetadata wypełnia statyczny stos środowiskowy danymi
korzeniowymi. Następnie optymalizator uruchamia funkcję optimize na drzewie, które zostało
wcześniej stworzone przez parser. Przed właściwą optymalizacją następuje faza analizy
statycznej (staticEval), podczas której nadawane są numerki węzłom drzewa. Trzy metody:
optymalizujDrzewo, jestNiezalezne, przeniesPoddrzewo są główną siłą sprawczą
optymalizacji. Optymalizator z metabazą komunikuje się poprzez DataBaseWrapper.

Rys. 5.3 Najważniejsze metody klasy QueryOptimizer

5.3.1 Sygnatury

Statyczna analiza wymaga odpowiedników wszystkich rezultatów czasu wykonania. Tą

rolę sprawują sygnatury. Opisują one zależności między rezultatami (np.: int + int = int; int +
float = float). Działają całkowicie w oparciu o metabazę, co oznacza że dane, które
przechowują są odpowiednikami typów danych w składzie.

5.3.2 Stosy

Stosy są to struktury niezbędne do poprawnej prac optymalizatora. S_QRES (statyczny

stos rezultatów) przechowuje rezultaty zapytań, natomiast na stosie S_ENVS odkładane są
sekcje, przechowujące statyczne bindery.

initWithMetadata()
optimize()
optymalizujDrzewo()
jestNiezalezne()

przeniesPoddrzewo()
staticEval()
staticNested()

QueryOptimizer

background image

Rys. 5.4 Stosy Q_ENVS, S_QRES oraz struktury powiązane

5.3.4 Przykłady

Poniżej autor przedstawił wyniki działania optymalizatora zapytań. Dla pojedynczego

zapytania zwracane są w konsolowym okienku jego dwie postaci: oryginalna oraz
zoptymalizowana. Ta druga zawiera numery (w nawiasach []) nadane nazwom i operatorom
niealgebraicznym.

Zanim przejdziemy do konkretnych przykładów proszę spojrzeć na poniższy rysunek.
Przedstawia on wygląd drzewa po ewaluacji zapytania P.5.1. Daje się zauważyć nadane
numerki dla nazw i operatorów algebraicznych.
[P.5.1] Pracownik.PracujeW.Dzial

Rys. 5.1 Zrzut ekranu pokazujący wyniki drzewo oraz analizę statyczną zapytania P.5.1

DataBaseWrapper

QueryOptimizer

push()

pop()

StaticEnviromentalStack

StaticSection

StaticBinder

push()
pop()

StaticQueryResultStack

1
1

1

1

1

1

*

1

*

1

SBQLSignature

1

1

*

*

SBQLCollectionSignature

SBQLElementSignature

SBQLBagSignature

SBQLSequenceSignature

SBQLAtomSignature

SBQLBinderSignature

SBQLStructSignature

*

*

*

*

*

*

SBQLReferenceS. SBQLIntegerS.

SBQLFloatS.

SBQLDateS.

SBQLBooleanS.

SBQLStringS.

background image

[P.5.2] Pracownik where Imie = "Marcin"

Rys. 5.2 Wynik optymalizacji zapytania P.5.2

[P.5.3] Pracownik where Zarobek = ((Pracownik where Imie = "Marcin").Zarobek)

Rys. 5.3 Wynik optymalizacji zapytania P.5.3

[P.5.4] (Lecture where (credits > ((Lecture where (subject = "lect1")).credits)))

Rys. 5.4 Wynik optymalizacji zapytania P.5.4

[P.5.5] (Student where (avgGrade > avg(((((((((Faculty where (fname = "fac1")) . employ
s) . Professor) . gives) . Lecture) . attended_by) . Student) . avgGrade))))

Rys. 5.5 Wynik optymalizacji zapytania P.5.

[P.5.6] (Student where (avgGrade > avg(((((((((Faculty where (fname = "fac1")) . employs) .
Professor) . gives) . Lecture) . attended_by) . Student) . avgGrade))))

Rys. 5.6 Wynik optymalizacji zapytania P.5.6

background image

6. Podsumowanie

Autor zrealizował postawiony przed sobą cel. Optymalizator zapytań języka SBQL

został zaimplementowany oraz przetestowany. Najważniejszy wniosek wypływający z tej
pracy to taki, że optymalizacja zapytań oparta na idei statycznej analizy i przepisywaniu jest w
pełni wykonalna i implementowalna. Implementacja prototypu optymalizatora wykazała
również, że mechanizm statycznej analizy jest bardzo użyteczny w gromadzeniu informacji,
które są niezbędne do zoptymalizowania zapytania. Praca ta pokazuje również, że pomysł aby
optymalizację zapytania przeprowadzać poprzez modyfikację jego drzewa syntaktycznego i
nie używać żadnych innych pośrednich form jest jak najbardziej poprawny.

W rozdziale 3 zostało zaprezentowane podejście stosowe. Jego uniwersalność polega na

tym, że uwzględnia ono bardzo ogólnie model obiektowy, który zawiera np. obiekty złożone,
kolekcje atrybutów, klasy, metody oraz dziedziczenie. Ponadto tworzy ono prostą i
uniwersalną podstawę semantyczną dla języków zapytań OQL'o podobnych połączoną z
pojęciami programistycznymi takimi jak: metody, procedury, perspektywy, przekazywanie
parametrów, co ostatecznie pozawala nam na precyzyjne opisanie semantyki SBQL.

Oprócz statycznej analizy (rozdział 4.3) drugim głównym pojęciem, które zostało

omówione w tej pracy była metoda niezależnych pod-zapytań (rozdział 4.1). Jest ona
najbardziej interesującą metodą spośród metod opartych na przepisywaniu (pozostałe metody
Pojedyncze niezależnie pod-zapytanie może być dowolnie złożone – może zawierać funkcje
agregacyjne, wołania metod, złączenia, kwantyfikatory. Zewnętrzne zapytanie, które zawiera
takie niezależne pod-zapytanie może być również złożone. Głównym powodem jest fakt, że
przy podejściu stosowym semantyka wszystkich niealgebraicznych operatorów (where, join,
projekcji itp.) jest taka sama oczywiście z wyjątkiem obliczania końcowego wyniku.

Podczas implementacji autor napotkał następujące trudności:

połączenie optymalizatora z prototypem języka SBQL (klasyczny przykład
tworzenia oprogramowania na bazie innego, co zawsze stwarza problemy)

tworzenie metadanych – konieczność wkompilowania metabazy w program

szeroko występująca rekursja, wystarczająco skomplikowana, aby stracić
panowanie nad programem

W przyszłości prototyp ten zostanie rozbudowany o pozostałe metody oparte na

przepisywaniu połączone razem z modelem kosztów, co stanowić będzie razem pełen
algorytm optymalizacyjny. Możnaby się również pokusić o

jednoczesną optymalizację wielu zapytań

dynamiczną optymalizację zamiast statycznej

indeksy ścieżkowe

czynniki redukcji dla bardziej złożonych predykatów selekcji

bardziej realistyczną dystrybucję wartości atrybutów (t.j. inną niż standardowa)

background image

7. Literatura

[Subieta 2004] K. Subieta “Teoria i konstrukcja obiektowych języków zapytań”

[Subieta 2002] K. Subieta “Podstawy semantyczne języków zapytań”

[Płodzień 2000] J. Płodzień “Optimization Methods in Object Query Languages”

[ICONS_1] Rodan Icons

http://www.rodan.pl/badania/icons/

[ICONS_2] Rodan Icons

http://rodan.pl/pdf/icons.pdf

[OOPortal] Rodan ooPortal

http://rodan.pl/pdf/officeobjects_portal.pdf

[RR+JG 99] Raghu Ramakrishnan i Johannes Gehrke „Database Management Systems,
2

nd

edition”

background image

Dodatek A

API (application programming interface) - interfejs programistyczny dla aplikacji

ASR (access support relation)

- relacja wsparcia dostępu

ENVS (enviromental stack)

- stos środowiskowy

ooPortal

- ooPortal OfficeObjects® Portal

OQL (object query language)
QRES (query result stack)

- stos rezultatów

S_ENVS (static enviromental stack)

- statyczny stos środowiskowy

S_QRES (static query result stack)

- statyczny stos rezultatów

SBA (stack based approach)

- podejście stosowe

SBQL (stack based query language)

- język oparty na podejściu stosowym

SQL (structured query language)

- strukturalny język zapytań

SZBD

- systemy zarządzania bazami danych

XML (extensible markup language)


Wyszukiwarka

Podobne podstrony:
Cechy języka optymalnego
Optymalizacja LP
Zasady ergonomii w optymalizacji czynności roboczych
optymalizacja fak
Podstawy Optymalizacji, simplex
gimnastyka buzi i języka
model optymalizacyjny
Praktyczna Nauka Języka Rosyjskiego Technologia4
Praktyczna Nauka Języka Rosyjskiego Moje notatki (leksyka)2
BO WYK2 Program liniowe optymalizacja
Kilka słów o nerwicy języka
Praktyczna Nauka Języka Rosyjskiego Sport1
Logistyka i optymalizacja kosztow w handlu internetowym
(ebook www zlotemysli pl) matura ustna z jezyka angielskiego fragment W54SD5IDOLNNWTINXLC5CMTLP2SRY

więcej podobnych podstron