Zapamiętane zapytania jako metoda optymalizacyjna dla obiektowego języka zapytań SBQL

Wielkość: px
Rozpocząć pokaz od strony:

Download "Zapamiętane zapytania jako metoda optymalizacyjna dla obiektowego języka zapytań SBQL"

Transkrypt

1 Zapamiętane zapytania jako metoda optymalizacyjna dla obiektowego języka zapytań SBQL Rozprawa doktorska Piotr Cybula Promotor: prof. dr hab. Kazimierz Subieta Instytut Podstaw Informatyki PAN Warszawa 2010

2

3 Mojej żonie, dzieciom i rodzicom.

4

5 Spis treści Spis treści Spis rysunków...7 Streszczenie...9 Summary Wprowadzenie Stan sztuki Optymalizacja poprzez materializację wyników Zmaterializowane perspektywy w systemach relacyjnych Zmaterializowane perspektywy w systemach obiektowych i XML-owych Materializacja wyników funkcji Oracle Result Cache MySQL Query Cache Plan Caching w PostgreSQL Język zapytań Microsoft.NET LINQ Inne metody optymalizacji zapytań Usuwanie martwych podzapytań Usuwanie zbędnych nazw pomocniczych Wyciąganie selekcji przed złączenia Wyciąganie wspólnych wyrażeń Wyciąganie niezależnych podzapytań Wykorzystanie indeksów Wspomaganie dostępu do danych Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) Model składu Stos środowisk i wiązanie nazw Metabaza Język SBQL Statyczna ewaluacja zapytania i kontrola typologiczna Przykładowa baza danych Koncepcja mechanizmu zapamiętanych zapytań

6 Spis treści 4.1 Architektura systemu przetwarzania zapytań Menadżer zapamiętanych zapytań Rejestr wyników zapytań Statystyki dotyczące zapamiętanych zapytań Konfiguracja działań menadżera Optymalizacja zapytań Normalizacja zapytania Dekompozycja zapytania Analiza zapytania Aktualizacja typologiczna zapytania Interpretacja zapytań i zarządzanie zasobami Użycie zapamiętanych wyników Optymalizacja wykorzystania zasobów Aktualizacja wyników zapamiętanych zapytań Metoda eliminacji Język podschematów dla języka SBQL Optymalizacja porównywania podschematów Korekta przyrostowa wyników Prototyp Optymalizator ISBQLOptimizer ASTNode CacheOptimizer QueryFinder IndependentQuery Interpreter Menadżer pamięci podręcznej CacheManager Polecenia CLI Wyniki metody optymalizacyjnej Podsumowanie i dalsze prace Bibliografia

7 Spis rysunków Spis rysunków Rys.3.1. Poglądowy obraz małej bazy danych...33 Rys.3.2. Przykładowa sytuacja na stosie środowisk...35 Rys.3.3. Graf schematu bazy danych z Rys Rys.3.4. Stany stosu rezultatów dla prostego wyrażenia...43 Rys.3.5. Przykładowy stan stosu rezultatów QRES...44 Rys.3.6. Architektura mechanizmu przetwarzania zapytań...44 Rys.3.7. Rezultat działania procedury eval na stosie rezultatów QRES...45 Rys.3.8. Kroki ewaluacji zapytania: Prac where (Zar > 1000)...50 Rys.3.9. Diagram klas przykładowej obiektowej bazy danych...54 Rys Skład danych...55 Rys Skład danych w formie grafu...56 Rys Graf schematu przykładowej bazy danych...57 Rys.4.1. Architektura uwzględniająca przetwarzanie i optymalizację zapytań/programów...60 Rys.4.2. Uproszczony schemat ewaluacji zapytania przesłanego do bazy danych...67 Rys.4.3. Drzewo syntaktyczne zapytania...68 Rys.4.4. Schemat przebiegu optymalizacji zapytania...70 Rys.4.5. Modyfikacja syntaktyczna zapytania z użyciem zapamiętanego zapytania...93 Rys.4.6. Modyfikacja syntaktyczna zapytania z użyciem części wyników zapamiętanego zapytania...94 Rys.4.7. Schemat przebiegu interpretacji zapytania...99 Rys.6.1. Schemat interfejsu ISBQLOptimizer Rys.6.2. Drzewo ASTNode zapytania Rys.6.3. Optymalizator pamięci podręcznej wyników Rys.6.4. Optymalizator niezależnych podzapytań Rys.6.5. Menadżer pamięci podręcznej Rys.7.1. Porównanie czasu ewaluacji zapytania w zależności od wykorzystania pamięci podręcznej Rys.7.2. Porównanie czasu ewaluacji zapytania w zależności od wykorzystania pamięci podręcznej

8 Spis rysunków Rys.7.3. Średni czasu wykonania zapytania w zależności od wykorzystania pamięci podręcznej Rys.7.4. Porównanie czasu ewaluacji zapytania w zależności od wykorzystania pamięci podręcznej Rys.7.5. Porównanie czasu ewaluacji zapytania w zależności od wykorzystania pamięci podręcznej Rys.7.6. Średni czasu wykonania zapytania w zależności od wykorzystania pamięci podręcznej

9 Streszczenie Streszczenie Celem rozprawy jest opracowanie metod i struktur danych realizujących proces optymalizacji zapytań do obiektowych baz danych. Optymalizacja ta dotyczy zapytań formułowanych w językach opartych na podejściu stosowym SBA (ang. Stack-Based Approach) do konstrukcji języków zapytań w obiektowych (i nie tylko) bazach danych. Podejście to zostało udokumentowane licznymi pracami, ma bardzo silne podstawy naukowe i jest wykorzystywane w celu stworzenia nowoczesnego systemu zarządzania bazą danych. Problem optymalizacji zapytań bazodanowych jest zagadnieniem bardzo szerokim i może dotyczyć różnych metod przyspieszenia realizacji odpowiedzi na zapytanie do bazy danych. Niniejsza rozprawa dotyczy jednej z takich metod - optymalizacji poprzez ponowne wykorzystanie wyników wcześniej zrealizowanych zapytań (zapamiętanych, zmaterializowanych, składowanych zapytań). Innymi metodami optymalizacji zapytań mogą być: optymalizacja poprzez przepisywanie oraz optymalizacja poprzez indeksowanie danych. Zagadnienia te nie są tematem rozprawy. Optymalizacja, której dotyczy rozprawa, jest realizowana w sposób przezroczysty dla końcowego użytkownika oczekującego na wyniki zapytania. Zapamiętanie wyników zapytań daje stosunkowo największy wzrost wydajności w realizacji zapytań, tzn. znacząco obniża czas oczekiwania na odpowiedź ze strony systemu zarządzania bazą danych. Wynika to z faktu, iż wykorzystanie wyników zmaterializowanego zapytania nie wymaga ponownego, czasochłonnego wyszukiwania wymaganych danych w celu uzyskania wyników identycznego zapytania. Dodatkowym atutem jest tutaj niezależność czasu poświęconego na wykorzystanie zapamiętanego zapytania od typu zapytania oraz stanu bazy, gdy tymczasem czynniki te mają istotny wpływ na inne procesy optymalizacyjne, np. indeksowanie. Oczywiście optymalizacja przez materializację odbywa się pewnym kosztem. Wynika on z konieczności przydzielenia dodatkowych zasobów, pamięci podręcznej (ang. cache) na zapamiętanie zmaterializowanych zapytań oraz z czasu niezbędnego do wykonania następujących czynności: zapamiętania struktury zapytania wraz z wynikami, określenia przydatności wyników zapamiętanych zapytań do realizacji aktualnego zapytania, usuwania najrzadziej wykorzystywanych zapamiętanych zapytań (w celu optymalnej utylizacji dostępnych zasobów) oraz aktualizacji wyników zapamiętanych zapytań po zmianach w bazie danych. W tej rozprawie udowodnimy, iż koszt w/w procesów jest w wielu przypadkach mniejszy niż koszt tradycyjnej ewaluacji zapytania, a zatem pokażemy słuszność tezy, że opisywana metoda jest metodą optymalizacyjną. 9

10 Streszczenie Zagadnienie optymalizacji poprzez zapamiętanie wyników zapytań języków opartych na podejściu stosowym wymagało pochylenia się nad wieloma aspektami architektury i funkcjonalności systemu przetwarzania zapytań do bazy danych. Opracowano m.in.: struktury danych i algorytmy realizujące rejestr zapamiętanych zapytań odpowiedzialny za przechowywanie wszystkich niezbędnych związanych z nimi danych oraz ich sprawne wyszukiwanie; struktury danych i algorytmy wspomagające optymalizację zasobów przeznaczonych na zapamiętane zapytania usuwanie zapytań najrzadziej wykorzystywanych; metody normalizacji i dekompozycji zapytań w celu ujednoznacznienia oraz zwiększenia prawdopodobieństwa użycia zapamiętanych wyników; algorytmy doboru zapamiętanego zapytania oraz przepisywania zapytań w celu wykorzystania zapamiętanych wyników; struktury danych i algorytmy pozwalające na możliwie największą redukcję zbioru zapamiętanych zapytań wymagających zaktualizowania wyników po zmianach w bazie danych; algorytmy optymalizacji procesu synchronizacji stanu pamięci podręcznej wyników z aktualnych stanem bazy danych. Ostatecznym etapem pracy było stworzenie prototypowego modułu realizującego omawiane zagadnienia, wykorzystującego zaproponowane algorytmy i struktury danych. Moduł ten jest elementem większego systemu zarządzania obiektową bazą danych dostarczającego nowoczesnego obiektowego języka zapytań SBQL (ang. Stack- Based Query Language) opartego na podejściu stosowym. System ten, o nazwie ODRA (ang. Object Database for Rapid Application development), jest platformą dla obiektowych aplikacji webowych oraz gridowych i jest obecnie konstruowany przez szersze grono naukowców zajmujących się problematyką baz danych, inżynierii oprogramowania i systemów rozproszonych. Implementacja prototypu pozwoliła na przeprowadzenie szeregu testów metody optymalizacyjnej będącej tematem niniejszej rozprawy, w wyniku których uzyskano znaczną redukcję czasu ewaluacji zapytań sięgającą dwóch rzędów wielkości (poniżej 1% czasu normalnej ewaluacji zapytań). 10

11 Summary Summary Cached queries as an optimization method in the object-oriented query language SBQL The Ph.D. thesis focuses on the development of robust methods and data structures for query optimization for object-oriented databases. The thesis has been developed in the context of the Stack-Based Approach (SBA), a theoretical and methodological framework for developing object-oriented query and programming languages. The developed query optimization methods are based on the corresponding Stack-Based Query Language (SBQL). SBA is well documented, has strong theoretical fundamentals and practical solutions for developing modern object-oriented database management system (OODBMS). The orthogonality and very clear semantics of SBQL constructs enable large optimization opportunities. Query optimization is a very wide issue. There are many different solutions for improving of query execution performance. The thesis focuses on one of them an optimization by caching the results of previously executed queries (so-called cached, stored, materialized queries). Other main optimization method are many rewriting optimizations and data indexing. From the conceptual point of view transparency is the most essential property of a cached query. It implies that programmers need not to involve explicit operations on cached results into an application program. Caching of query results yields relatively most improvement in a query evaluation performance, i.e. significantly decreases the time of an anticipation for a response from database management system. The main reason is that receiving the results from a cache for previously performed query, instead of its consecutive reevaluation, is much quicker than time-consuming data exploration. Additional advantage of the query caching method is that reuse time of cached results is independent of query type, its complexity and current database state. On the other hand there are some costs of the result materialization. Firstly, some memory resources are necessary for the cache storing the queries and their results. Secondly, the optimization method needs some time for: storing in the cache queries, their results together with proper structures for maintenance purposes, recognizing the useability of currently materialized results for new queries, removing some rarely used 11

12 Summary cached queries in order to optimal cache utilization and finally, updating of the cached results after database changes. Our work proofs that these costs are in many cases significantly less than cost of the traditional full query evaluation, so we proof thesis of cached queries being an optimization method. A key aspect concerning the development of database query optimization methods is preservation of original query semantics. Consequently, for the designed optimization methods we have determined necessary rules and data structures in a context of the assumed object data model and the SBQL query language. We have developed: a data structures and algorithms for the query cache registry dealing with storing all necessary data of cached queries and smart query searching; a data structures and algorithms for cache maintenance and optimal utilization of assigned resources by removing rarely used results; query normalization and decomposition methods for reuse of cached results for semantically equivalent but syntactically different queries, raising the probability of cache utilization; algorithms for optimal queries selection and rewriting new queries with use of cached results; a data structures and algorithms for maximum reduction of cardinality of cached queries set which were influenced by a database update and should be corrected; algorithms for automatic cache synchronization with current state of a database. A significant part of algorithms and solutions developed in the thesis have been verified and confirmed in the prototype module implementation of caching optimizer as a part of ODRA (Object Database for Rapid Application development) OODBMS project serving object-oriented query language SBQL. ODRA is a platform for modern object-oriented web and grid applications, and is developed by a group of scientists and IT students. The prototype implementation allowed for performing several experimental tests of the optimization results receiving significant improvements of query evaluation performance even by two orders of magnitude (less than 1% of the full query reevaluation time). 12

13 1. Wprowadzenie 1. Wprowadzenie Zapamiętywanie rezultatów wcześniej ewaluowanych zapytań wydaje się być oczywistą metodą optymalizacji zapytań. W koncepcji tej zakłada się, że jest duże prawdopodobieństwo na to, aby takie samo zapytanie zostało wysłane do serwera bazy danych co najmniej kilka razy. W tej sytuacji zamiast wykonywania ewaluacji takiego zapytania za każdym razem, można zwrócić wcześniej zapamiętany rezultat. Taka optymalizacja ma sens gdy częstotliwość aktualizacji jest dość mała w porównaniu z częstotliwością użycia danego zapytania (np. jedna aktualizacja na sto operacji odczytu danych). Przykładem mogą być hurtownie danych (aplikacje OLAP OnLine Analytical Processing), różnorodne bazy danych archiwalnych, systemy regulacji i artykułów prawnych, bazy wiedzy, itp. Poza kwestią częstotliwości zmian w bazie danych, kolejnym ważnym elementem wartym rozważenia jest prawdopodobieństwo ponownego użycia zapamiętanego rezultatu. W niektórych przypadkach wartość ta jest dość wysoka. Na przykład w typowym sklepie internetowym możemy założyć, że około 90% zapytań odnosi się do 10% produktów, dlatego też warto zapamiętać rezultaty zapytań odnoszących się do tych 10% produktów. Takie zapamiętywanie wyników jest dość rozpowszechnione w komercyjnych aplikacjach internetowych, gdzie odbywa się to w postaci zapisywanych stron HTML (ang. HyperText Markup Language) lub plików XML (ang. extensible Markup Language). W przypadku takich aplikacji generowanych jest wiele dynamicznych stron, których treść bazuje na wynikach pobranych z bazy danych. Są to wyniki często powtarzających się zapytań wykonywanych dla różnych użytkowników systemu, które mogą być zapamiętane międzysesyjnie, w globalnej pamięci podręcznej serwera bazy danych współdzielonej przez sesje użytkowników. W niektórych rodzajach aplikacji internetowych, np. portalach newsowych czy blogach, część zawartości strony niezwiązana z prezentacją tych wyników, np. reklamy, podpowiedzi czy dane użytkownika, jest zmienna, więc zapisywanie całej strony w pamięci podręcznej przeglądarki internetowej i wykorzystanie jej ponownie może być w wielu sytuacjach niepożądane. W tym przypadku zapamiętywanie wyników zwracanych przez system bazodanowy, w samym systemie bazy danych lub dodatkowo w warstwie pośredniej na poziomie serwera WWW, może być bardzo korzystne. W sytuacji, gdy prawdopodobieństwo ponownego użycia jest małe, używanie tej metody jest oczywiście nieefektywne, gdyż czas potrzebny do zarządzania procesem zapamiętywania wyników jest większy niż każdorazowa ewaluacja zapytań, np. system rejestrowania aktualnych połączeń operatora telefonii komórkowej nie daje dużych 13

14 1. Wprowadzenie szans na ponowne użycie wyników zapytania przed ich unieważnieniem na skutek zmiany w bazie danych. Pamięć podręczna przypomina swoją funkcjonalnością zmaterializowane perspektywy, dla których wynik jest zapamiętywany (fizycznie na dysku serwera bazy danych) i w przeciwieństwie do zwykłych perspektyw nie jest konieczna każdorazowa ewaluacja zapytania tworzącego perspektywę (tematyka ta została szerzej opisana w następnym rozdziale). Jednakże łatwo zauważamy istotne różnice. Jedną z różnic między tymi mechanizmami jest skala użycia implikująca zupełnie inne podejście koncepcyjne - zmaterializowanych perspektyw może być kilkaset, natomiast w pamięci podręcznej można zapisywać wyniki milionów zapytań. Drugą różnicą jest przezroczystość. Zauważmy, że mechanizm widoków jest dostępny i zrozumiały dla użytkowników bazy danych, natomiast pamięć podręczna wyników zapytań jest mechanizmem wewnętrznym całkowicie przezroczystym dla nich. Nasze badania koncentrują się na sposobie wykorzystania tej przezroczystości do optymalizacji zapytań zakładając brak zmian w syntaktyce, semantyce i pragmatyce języka zapytań samego w sobie. Zapamiętane zapytania są także podobne do indeksów w bazie danych, gdyż oba podejścia oparte są na pomocniczych strukturach po stronie serwera oraz są używane w celu szybszego wydobycia wyników zapytań przesłanych do bazy. Na przykład indeks gęsty posiadający wartość kluczową clerk dla atrybutu Job tabeli Person może być przedstawiony jako zapamiętany wynik zapytania SBQL: <Person where Job = clerk, kolekcja identyfikatorów obiektów (OID s)> gdzie kolekcja identyfikatorów obiektów jest zbiorem zawierającym identyfikatory obiektów spełniających kryterium tego zapytania wartością niekluczową związaną z kluczową wartością clerk tego indeksu. Ta obserwacja sugeruje strukturę danych do implementacji w pamięci podręcznej zapamiętanych zapytań, gdzie kolekcja podobnych zapytań jest reprezentowana przez zapytanie z parametrem wyposażone w dwukolumnową tabelę. W pierwszej kolumnie znajdują się wszystkie wartości parametru (wartości kluczowe), a w drugiej rezultat zapytań dla tych parametrów (wartości niekluczowe). Jeżeli zapytanie nie jest parametryzowane, jest zapamiętane w pojedynczym rekordzie takiej tabeli unikalna treść zapytania i wyniki. Struktura ta może być zaimplementowana w oparciu o tablice haszujące bądź B-drzewa, podobnie do organizacji indeksów w bazach danych. Jednakże zapamiętane zapytania wykazują jednocześnie znaczne różnice w stosunku do indeksów. Jedną z nich jest to, iż indeksy są najczęściej używane dla prostych zapytań, natomiast w pamięci podręcznej możemy zapamiętać wyniki dowolnie skomplikowanych zapytań, co do których mamy przypuszczenie, że zostaną użyte jeszcze co najmniej kilka razy. Dla przykładu rozważamy zapamiętanie zapytań 14

15 1. Wprowadzenie zawierających wielokrotnie parametryzowane selekcje, agregacje, długie wyrażenia ścieżkowe, grupowania wartości, itp. Koncepcja zapamiętanych wyników zapytań generalizuje pojęcie indeksów ograniczonych do zapamiętywania wyników (najczęściej identyfikatorów rekordów lub obiektów) spełniających warunki predykatów operacji selekcji. Dodatkowo indeksy są tworzone na etapie projektu bazy danych, czyli dużo wcześniej przed ich użyciem, gdy tymczasem zapamiętane zapytania pojawiają się automatycznie i w locie jako swoisty efekt uboczny ewaluacji poprzednio przetwarzanych zapytań. Indeksy zawierają wpisy dla wszystkich wartości indeksowanego atrybutu (dla indeksów gęstych są to osobne wpisy dla każdej wartości, dla indeksów rzadkich wszystkie wartości są pogrupowane w przedziały będące wpisami w indeksie), a zapamiętane zapytania dotyczą zazwyczaj podzbioru tych wartości. Kolejną różnicą jest proces aktualizacji: indeksy najczęściej są automatycznie aktualizowane po zmianie bazy, natomiast aktualizacja pamięci podręcznej wymaga dodatkowych mechanizmów poruszanych w tej rozprawie. Z tych względów zapamiętane zapytania implikują całkiem nowe problemy badawcze i implementacyjne (optymalizacja z wykorzystaniem indeksów została szerzej opisana w następnym rozdziale). W rozprawie konstruujemy specjalną strukturę danych występującą po stronie serwera bazy danych, którą nazywamy rejestrem zapamiętanych zapytań (ang. query cache registry). Koncepcyjnie taki rejestr może być rozumiany jako dwukolumnowa tabela, gdzie pierwsza kolumna zawiera zapytania w określonym wewnętrznym formacie (np. znormalizowane drzewa syntaktyczne), natomiast druga ich rezultaty. Rezultat może być zapamiętany jako kolekcja identyfikatorów obiektów (OID's), ale w szczególnych przypadkach mógłby to być plik XML umożliwiający szybie użycie w aplikacji webowej. Proces wyszukiwania zapytania w liście pamiętanych jest wyposażony w dodatkowe mechanizmy umożliwiające szybkie sprawdzenie i ewentualnie wydobycie wyniku takiego zapytania. Obsługa pamięci podręcznej wyników zapytań może być realizowana na kilka sposobów: zapamiętywanie wszystkich zapytań przysłanych do serwera bazy danych; zapamiętywanie tylko zapytań dodanych przez administratora w sposób jawny; analiza zapytań pod kątem przyszłej przydatności lub zgodności z wzorcami predefiniowanymi przez administratora i warunkowe zapamiętanie wyników (administrator może być wyposażony w narzędzie do analizy historii zapytań i ich częstotliwości); analiza zapytań pod kątem ich złożoności i ewentualne zapamiętanie zamiast całości wyników zapytania bardziej ogólnego lub jego podzapytania, np. w przypadku zapytania zwracającego listę pracowników zarabiających więcej niż Smith, możliwa jest dekompozycja na dwa podzapytania pierwsze zwracające wynagrodzenie pracownika Smith i drugie z parametrem x pobierające 15

16 1. Wprowadzenie pracowników o wynagrodzeniu wyższym niż x; pierwsze podzapytanie nie będzie zapamiętane gdy jest wspomagane w bazie danych indeksem, natomiast drugie warto zapamiętać; analiza zapytań pod kątem czasu ewaluacji lub rozmiaru wyników ustalonego przez administratora w celu zmniejszenia obciążeń utrzymania pamięci podręcznej; analiza zapytań pod kątem częstotliwości ich występowania i zapamiętywanie tylko takich, które pojawiły się niemniej niż wskazaną przez administratora liczbę razy. Wybór odpowiedniej kombinacji strategii może być predefiniowany lub realizowany przez bardziej złożony proces oparty o model kosztowy bazujący na statystykach gromadzonych w rejestrze zapamiętanych zapytań. Ten model musi brać pod uwagę: koszt zapamiętania zapytania (mniej istotny gdy zapamiętywane są wszystkie zapytania, rosnący gdy wybrano strategie filtrujące lub dekomponujące opisane wcześniej); koszt dodatkowej pamięci RAM lub/i dyskowej (obecnie pomijalny); koszt użycia pamięci podręcznej (jeśli zbiór zapisanych wyników jest dość duży, koszt ten można zmniejszyć za pomocą struktur wspomagających szybkie dopasowanie i wyszukiwanie wyników zapytania); koszt utrzymania (po aktualizacji bazy danych część zapytań należy usunąć lub zaktualizować wyniki). Teza niniejszej rozprawy brzmi następująco: Zapamiętywanie i ponowne wykorzystanie wyników wcześniej wykonywanych zapytań do obiektowego języka zapytań SBQL jest metodą optymalizacyjną, czyli redukuje czas wykonania kolejnych zapytań. W celu udowodnienia słuszności tej tezy, zagadnienie optymalizacji poprzez zapamiętanie wyników zapytań języków opartych na podejściu stosowym podzielono na trzy grupy podzagadnień, które zostały rozwinięte w kolejnych rozdziałach niniejszej rozprawy. Są one następujące: 1. Struktury danych i algorytmy realizujące rejestr zapamiętanych zapytań. W pracy opisujemy sposób składowania zmaterializowanych zapytań wraz z wynikami w obiektowej bazie danych zgodnej z SBA. Wykorzystane struktury minimalizują zasoby niezbędne do zmaterializowania zapytań oraz umożliwiają prosty i szybki sposób wykorzystania tych zapytań podczas ewaluacji kolejnych. Zakładamy potrzebę dekompozycji zapytania przed jego zmaterializowaniem, tzn. wydzielenia z niego mniejszych niezależnych podzapytań, dającego większe 16

17 1. Wprowadzenie prawdopodobieństwo ich wykorzystania przy ewaluacji innych zapytań. Materializacja wyników zapytań jest w pewnym sensie efektem ubocznym ich pierwotnej ewaluacji, przez co nie jest konieczne przydzielanie specjalnego czasu na ten proces. Jednocześnie podejmujemy realizację algorytmu eliminującego ze zbioru zapamiętanych zapytań podzbioru najrzadziej wykorzystywanych oraz najkosztowniejszych w utrzymaniu. Dzięki temu zasoby przydzielone dla procesu optymalizacji są wykorzystywane optymalnie. Oczyszczanie rejestru ze zbędnych danych jest wykonywane w taki sposób, aby nie powodowało dodatkowego obciążenia systemu bazy danych, a więc spowolnienia realizacji innych zapytań. 2. Struktury danych i algorytmy realizujące dopasowywanie zapamiętanych zapytań w procesie ewaluacji zapytań bieżących. Opisujemy proces przeszukiwania zbioru zmaterializowanych zapytań w celu określenia podzbioru optymalnie przyspieszającego wykonanie aktualnego zapytania. Algorytmy te są oparte na dopasowaniu zapytań, czyli wyszukaniu w drzewie syntaktycznym zapytania bieżącego poddrzew odpowiadających drzewom zapamiętanych zapytań. Znalezione poddrzewo jest zastępowane gotowymi wynikami zapamiętanego zapytania. Przeszukiwanie i dopasowywanie zapamiętanych zapytań jest wspomagane specjalnymi strukturami danych przechowującymi zapytania w formie znormalizowanej, co umożliwia łatwe i niezawodne ich porównywanie. 3. Algorytmy aktualizacji wyników zapamiętanych zapytań po zmianach w bazie danych. Opisujemy sposób utrzymania zapamiętanych zapytań w stanie odpowiadającym aktualnemu stanowi bazy danych. W pierwszej kolejności zajmujemy się oceną czy zmiana zawartości bazy danych ma wpływ na zapytanie zapamiętane w rejestrze zapytań, tzn. czy modyfikacja danych dotyczyła tej części bazy, na podstawie której obliczono wyniki tego zapytania. Dzięki wyeliminowaniu z procesu aktualizacji zapytań, których zmiana w bazie nie dotyczy, kosztowna aktualizacja jest realizowana tylko wtedy, gdy jest niezbędna dla synchronizacji bazy danych i pamięci podręcznej wyników. Zostały opracowane struktury danych przyspieszające proces wyodrębniania podzbioru zapamiętanych zapytań dotkniętych przez modyfikację bazy danych wykorzystujące mechanizm podschematów. Dodatkowo zajmujemy się optymalizacją samego procesu aktualizacji wyników zapytań. Wymaga to algorytmu przyrostowej korekty tych wyników zamiast całkowitego, ponownego wykonania zapytania. Algorytm korekty służy odnalezieniu w wynikach zapytania tych elementów, które należy usunąć w związku z usunięciem odpowiednich danych w bazie, jak również tych, które trzeba dodać (nowe dane w bazie) oraz tych, które powinny być zmienione (modyfikacja istniejących danych w bazie). Podejmujemy próby oceny tych zmian w bazie, 17

18 1. Wprowadzenie dla których aktualizacja wyników zapytań jest kosztowniejsza niż bezpośrednie niezależne wykonanie zapytania bieżącego. Ta ocena może być przydatna dla określenia zbyt kosztownych w utrzymaniu (zbędnych) zapamiętanych zapytań. Opracowanie w/w struktur danych i algorytmów umożliwiło w ostatecznym etapie pracy na implementację prototypowego modułu realizującego omawiane zagadnienia. Rozbudowano optymalizator języka SBQL w systemie ODRA [Sub06, LS07, ADH+08, ODRA1] tworzącego platformę dla obiektowych aplikacji webowych i gridowych dodając do niego moduł optymalizatora cache'ującego. System ten jest obecnie rozbudowywany przez szersze grono naukowców zajmujących się problematyką baz danych, inżynierii oprogramowania i systemów rozproszonych. Implementacja prototypu pozwoliła na przeprowadzenie szeregu testów metody optymalizacyjnej będącej tematem niniejszej rozprawy, w wyniku których uzyskano znaczną redukcję czasu ewaluacji zapytań sięgającą dwóch rzędów wielkości (poniżej 1% czasu normalnej ewaluacji zapytań). Plan niniejszej rozprawy przedstawia się następująco: Rozdział 2 zawiera przegląd aktualnych rozwiązań dotyczących optymalizacji języków zapytań do baz danych, w szczególności optymalizacji zapytań poprzez zapamiętywanie wyników; Rozdział 3 przybliża koncepcję SBA dotyczącą obiektowych baz danych oraz zasady i mechanizmy przetwarzania zapytań konstruowanych w języku SBQL; Rozdział 4 zawiera ogólny opis koncepcji optymalizacji z wykorzystaniem wyników wyników wcześniej wykonanych zapytań oraz szczegółowe rozwinięcie zagadnień zarządzania rejestrem zapytań, procesu optymalizacji i interpretacji zoptymalizowanego zapytania; Rozdział 5 porusza temat aktualizacji wyników zapamiętanych zapytań po zmianach w bazie danych; Rozdział 6 opisuje cechy i sposób realizacji prototypu optymalizatora; Rozdział 7 przedstawia uzyskane za pomocą prototypowego optymalizatora wyniki eksperymentalne; Rozdział 8 podsumowuje pracę i wytycza tematy dalszych badań. 18

19 2. Stan sztuki 2. Stan sztuki Wysoki poziom abstrakcji i deklaratywność zapytań konstruowanych w językach zapytań do baz danych prowadzi zwykle do niskiej efektywności ich wykonania. Zadanie radykalnego skrócenia czasu wykonania zapytań, zwane optymalizacją zapytań, jest więc nieodłącznym aspektem teorii i praktyki baz danych. Literatura dotycząca optymalizacji jest w związku z tym bardzo obszerna, jednakże optymalizacja z wykorzystaniem zapamiętywania wyników jest poruszana stosunkowo rzadko (głównie ze względu na trudności koncepcyjne związane z tym rodzajem optymalizacji pojawiające się w stosowanych modelach danych, szczególnie modelu relacyjnym). Przegląd różnych mechanizmów optymalizacji zapytań dla sieciowych, relacyjnych, obiektowych i obiektowo-relacyjnych baz danych można znaleźć m.in. w [DG82, JK84, KRB85, SZ89, DKL+91, OS91, CD94, Jos95, Ioa96, GGM+97, Cha98, CB00, Plo00, EN04]. Opis stosowanych technik optymalizacyjnych w najpopularniejszych komercyjnych systemach baz danych znajduje się m.in. w [SLM+01, IBM8, ACN00, MSSQL05, MySQL5, Oracle9, Oracle11, PgSQL8]. Techniki i metody optymalizacyjne zapytań do baz danych można podzielić na następujące grupy: 1. Metody ustalające fizyczne zasady organizacji, przechowywania, buforowania i udostępniania danych, np. kodowanie mieszające, mapowanie adresów, itp. 2. Metody oparte na przepisywaniu, w których dokonuje się tylko i wyłącznie transformacji tekstu zapytania (zwykle wewnętrznej reprezentacji zapytania w postaci drzewa syntaktycznego) na inną postać rokującą lepszy czas wykonania, do których należą techniki m.in. polegające na: usuwaniu martwych podzapytań (nie wpływających na wynik) i zbędnych nazw pomocniczych; przekształceniach opartych na rozdzielności operatorów (przesuwaniu operatora selekcji przed operator złączenia, wyodrębnianiu wyrażeń wspólnych); wyodrębnianiu podzapytań niezależnych (zagnieżdżonych). 3. Metody niskopoziomowe oparte przepisywaniu z wykorzystaniem pomocniczych, redundantnych struktur danych zwanych zwykle indeksami, do których zaliczamy: indeksy gęste, zakresowe, unikalne, odwrócone, bitmapowe, przestrzenne, itp.; struktury wspomagające dostęp do danych, np. wyrażeń ścieżkowych; 19

20 2. Stan sztuki zapamiętane wyniki (do tej grupy należy technika będąca tematem niniejszej rozprawy). W rozdziale 2.1 omówiono aktualne rozwiązania dotyczące metody optymalizacyjnej opisywanej w rozprawie, natomiast w rozdziale 2.2 przedstawione zostały inne najczęściej stosowane techniki optymalizacji zapytań. 2.1 Optymalizacja poprzez materializację wyników Koncepcja optymalizacji zapytań poprzez zapamiętywanie wyników prezentowana w niniejszej rozprawie nawiązuje do badań zaprezentowanych w [SR87, RS88], dedykowanych dla pewnego modelu sieciowej bazy danych [SK84, Sub85]. Autorzy prezentują podejście optymalizacyjne oparte na tzw. składowanych zapytaniach (ang. stored queries). Rezultaty zapytań są zapamiętywane zgodnie z aktualnym stanem bazy danych i aktualizowane po zmianach w bazie. Uniwersalność wypracowanych w tych pracach rozwiązań może być podstawą dla realizacji materializacji wyników zapytań w innych podejściach do architektury baz danych, również w SBA, szczególnie w kwestii utrzymania wyników w zgodności ze zmieniającym się stanem bazy. Jednakże zorientowany obiektowo model danych (obejmujący również przetwarzanie danych półstrukturalnych bazujących na plikach XML) wprowadza całkiem nową jakość, stąd metody które proponujemy znacznie różnią się od tego podejścia. Nasze badania są prowadzone w nurcie podejścia stosowego do obiektowych języków zapytań/programowania (SBA) przybliżonego w następnym rozdziale. SBA jest formalną teorią i koncepcyjnie uniwersalnym rozwiązaniem dla języków tego typu, w szczególności pozwala precyzyjnie wnioskować w kontekście różnorodnych aspektów metody zapamiętanych zapytań, tzn. semantyki zapytań, dekompozycji, indeksowania zapytań w pamięci podręcznej, itp. Przedstawiona metoda wykorzystania zapamiętanych wyników zapytań jest częścią optymalizatora stworzonego dla języka SBQL wykorzystującego inne metody optymalizacji zapytań, które zostały szerzej opisane w rozdziale 2.2. Literatura dotycząca baz danych zawiera niewiele pozycji traktujących o zapamiętanych zapytaniach lub podobnych technikach optymalizacji. W kolejnych podrozdziałach dokonano przeglądu istniejących rozwiązań w tej dziedzinie Zmaterializowane perspektywy w systemach relacyjnych W systemach relacyjnych baz danych koncepcja zapamiętywania wyników pojawiła się dość dawno w kontekście tzw. zmaterializowanych perspektyw (ang. 20

21 2. Stan sztuki materialized views), dla których wynik jest zapamiętywany (fizycznie na dysku serwera bazy danych) i w przeciwieństwie do zwykłych perspektyw nie jest konieczna każdorazowa ewaluacja zapytania tworzącego perspektywę. Jest zatem wiele prac dotyczących wykorzystania i utrzymania zmaterializowanych perspektyw w kontekście modelu relacyjnego, np. [AL80, Adi81, SI84, LHM+86, Han87, Rou91, CR94, JMR+93, CKP+95, GB95, GM95, RSS96, Rou97, SV98, LR01, Oracle9, IBM8]. Rozwiązania te zakładają pewnego rodzaju restrykcje na definicje perspektyw podlegających materializacji, a dokładnie na rodzaj wyrażeń stosowanych w zapytaniach i zwracanych, zapamiętywanych struktur. Tymczasem w niniejszej rozprawie przyjęliśmy założenie o dowolności zapytań uwzględnianych w metodzie optymalizacyjnej, co jest wykonalne w kontekście spójnej i jasnej semantyki języka SBQL. Starsze prace [Fin82, LY85] orbitujące wokół algebry relacji dotyczą użyteczności zbioru zapamiętanych zapytań dla celów optymalizacyjnych. Zagadnienie zarządzania zapamiętanymi zapytaniami podjęte w naszej pracy jest zbliżone do pomysłów zaprezentowanych w [BLT86], lecz w naszym przypadku zależność pomiędzy częstotliwością modyfikacji bazy danych i liczebnością elementów do utrzymania jest w przypadku zapamiętanych zapytań jest zupełnie odwrotna niż w przypadku zmaterializowanych perspektyw opisanych w tym artykule. Wspomniane zmaterializowane perspektywy są też w wielu podejściach stosowane jako narzędzie do optymalizacji grupowej zapytań (ang. multi-query optimization), np. w [Sel88, SSN94, CKA+96, RSS+00, MRS+01] Zmaterializowane perspektywy w systemach obiektowych i XML-owych Materializacja wyników zapytań za pomocą mechanizmu perspektyw w bazach danych opartych na algebrach obiektowych jest rozważana w [KM90, AFP03]. W kilku przypadkach te podejścia są wiązane z aspektem przetwarzania zapytań rozproszonych [ACP+96, DFJ+96, KFD97]. W [CR02, SWD+02] zaproponowane jest rozwiązanie dla przetwarzania zapytań XML z użyciem zmaterializowanych perspektyw zapisanych w języku zapytań XQuery. Autorzy tych prac przedstawiają algebraiczne podejście do przyrostowego aktualizowania wyników takich perspektyw. W aktualnie rozwijanych obiektowych systemach baz danych (zarówno prototypowych jak i komercyjnych), wzorujących własne języki zapytań na języku OQL (ang. Object Query Language), będącym wzorcowym językiem standardu ODMG (ang. Open Database Management Group) [CB00], brakuje rozwiązań optymalizacyjnych wykorzystujących materializację wyników zapytań. Wprowadzono jedynie pamięć podręczną obiektów z głównym wykorzystaniem w rozproszonym 21

22 2. Stan sztuki środowisku bazy danych. Optymalizacja z wykorzystaniem zapamiętanych zapytań dla czysto obiektowego języka zapytań takiego jak SBQL jest więc zupełnie nowym wyzwaniem w dziedzinie informatyki, którego podjęliśmy się w niniejszej rozprawie Materializacja wyników funkcji W [KKM91, HS93] podjęta została kwestia zapamiętywania wyników funkcji (wbudowanych jak i definiowanych przez programistę) lub metod klas w miejsce wielokrotnego ich wywoływania z tymi samymi parametrami. Parametry wejściowe oraz odpowiadające im wyniki są przechowywane w specjalnej strukturze. Gdy następuje ponowne uruchomienie funkcji z parametrami już raz użytymi, wystarczy sięgnąć do tej struktury po zapamiętane wyniki bez konieczności wykonania kodu funkcji. Funkcja wywoływana jest ponownie tylko po zmianach w bazie danych, które wpłynęły na jej wyniki wszystkie wpisy dotyczące tej funkcji są wtedy uznawane za nieaktualne. Struktury przechowujące wyniki zapytań realizowane są w postaci tablic haszowanych (gdy wyniki przechowywane są w pamięci operacyjnej), tabel relacyjnych sortowanych względem kolumn zawierających parametry funkcji lub form mieszanych Oracle Result Cache Materializacja wyników zapytań SQL (ang. Structured Query Language), procedur składowanych zakodowanych w języku PL/SQL i zapytań kierowanych poprzez interfejs programistyczny ODP.NET (ang. Oracle Database Provider for.net) została wprowadzona w systemie bazy danych Oracle Database 11g [Oracle11]. Wyniki zapamiętane przez system są wykorzystywane przy ponownym wykonaniu tego samego zapytania i podlegają automatycznej aktualizacji po zmianach w bazie danych. Niestety również w przypadku tego aspektu materializacji wyników zapytań podjętego w tym systemie bazy danych mechanizm zapamiętywania wyników nie jest przezroczysty dla programisty. W przypadku potrzeby zapamiętania wyników często wykonywanego zapytania programista jest zmuszony zasugerować takie działanie kompilatorowi języka poprzez umieszczenie w treści zapytania tzw. parametru result_cache wyławianego z komentarza zawartego pomiędzy znakami /* i */, np.: select /*+ result_cache */ field1,, fieldn from Analogiczne jawne konstrukcje pojawiają się w kodzie procedur i funkcji składowanych tworzonych w języku PL/SQL, np.: create function fun_name(...) return result_cache relies_on(table_name) as 22

23 2. Stan sztuki Programista musi podczas tworzenia kodu podjąć decyzję dotyczącą materializacji wyników końcowych (w przypadku funkcji) oraz wyników operacji wewnętrznych wstawiając odpowiednie parametry w nagłówku tej funkcji/procedury. Drugi parametr ( relies_on ) jest jawną sugestią dla systemu zawierającą informację o liście tabel, których zmiana wymusza aktualizację wyników takiej funkcji. Mechanizm aktualizacji wyników nie jest zatem również przezroczysty w zastosowaniu. Podobnie jak w przypadku zwykłych zapytań SQL mamy tutaj do czynienia z pełną reewaluacją zapytania, czy funkcji składowanej w ramach odnowienia zapamiętanych wyników MySQL Query Cache Mechanizm pamięci podręcznej wyników zapytań jest zaimplementowany w systemie relacyjnej bazy danych MySQL [MySQL5] jako Query Cache. Mechanizm ten zapamiętuje tylko i wyłącznie pełną tekstową treść zapytań SELECT wraz z odpowiadającym im wynikom. Gdy identyczne zapytanie pojawia się w systemie, serwer pobiera wyniki bezpośrednio z pamięci podręcznej zamiast ponownego parsowania i wykonywania tego zapytania. Podobnie jak w przypadku bazy Oracle dane zapisane w pamięci wyników są współdzielone pomiędzy sesjami użytkowników, więc wyniki wygenerowane dla jednego użytkownika mogą być użyte jako odpowiedź na takie samo zapytanie innych użytkowników. Zapamiętywane są również wyniki zapytań SELECT tworzących perspektywy (ang. views), lecz nie podlegają tej optymalizacji podzapytania SELECT wchodzące w skład innych zapytań, części unii jak i również perspektyw tworzonych w locie (ang. inline views). Nie są zapamiętywane także (w przeciwieństwie do bazy Oracle) wyniki procedur składowanych, nawet jeżeli wywołują po prostu zapytanie SELECT. W odróżnieniu od systemu Oracle mechanizm zapamiętywania wyników jest tutaj przezroczysty dla programisty nie są wymagane żadne dodatkowe dyrektywy w treści zapytań włączające użycie go. Niemniej jednak dostępne są dyrektywy SQL_CACHE i SQL_NO_CACHE pozwalające wymusić użycie lub nie pamięci podręcznej w zależności od domyślnej konfiguracji systemu. W systemie MySQL Query Cache optymalizacja ma miejsce tylko gdy zapytania są dokładnie zgodne, tzn. muszą zgadzać się tekstowo bajt po bajcie. Powodem takiego rygorystycznego założenia jest fakt, iż ten mechanizm działa jeszcze przed parsowaniem treści zapytania, więc nie mają tutaj miejsca żadne normalizacje syntaktyczne zapytań. Konsekwencja jest taka, że umieszczenie dynamicznych komentarzy w treści zapytań, dodatkowe białe znaki lub zmiana wielkości liter będą oznaczać pojawienie się w pamięci podręcznej zdublowanych wyników semantycznie zgodnych, lecz syntaktycznie nie, zapytań. Po zmianach w bazie MySQL ogranicza się do poziomu tabeli jako minimalnej jednostki zapewnienia zgodności zawartości Query Cache ze stanem bazy. Gdy tabela zostaje zmieniona wszystkie zapamiętane zapytania, które jej używają są usuwane wraz z wynikami z pamięci podręcznej, bez prób 23

24 2. Stan sztuki aktualizacji wyników, ani rekalkulacji. Nawet gdy modyfikacja dotyczyła jednej kolumny w tabeli, której zawartość nie wpływa na wyniki części zapytań, są one wszystkie eliminowane z pamięci. Jeżeli wiele zapytań używa pewnej tabeli, jej modyfikacja może zdegradować w sposób istotny zarówno korzyści z tego mechanizmu optymalizacji jak i czas wykonania tej modyfikacji Plan Caching w PostgreSQL W systemie bazy danych PostgreSQL [PgSQL8] zastosowano rozwiązanie przyspieszające ewaluację zapytań polegające na zapamiętywaniu w pamięci podręcznej planów ewaluacji tych zapytań zaraz po pierwszym ich wykonaniu (dotyczy to przede wszystkim zapytań stanowiących treść procedur i funkcji składowanych w bazie danych). Nie zapamiętywane są jednak wyniki zapytań. Rozwiązanie to ma istotny wpływ na czas ewaluacji zapytań w przypadku bardzo skomplikowanych zapytań, dla których proces doboru optymalnego planu wykonania (szczególnie pod kątem wykorzystania indeksów i innych struktur dostępnych dla optymalizatora) może być długotrwały i nie warto przeprowadzać go wielokrotnie. Po zmianach dotyczących tabel wykorzystywanych przez zapamiętane plany zapytań, plany takie są usuwane z pamięci podręcznej i tworzone ponownie przy kolejnej ewaluacji tych zapytań Język zapytań Microsoft.NET LINQ Brak jest wykorzystania techniki zapamiętanych wyników zapytań w języku zapytań Microsoft.NET LINQ [LINQ35] w znaczeniu optymalizacyjnym. Pojawia się tam pamięć podręczna wyników w kontekście tzw. opóźnionego przetwarzania zapytań (ang. deferred query evaluation), jednak jest to jedynie optymalizacja związana z ewaluacją zapytania dopiero w momencie konieczności wykorzystania jego wyników. Zapamiętanie wyników w miejsce wielokrotnego wykonywania zapytania realizowana jest tutaj również w sposób jawny z użyciem odpowiednich konstrukcji programistycznych zapisujących je w formie tablicy lub listy (ToList, ToArray). Nie jest to więc mechanizm przezroczysty dla programisty. 2.2 Inne metody optymalizacji zapytań Metoda wykorzystania zapamiętanych wyników zapytań, jako jedna z technik optymalizacji zapytań, może być zastosowana razem z innymi metodami optymalizacyjnymi (wymienionymi na początku tego rozdziału). Ze względu na swój charakter, czyli możliwość zapamiętania wyników całego zapytania, która w efekcie sprowadza ewaluację zapytania do wywołania pojedynczej funkcji pobierającej 24

25 2. Stan sztuki zapamiętane wyniki, powinna być uruchamiana jako ostatnia w łańcuchu optymalizacji. Wraz z wynikami najczęściej zapamiętywany jest plan ewaluacji zapytania, który może zostać ponownie uruchomiony w przypadku konieczności aktualizacji wyników po zmianach w bazie danych. Warto więc przed zapamiętaniem zoptymalizować zapytanie innymi dostępnymi metodami, aby ewentualna reewaluacja była jak najmniej kosztowna. Poniżej przedstawiono kilka najważniejszych metod optymalizacji zapytań, które powinny być stosowane przed optymalizatorem cache'ującym (niektóre z nich mogą być też wykorzystane jako metody pomocnicze w trakcie jego pracy), najlepiej w podanej kolejności Usuwanie martwych podzapytań Martwym pod-zapytaniem (ang. dead subquery) jest taki fragment zapytania, który nie ma wpływu na jego końcowy wynik. Martwe pod-zapytania można więc usunąć, razem z operatorami, które je wiążą z całym zapytaniem, i nie zmieni to końcowego wyniku. Przykład zapytania (wyrażonego w języku SBQL) zawierającego martwe podzapytanie jest następujący: (Prac join (PracujeW.Dział)).Nazwisko Zapytanie to zwraca nazwiska pracowników, a kosztowny operator złączenia join działa na pod-zapytaniu (PracujeW.Dział), ale wynik tego pod-zapytania jest niepotrzebny. Końcowy wynik wyznacza pod-zapytanie Nazwisko połączone operatorem kropki, zaś to pod-zapytanie korzysta wyłącznie z wyniku pod-zapytania Prac. Zatem pod-zapytanie (PracujeW.Dział) oraz operator join można w tym przypadku usunąć, optymalizując to zapytanie do postaci: Prac. Nazwisko Szczegółowy opis tej metody optymalizacyjnej został zawarty w [SMA90, Plo00, Sub04]. Zapytania posiadające martwe pod-zapytania mogą pojawić się jako skutek wielu przeróbek programów aplikacyjnych, w wyniku których końcowa intencja zapytania różni się od początkowej, ale o tej początkowej programista nie pamięta Usuwanie zbędnych nazw pomocniczych Nazwy pomocnicze (ang. auxiliary names) są bardzo użytecznym narzędziem w definiowaniu zapytań. Umożliwiają etykietowanie różnych pośrednich wyników zapytań w celu łatwego dostępu do nich w dalszych częściach tych zapytań. W wielu jednak sytuacjach nazwy te są zbędne, a ich obecność wynika z przyzwyczajeń 25

26 2. Stan sztuki programistów lub jest efektem działania metod optymalizacyjnych polegających na rozwijaniu kodu perspektyw lub funkcji i wstawianiu ich bezpośrednio do zapytania w celu umożliwienia zastosowania innych metod optymalizacyjnych (np. indeksów lub zapamiętanych zapytań). Optymalizacja przez usuwanie zbędnych nazw pomocniczych (ang. unnecessary auxiliary names) [Plo00] może być wykonana tylko dla tych nazw, których wyeliminowanie nie zmienia końcowego wyniku zapytania (dla przykładu w języku SBQL wynikiem zapytania może być nazwana wartość, tzw. binder, gdyż wprowadzenie nazwy pomocniczej odbywa się za pomocą operatora algebraicznego as [Sub04]). Rozpatrzmy zapytanie pobierające pracowników o zarobkach mniejszych niż 1000: SQL: SELECT p.* FROM Prac p WHERE p.zar < 1000 SBQL: ((Prac as p) where p.zar < 1000).p W obu wersjach zapytania pomocnicza nazwa p została użyta do jednoznacznego odwołania się do atrybutu Zar i ostatecznie do wynikowej projekcji zwracającej odpowiednio pełne rekordy z tabeli Prac lub identyfikatory obiektów Prac. Nazwa ta może być jednak zupełnie usunięta bez wpływu na semantykę, a więc i wyniki zapytania: SQL: SELECT * FROM Prac WHERE Zar < 1000 SBQL: Prac where Zar < 1000 Nowe zapytanie bez konieczności wiązania nazw pomocniczych nie musi być wykonane szybciej, ale zwiększa szanse użycia innych metod optymalizacji zapytań Wyciąganie selekcji przed złączenia Jedną z podstawowych metod optymalizacji relacyjnych zapytań jest tzw. przesuwanie selekcji przed złączenie (ang. pushing selection before join). Operacja złączenia jest czasochłonna i bezpośrednio zależy od wielkości złączanych relacji. Metoda przesuwania selekcji przed złączenie redukuje wielkość relacji, przez co zapytanie jest wykonywane znacznie szybciej [LMS94]. 26

27 2. Stan sztuki W SBA metoda ta została uogólniona do wyciągania selekcji przed dowolny operator posiadający cechę rozdzielności (ang. distributivity property) [Plo00, Sub04]. Dotyczy to więc poza operatorem złączenia (join) innych operatorów niealgebraicznych, np. nawigacji (.) i samej selekcji (where) oraz algebraicznych, np. iloczynu kartezjańskiego i przecięcia mnogościowego kolekcji. Przykładem jest zapytanie pobierające identyfikatory pracowników o nazwisku Nowak wraz z działami, w których są zatrudnieni: Prac join (PracujeW.Dział where Nazwisko = Nowak ) W zapytaniu tym wyznaczane są działy dla wszystkich pracowników, nawet tych, którzy nia mają nazwiska Nowak. Predykat Nazwisko = Nowak selekcji dotyczy obiektów pracowników, a nie działów, więc może być przesunięty przed operację złączenia. W takim przypadku zapytanie należy przekształcić do postaci: (Prac where Nazwisko = Nowak ) join (PracujeW.Dział) W zoptymalizowanym zapytaniu złączenie wykonywane jest na minimalnej liczbie elementów. Ponieważ metoda ta prowadzi do bardziej optymalnych zapytań, należy ją zastosować przed metodą niezależnych podzapytań opisaną w rozdziale Wyciąganie wspólnych wyrażeń Identyfikacja wspólnych wyrażeń (podzapytań) w celu jednokrotnej ich ewaluacji jest popularną metodą optymalizacyjną i może występować w wielu odmianach, np. opisanych w rozdziałach i Specyficznym zastosowaniem tej metody, szczególnie w obiektowych modelach danych, może być wyciąganie z zapytania wspólnych wyrażeń ścieżkowych (ang. common path subexpressions) [CCM96, Plo00] popularnych w obiektowych językach zapytań. Podobnie jak w przypadku wyciągania selekcji przed złączenie, metoda ta bazuje na regule rozdzielności, w tym przypadku operatora nawigacji (.), względem operatorów algebraicznych. Dla przykładu zapytanie pobierające pracowników pracujących w Warszawie na ulicy Zielonej zapisane w języku SBQL może mieć postać: Prac where (PracujeW.Dział.Adres.Miasto = Warszawa and PracujeW.Dział.Adres.Ulica = Zielona ) Wyrażenie ścieżkowe PracujeW.Dział.Adres jest wywoływane dwukrotnie dla każdego obiektu Prac, będąc wspólnym podwyrażeniem dla obu predykatów selekcji. Po zastosowaniu metody wyciągnięcia tego wyrażenia uzyskujemy zapytanie: 27

28 2. Stan sztuki Prac where PracujeW.Dział.Adres.(Miasto = Warszawa and Ulica = Zielona ) W otrzymanej wersji wyrażenie ścieżkowe jest ewaluowane dla każdego pracownika tylko raz Wyciąganie niezależnych podzapytań Metoda niezależnych podzapytań (ang. independent subqueries) jest jedną z najważniejszych metod optymalizacyjnych. Jej początki sięgają systemów relacyjnych i kwestii likwidacji zagnieżdżeń w zapytaniach języka SQL (ang. nested queries) [Ram98] poprzez wykrywanie zapytań niezależnych, tzn. takich, które mogą być wykonane jednokrotnie zamiast wielokrotnej ewaluacji w wersji zagnieżdżonej. W przypadku podejścia SBA polega ona na badaniu, w której sekcji stosu środowisk są wiązane poszczególne nazwy wchodzące w skład podzapytań tego zapytania [Plo00, Sub04]. Jeżeli wszystkie nazwy danego podzapytania są wiązane w sekcji innej niż ta, które na stos kładzie aktualnie ewaluowany operator niealgebraiczny, to może ono być obliczone zanim operator otworzy tę sekcję czyli wcześniej, niż to wynika z tekstowego umieszczenia tego podzapytania w zapytaniu. Zatem analiza numerów sekcji, w których są wiązane poszczególne nazwy w zapytaniu, staje się podstawą optymalizacji polegającej na unikaniu wielokrotnego liczenia tego samego podzapytania. Rozważmy zapytanie dające w wyniku tych pracowników, którzy zarabiają tyle samo co Nowak (zakładamy tutaj, że w bazie danych jest tylko jeden pracownik o takim nazwisku): SQL: SELECT * FROM Prac p1 WHERE p1.zar = (SELECT p2.zar FROM Prac p2 WHERE p2.nazwisko = Nowak ) SBQL: Prac where Zar = ((Prac where Nazwisko = Nowak ).Zar) Zauważmy, że podzapytanie zwracające zarobek Nowaka: SQL: SBQL: SELECT p2.zar FROM Prac p2 WHERE p2.nazwisko = Nowak (Prac where Nazwisko = Nowak ).Zar jest ewaluowane tyle razy, ile w bazie danych jest rekordów (obiektów) Prac (potencjalnie bardzo wiele), podczas gdy wystarczyłoby policzyć je tylko raz, gdyż za każdym razem jego ewaluacja da dokładnie taki sam wynik. Gdy wynik ten jest już znany, to za każdym razem, gdy niezależne podzapytanie ma być ewaluowane, wystarczy odczytać obliczoną już wartość. 28

29 2. Stan sztuki O podzapytaniu tego rodzaju w przypadku języka SBQL mówi się, że jest niezależne od najbliższego operatora niealgebraicznego, którym w podanym przykładzie jest pierwszy operator where. Żeby wyrazić to w tekstowej postaci, należy wyciągnąć niezależne podzapytanie przed pętlę iteracyjną implikowaną przez pierwszy operator where. W tym celu trzeba wprowadzić nową, unikalną nazwę pomocniczą, nazwać nią wynik ewaluacji tego podzapytania i wstawić ją w jego miejsce: (((Prac where Nazwisko = Nowak ).Zar) group as z).(prac where Zar = z) Po modyfikacji każde podzapytanie, z wyjątkiem podzapytania z, jest zależne od swojego najbliższego operatora niealgebraicznego, więc dla nich metody niezależnych pod-zapytań nie można już zastosować. Wykorzystanie tej metody optymalizacyjnej w kontekście dekompozycji zapytań dla optymalizacji przez zapamiętywanie wyników zostało przedstawione w rozdziale W przypadku języka SQL, którego składnia nie umożliwia tego rodzaju rozwiązań, stosuje się najczęściej zamianę treści zapytania na postać kanoniczną [Ram98] lub przedefiniowanie niezależnego zapytania wewnętrznego w postaci zmaterializowanej perspektywy i wykorzystanie operacji złączenia z zapytaniem zewnętrznym [SPL96] Wykorzystanie indeksów Indeksy są pomocniczymi (redundantnymi) strukturami danych przechowywanymi po stronie serwera [JK84, BF95, Ioa96, Kim96, Cha98, Ram98, CB00, Plo00, EN04, Sub04, KWK+08]. Administrator bazy danych zarządza pulą indeksów, generując nowe indeksy, o ile rozpozna ich potrzebę, lub je usuwając, jeżeli pewne indeksy są nieprzydatne. Możliwe jest również automatyczne tworzenie i usuwanie indeksów przez system bazy danych [Oracle11]. Zaletą indeksu jest jego stosunkowo mały rozmiar (w porównaniu do całości bazy danych) oraz jednoaspektowość wyszukiwania, co umożliwia ich bardzo efektywną organizację. W najbardziej popularnym ujęciu indeks należy rozumieć jako dwu-kolumnową tablicę, gdzie pierwsza kolumna zawiera wartości kluczowe, zaś druga wartości niekluczowe, najczęściej identyfikatory rekordów lub referencje do obiektów. Wartości kluczowe są unikalne i służą jako wejście dla procedury wyszukiwania w indeksie. Wynikiem wyszukiwania wg danej wartości kluczowej są wartości nie-kluczowe umieszczone w tym samym wierszu tablicy. Wartości kluczowe są wartościami zapamiętanymi w określonych atrybutach obiektów bazy danych (dla indeksów gęstych) lub są reprezentantami przedziałów tych wartości (dla indeksów zakresowych). Najbardziej popularną strukturą danych używaną do organizacji indeksów są indeksy z kodowaniem haszującym (ang. hash coding, linear hashing) [Lit80], indeksy oparte o metodę zwaną B-drzewem (ang. B-tree, balanced tree) [Com79] w bardzo wielu odmianach oraz inne struktury dla indeksów specjalizowanych, np. R-drzewa dla 29

30 2. Stan sztuki indeksowania danych przestrzennych (ang. spatial indices) [Gut84]. Istotnymi cechami indeksów są przezroczystość dla twórcy zapytań oraz automatyczna aktualizacja zawartości indeksu po zmianach w bazie. Indeks można uważać za kolekcję zapamiętanych zapytań o jednakowej budowie. Np. indeks IndeksPracNazwisko(x), dotyczący atrybutu Nazwisko rekordów (obiektów) Prac, można uważać za realizację zapytania Prac where Nazwisko = x, gdzie x jest łańcuchem znakowym. Dzięki temu, zapytanie o postaci: SQL: OQL: SBQL: SELECT * FROM Prac WHERE Nazwisko = Nowak SELECT * FROM Prac p WHERE p.nazwisko = Nowak Prac where Nazwisko = Nowak można zastąpić wywołaniem funkcji IndeksPracNazwisko( Nowak ), co w znacznym stopniu przyspieszy wykonanie tego zapytania Wspomaganie dostępu do danych Rozszerzeniem pojęcia indeksów w obiektowych bazach danych są struktury wspomagające dostęp do danych, poprzez indeksowanie całych wyrażeń ścieżkowych (ang. access support relations, ASRs) [KM90, Plo00]. Pomysł polega na utrzymywaniu redundantnie wyników najczęściej używanych łańcuchów referencyjnych, czyli wyrażeń ścieżkowych, np. wyrażenia zwracającego przełożonych pracowników: Prac.PracujeW.Dział.Szef.Prac Podobnie jak w przypadku użycia indeksów ewaluacja takiego wyrażenia może być zastąpiona wywołaniem odpowiedniej funkcji zdefiniowanej przez programistę lub administratora bazy danych o nazwie np. ASRPracSzef, która dodatkowo może filtrować zwracane wyniki dostosowując się do kontekstu, w którym została uruchomiona. W zapytaniu pobierającym nazwiska szefów w wieku nie przekraczającym 35 lat, będących przełożonymi pracowników o zarobkach przekraczających 3000 (wyrażonym w SBQL): ((Prac where Zar > 3000).ASRPracSzef where Wiek <= 35).Nazwisko funkcja ta została użyta w celu zoptymalizowania dostępu do danych i zwraca tylko część wyników zapisanych w odpowiednim indeksie ASR. 30

31 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) Stack-Based Apporach (SBA) wraz z językiem zapytań SBQL (Stack-Based Query Language) jest rezultatem poszukiwań nowej koncepcji i semantycznej platformy integrującej zapytania z językiem programowania w obiektowych bazach danych. W rozdziale tym (w podrozdziałach ) dokonujemy streszczenia głównych założeń podejścia stosowego opisanych szczegółowo w [Sub04]. Opisane zostały te aspekty tego podejścia, które są istotne w świetle metody optymalizacyjnej będącej tematem niniejszej rozprawy. SBA zakłada, że język zapytań jest odmianą języka programowania. Ta metoda jest abstrakcyjna i uniwersalna, dzięki czemu staje się odpowiednią dla modelu obiektowego. SBA umożliwia precyzyjnie określić semantykę języków zapytań, ich związki z obiektową koncepcją, z programowaniem abstrakcyjnym, zawierającym procedury, procedury funkcyjne, widoki, moduły, itp. Ich główne cechy: Każda nazwa występująca w zapytaniu będzie wiązana do bytu programistycznego czasu wykonania (trwałych danych i obiektów, atrybutów obiektów, procedur, metod, parametrów procedur i metod, lokalnych danych procedur, itd.) zgodnie z zakresem właściwym dla tej nazwy. Reguły zakresu dla wiązania nazw będą ustalone poprzez stos środowisk (ang. environment stack) występujący w nieomal wszystkich językach programowania. W odróżnieniu od standardowych stosów, nie zawiera obiektów, lecz ich identyfikatory wraz z innymi elementami. SBA zakłada wewnętrznej identyfikacji, ortogonalną trwałość (ang. orthogonal persistence), czyli brak jakiejkolwiek różnicy w sposobie formułowania zapytań do trwałych i nietrwałych danych, jak również fakt, iż jedno zapytanie może zawierać odwołania do dowolnych bytów czasu wykonania (w szczególności trwałych i nietrwałych danych) oraz relatywizm obiektów, tj. składnia, semantyka i pragmatyka użycia zapytań będzie identyczna dla dowolnego poziomu hierarchii danych (w szczególności, dla poziomu całej bazy danych, dla poziomu wnętrza pojedynczego obiektu, itd.). Rezultaty procedur funkcyjnych oraz metod należą do tej samej kategorii semantycznej i są traktowane jako rezultat zapytania. Dlatego funkcje i metody mogą być zagnieżdżone w zapytaniu. 31

32 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) Typy są własnościami klas/interfejsów i są używane podczas statycznej kontroli typologicznej zapytań SBQL jak i programów oraz jako mechanizm do określania, kiedy obiekty są zbudowane ze zgodnością z schematem bazy danych. 3.1 Model składu Każdy obiekt posiada: I wewnętrzny identyfikator, który nie może być użyty w zapytaniu i nie ma możliwości jego wyświetlenia. N zewnętrzną nazwę(wprowadzoną przez programistę lub projektanta bazy), która jest używana by uzyskać dostęp do obiektu z poziomu programu. V wartość, która może być atomowa, pointerowa lub złożona. Obiekty określamy następują: Obiektem jest trójka <i, n, v>. Taki obiekt będziemy nazywać obiektem atomowym. Obiektem jest trójka <i 1, n, i 2 >. Taki obiekt będziemy nazywać obiektem pointerowym lub referencyjnym. Obiekt taki jest identyfikowany przez i 1, natomiast i 2 jest jego wartością, która jest pointerem (inaczej referencją) do innego obiektu. Obiektem jest trójka <i, n, T>, gdzie T jest zbiorem dowolnych obiektów. Taki obiekt będziemy nazywać obiektem złożonym. Powyższa reguła jest rekurencyjna, czyli umożliwia tworzenie obiektów o nieograniczonej złożoności i o nieograniczonej liczbie poziomów hierarchii. Skład obiektów jest zdefiniowany jako para <S, R>, gdzie S jest zbiorem obiektów, zaś R jest zbiorem identyfikatorów określanych jako identyfikatory startowe. Zbiór R wyznacza punkty wejściowe do składu obiektów, czyli takie obiekty, które mogą być początkiem nawigacji w zbiorze przechowywanych obiektów. Przeważnie są to identyfikatory obiektów znajdujących się na górnym poziomie hierarchii obiektów, tj. takich, które nie są zawarte w innych obiektach. 32

33 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) Rys.3.1. Poglądowy obraz małej bazy danych Skład obiektów podlega pewnym ograniczeniom: Każdy obiekt, podobiekt, itd. w składzie posiada unikalny identyfikator. Jeżeli (na dowolnym poziomie hierarchii obiektów) wystąpi obiekt pointerowy <i 1,n,i 2 >, to również powinien istnieć obiekt posiadający identyfikator i 2. Warunek oznacza brak zwisających pointerów (lub tzw. integralność referencyjną). Dowolny identyfikator ze zbioru R jest identyfikatorem pewnego obiektu znajdującego się w składzie. Obiekt bezpośrednio osiągalny posiada identyfikator ze zbioru R. Obiekt jest osiągalny, jeżeli jest bezpośrednio osiągalny lub jest podobiektem obiektu osiągalnego. Obiekt jest także osiągalny, jeżeli posiada identyfikator i 2 oraz jest osiągalny obiekt pointerowy <i 1, n, i 2 >. Obiekty nieosiągalne nie mają znaczenia dla semantyki języka, ponieważ nie są w stanie wpłynąć na wynik ewaluacji zapytań, są one tzw. nieużytkami (ang. garbage) i mogą być w dowolnym momencie skasowane. 33

34 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) 3.2 Stos środowisk i wiązanie nazw Stos środowisk ENVS (ang. environment stack, environmental stack) pojęcie to pojawiło się w informatyce bardzo wcześnie, na początku lat 60-tych, wraz z językiem Algol-60 i innymi językami zwanymi Algolo-podobnymi. Od tego czasu stos ten jest elementem konstrukcji większości znanych języków programowania, włączając Pascal, C/C++, Smalltalk, Java, itd. Idea tego stosu jest znana wszystkim konstruktorom języków oraz większości programistów programujących w tych językach. Stos środowisk (ENVS) Stos środowisk (określany także jako stos wołań, ang. call stack), to struktura danych odpowiedzialna za kontrolowanie zmian środowiska wykonania programów. W językach programowania cel, działanie i organizacja mechanizmu stosu środowisk jest dobrze rozpoznana. Stos ten jest odpowiedzialny za: kontrolowanie zakresów nazw zmiennych i wiązanie tych nazw; przechowywanie wartości lokalnych zmiennych funkcji, procedur lub metod; przechowywanie wartości parametrów aktualnych funkcji i procedur; przechowywanie tzw. śladu powrotu, tj. adresu instrukcji, do której ma przejść sterowanie po zakończeniu działania funkcji, procedury lub metody. Stos środowisk jest strukturą danych przechowywaną w pamięci operacyjnej (lub wirtualnej). Jest on podzielony na części, które będziemy określać jako sekcje, przy czym kolejność tych sekcji jest istotna. Stos jest zarządzany zgodnie z wołaniami procedur, funkcji, metod, itd. oraz w związku z wejściem sterowania w tzw. bloki programu. Nowa sekcja (tzw. zapis aktywacji, ang. activation record) pojawia się na wierzchołku stosu w momencie wejścia sterowania programu w procedurę (funkcję, metodę) oraz w momencie wejścia sterowania w blok. Sekcja ta zawiera wartości lokalnych zmiennych, wartości parametrów oraz (dla procedur, funkcji i metod) ślad powrotu. Nowa sekcja na stosie odpowiada każdemu wywołaniu procedury, funkcji lub metody, lub wejściu sterowania w nowy blok. Sekcja ta jest usuwana z wierzchołka stosu w momencie zakończenia procedury (funkcji, metody) oraz w momencie wyjścia z bloku. Wszystkie lokalne zmienne zadeklarowane w aktualnie wykonywanej procedurze (funkcji, metodzie) oraz jej parametry są przechowywane na wierzchołku tego stosu. Wiązanie nazw Wiązanie (ang. binding) nazw użytych w programie jest to zastępowanie oryginalnych nazw występujących w tekście programu na byty programistyczne czasu wykonania, np. na adresy pamięci operacyjnej, identyfikatory obiektów, adresy startowe procedur, itd. 34

35 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) Przykładowo, wiązanie zmiennej x oznacza zastąpienie tej nazwy przez adres pamięci operacyjnej, gdzie przechowywana jest wartość tej zmiennej. Wiązanie nazw na stosie środowiskowym (m.in. nazw zmiennych i parametrów) odbywa się więc według prostej zasady: wyszukuje się najpierw wartości opatrzonej tą nazwą na wierzchołku stosu; jeżeli na wierzchołku takiej nazwy nie ma, poszukuje się tej wartości w sekcji poniżej; proces ten jest kontynuowany aż do znalezienia wartości opatrzonej tą nazwą, ale z uwzględnieniem reguł zakresu (ang. scoping rules), które nakazują omijanie pewnych sekcji stosu; jeżeli podana nazwa nie została odnaleziona na stosie, wówczas poszukiwana jest ona wśród zmiennych globalnych (ew. tzw. zmiennych statycznych), bibliotek funkcji i zmiennych/stałych środowiskowych. Można uważać, że tego rodzaju globalne własności znajdują się na dole stosu środowisk. Sytuacja ta została zilustrowana na Rys.3.2. Zakładamy, że sterowanie aktualnie znajduje się w bloku b, który znajduje się wewnątrz procedury p2, a ta została wywołana z procedury p1. Strzałki pokazują kolejność przeszukiwania stosu podczas wiązania nazwy x występującej wewnątrz modułu m. Wierzchołek stosu Kolejność poszukiwania zmiennej x Zmienne zadeklarowane wewnątrz bloku b Zmienne i parametry procedury p2 Zmienne i parametry i procedury p1 p1... Dół stosu Zmienne globalne Rys.3.2. Przykładowa sytuacja na stosie środowisk Rys.3.2 ilustruje także regułę zakresu: zmienne i parametry procedury p1 są niewidoczne podczas wiązania nazw występujących w wywołanej przez nią procedurze p2 (oraz w dowolnych innych wywołanych przez nią procedurach). Reguła ta jest podyktowana koniecznością hermetyzacji lokalnych własności procedury. Programista programujący p1 i programista programujący p2 są to często dwie osoby, ich działania mogą być odległe w przestrzeni i czasie. Np. p2 może być procedurą biblioteczną zakupioną od innej firmy. Programista programujący p2 nie uwzględnia tego, kto i z jakiego środowiska wywoła jego procedurę. Dzięki regule zakresu nie występuje 35

36 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) sytuacja, że np. pewna zmienna globalna zostaje związana nie w dolnej sekcji stosu, lecz przypadkowo wewnątrz procedury p1. Zarówno programista programujący p1 jak i programista programujący p2 mogą dzięki temu używać dowolnych nazw, bez uzgadniania i bez możliwości konfliktu. Mechanizm stosu środowiskowego pozwala zrealizować następujące własności języków programowania: Abstrakcja i hermetyzacja: wnętrze napisanej procedury (funkcje, metody) zostaje ukryte przed programistami, którzy jej użyją. Procedura jest widoczna wyłącznie poprzez jej interfejs, na który składają się jej nazwa, jej parametry, jej wynik oraz jej skutki działania na zewnątrz. Izolacji: programiści piszący różne procedury (funkcje, metody) nie muszą o sobie wiedzieć ani nie muszą między sobą uzgadniać nazw użytych przez nich lokalnych zmiennych. Dowolne wywoływanie procedur z innych procedur, włączając wołania rekurencyjne. Dzięki temu, że sekcja stosu jest przypisana do wołania procedury, nie zachodzi konflikt przy wywołaniach procedur z procedur; w szczególności, procedura może bez ograniczeń wywołać samą siebie. Semantyczna niezależność i ponowne użycie: procedura o dobrze wyspecyfikowanym interfejsie i działaniu może być wielokrotnie wywołana z wielu miejsc danej aplikacji. Może być także używana w wielu aplikacjach. Spójne zarządzanie nazwami użytymi w programie: przestrzeń użytych nazw jest ściśle kontrolowana, zaś nazwy są wiązane do bytów programistycznych czasu wykonania według ścisłych reguł. Realizacja metod transmisji parametrów: wartości parametrów oraz inne ich własności są odkładane w lokalnych sekcjach stosu, dzięki czemu możliwy jest spójny dostęp i zarządzanie parametrami oraz realizacja metod transmisji parametrów, takich jak wołanie przez wartość (call-by-value) lub wołanie przez referencję (call-by-reference). Podane własności mają również znaczenie dla języków zapytań, pozwalając w spójny sposób zrealizować niektóre ich założenia, takie jak możliwość dowolnego zagnieżdżania zapytań, możliwość powoływania lokalnych nazw wewnątrz zapytań, możliwość używania nazw z bazy danych łącznie z nazwami zmiennych programistycznych, nazwami procedur, funkcji i metod. Podstawową strukturą przechowywaną na stosie środowisk jest binder. Binder jest parą <n, x>, gdzie n jest zewnętrzną nazwą (nazwą zmiennej, stałej, obiektu, funkcji, perspektywy, procedury, metody, itd.), zaś x jest bytem czasu wykonania (zwykle referencją do obiektu). Parę <n, x> będziemy zapisywać n(x). 36

37 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) Rezultaty zapytań Bazując na wcześniej zdefiniowanym modelu składu można rekurencyjnie określić możliwe rezultaty zapytań kierowanych do składu obiektów. Rezultatami zapytań mogą być: atomowe wartości należące do V (np. 3, "Kowalski", true, itd.); referencje do obiektów (inaczej identyfikatory obiektów) dowolnego typu należące do I (w szczególności referencje do metod, procedur, funkcji, perspektyw, itd.); pary n(x), gdzie x jest rezultatem, zaś n N (taki rezultat, będący nazwaną wartością, będziemy jak poprzednio nazywać binderem); struct{ x 1, x 2, x 3, }, gdzie x 1, x 2, x 3, są rezultatami. struct jest konstruktorem struktury, czyli pewnym dodatkowym atrybutem (flagą) rezultatu. Kolejność elementów w strukturze może mieć znaczenie. W odróżnieniu od znanych definicji struktur (rekordów, itd.) nie wymagamy, aby x i były nazwanymi wartościami (binderami). Będziemy także uważać, że dla pojedynczego x będącego rezultatem konstrukcja struct{x} jest równoważna x, zaś struct{} (konstruktor struct zastosowany do pustego zestawu elementów) jest konstrukcją niepoprawną. Powyższa konstrukcja uogólnia pojęcie krotki znane z modelu relacyjnego; bag{ x 1, x 2, x 3,...} oraz sequence{ x 1, x 2, x 3, }, gdzie x 1, x 2, x 3, są rezultatami. bag i sequence są konstruktorami kolekcji (flagami), na podobnej zasadzie jak konstruktor struct. Nie ma żadnych innych rezultatów zapytań poza wyżej wymienionymi. Przykładem złożonego rezultatu może być: bag{struct{ Dział(i 56 ), } Prac( bag{ struct{ n("nowak"), s(i 9 ) }, struct{ n("stec"), s(i 14 ) } ) } Funkcja nested Zawartość nowej sekcji, która ma być umieszczona na stosie ENVS jest wyznaczana przez funkcję nested. Jej dziedziną jest zbiór wszystkich wyników zapytań, natomiast jej wartościami są bindery identyfikatorów obiektów. Dzięki tej funkcji mamy dostęp do wnętrza obiektu złożonego w celu związania nazw jego podobiektów. Definicja tej funkcji ma postać: 37

38 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) 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 > }> zachodzi nested(i) = { n 1 (i 1 ), n 2 (i 2 ),..., n k (i k ) }; jeżeli i jest identyfikatorem obiektu pointerowego <i, n, i 1 > oraz istnieje w składzie obiekt <i 1, n 1, v 1 >, wówczas nested(i) = { n 1 (i 1 ) }. Inaczej mówiąc, wewnętrzne środowisko obiektu pointerowego jest binderem do pojedynczego obiektu, na który ten pointer wskazuje. Taka definicja będzie zgodna z ideą nawigacji w obiektowej bazie danych wzdłuż przechowywanych w niej pointerów. Jest to podstawa m.in. tzw. wyrażeń ścieżkowych (ang. path expressions); dla dowolnego bindera n(x) zachodzi nested( n(x) ) = { n(x) }. Inaczej mówiąc, lokalne środowisko bindera jest utożsamiane z nim samym. jeżeli argumentem funkcji nested jest struktura, wówczas wynik jest sumą mnogościową rezultatów funkcji nested dla pojedynczych elementów struktury: nested( struct{ x 1, x 2, x 3,...}) = nested( x 1 ) nested( x 2 ) nested( x 3 )... ; dla dowolnych innych argumentów wynik funkcji nested jest pusty. Dotyczy to w szczególności dowolnej wartości atomowej v V, identyfikatora obiektu atomowego, identyfikatora metody, procedury, funkcji, perspektywy, itd. 3.3 Metabaza Głównym składnikiem metabazy jest graf schematu bazy danych. Może również zawierać w sobie inne dane, takie jak: statystyki dostępu, definicje indeksów, itd. Graf schematu jest odpowiedzialny za modelowanie statycznie składu danych, czyli jest wewnętrznym katalogiem danych powstałym jako skutek kompilacji schematu danych. Węzły grafu zawierają definicje bytów znajdujących się w bazie danych (obiektów, atrybutów, pointerów, klas, asocjacji, itd.) oraz interfejsy metod przechowywanych w bazie danych, natomiast nazwane krawędzie grafu modelują powiązania pomiędzy definicjami. Wyróżniamy trzy rodzaje powiązań: is_owner_of (ciągła linia), pomiędzy bytem nadrzędnym (np. definicją obiektu złożonego) 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 ten pointer prowadzi; inherits_from (podwójna linia), pomiędzy definicją klasy obiektu oraz definicją jego nad-klasy. 38

39 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) Rys.3.3. Graf schematu bazy danych z Rys.3.1 Każdy węzeł schematu danych zawiera: wewnętrzny identyfikator węzła grafu, który będzie służył jako referencja do tego węzła; zewnętrzna nazwa danego bytu (używana w zapytaniach); informację, czy dany byt jest obiektem z identyfikatorem startowym; ograniczenie liczności definiowanego bytu w bazie danych; typ wartości danego bytu. Może to być typ elementarny (int, string, itd.), referencja do definicji obiektu, do którego prowadzi definicja pointera, referencje do definicji obiektów podrzędnych w grafie schematu, lub specyfikacja wejścia i wyjścia (sygnaturę) procedury lub metody. 39

40 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) 3.4 Język SBQL Język SBQL jest sformalizowanym obiektowym językiem zapytań w stylu SQL lub OQL. Jest pewną idealizacją opartą na abstrakcyjnej składni, pokazującą istotę semantyki operatorów wprowadzonych w większości języków zapytań. Pełni on tę samą rolę, jaką pełni algebra relacji i rachunek relacji w relacyjnych językach zapytań. W szczególności, posiada on semantyczne odpowiedniki podstawowych konstrukcji tych języków. Wspiera dobrze znane struktury sterowania (if, loop, itp.), jak również procedury, klasy, interfejsy, moduły, oraz inne programistyczne i bazodanowe abstrakcyjne. Definicja SBQL bazuje na pojęciu stanu, którego składnikami są skład obiektów oraz ENVS. Poniżej przedstawiony został skrócony opis podstawowych założeń języka SBQL. Szerszy obraz dostępny jest w [Sub85, Sub87, SBM+95, SKL95, Plo00, Sub04, Sub06]. Język ten został już wielokrotnie zaimplementowany, np. dla systemu LOQIS [SMA90, Sub90, Sub91], modelu XML DOM, OODBMS Objectivity/DB, w ramach europejskiego projektu ICONS i aktualnie jest rozwijany dla prototypów obiektowych systemów bazodanowych klient-serwer YAOD i ODRA ukierunkowanych na internetowe i gridowe aplikacje [LS07, ADH+08, ODRA1]. Składnia SBQL Przy definiowaniu składni SBQL trzeba przyjąć, że pewne elementy wprowadzonego poprzednio zbioru wartości (V) mają reprezentację zewnętrzną, która daje możliwość zapisania tego elementu w zapytaniu w postaci ciągu bajtów. Istnieje wiele elementów zbioru V, takich jak grafika, skompilowane procedury i metody, itd. które nie posiadają reprezentacji zewnętrznej, mogą istnieć więc wyłącznie wewnątrz składu obiektów lub są zwracane jako rezultat zapytania. Zwykle zewnętrzny reprezentant elementu zbioru V jest zwany w językach programowania literałem. Można użyć terminu stała, ale występuje wtedy kolizja znaczeniowa z nazwanym elementem składu obiektów, którego nie można modyfikować. Literał zewnętrzny, leksykalny reprezentanta pewnej wartości zbioru V. Zbiór literałów będziemy oznaczać L. Zakładamy, że istnieje prosta i jednoznaczna funkcja odwzorowująca element zbioru L w element zbioru V. Zgodnie z powszechną konwencją, funkcji tej nie będziemy specjalnie oznaczać. Np. literał Nowak może pojawić się w zapytaniu, oraz Nowak jest wartością tekstową (stringiem) przechowywaną w składzie obiektów bądź zwracaną jako rezultat zapytania. Zwrócimy uwagę, że pierwsza wartość (string) Nowak jest elementem języka zapytań, zaś drugi elementem meta-języka, który służy jako podstawa opisu języka. Wprowadzone poprzednio identyfikatory należące do zbioru I nie mają leksykalnych reprezentantów, więc nie mogą być zapisane w postaci literałów. Programista lub użytkownik nie może użyć w zapytaniu reprezentacji znakowej 40

41 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) wewnętrznego identyfikatora w taki sposób, że jej znaczeniem będzie identyfikator wewnętrzny. Do odwołania się do obiektów znajdujących się w składzie obiektów należy użyć ich zewnętrznej nazwy należącej do zbioru N. Przyjmiemy, że każdy element zbioru N może być użyty w zapytaniu. Dowolny element zbioru L jest zapytaniem; np. Jan, 25, Dowolny element zbioru N jest zapytaniem; np. Osoba, Firma, pensja, zarobek, wiek. Zapytania można łączyć w większe zapytania przy pomocy operatorów. Z syntaktycznego punktu widzenia operatory są podzielone na unarne i binarne. Operatory będą ponadto podzielone na algebraiczne i niealgebraiczne: Jeżeli jest oznaczeniem operatora algebraicznego unarnego, zaś q jest zapytaniem, wówczas (q) jest zapytaniem. Przykładami operatorów algebraicznych unarnych są: count, sum, avg, log, -, sin, sqrt, not, itd. W niektórych sytuacjach, np. dla operatorów - oraz not, będziemy pomijać nawiasy. Jeżeli jest oznaczeniem operatora algebraicznego binarnego, zaś q 1 i q 2 są zapytaniami, wówczas q 1 q 2 jest zapytaniem. Przykładami operatorów algebraicznych binarnych są: =, +, -, *, /, <. >, and, or, union, itd. Jeżeli θ jest oznaczeniem operatora niealgebraicznego, zaś q 1 i q 2 są zapytaniami, wówczas q 1 θ q 2 jest zapytaniem. Przykładami operatorów niealgebraicznych są: selekcja (where), projekcja lub nawigacja (.), zależne złączenie (join), kwantyfikatory, itd. Dla kwantyfikatorów zastosujemy tradycyjną składnię q 1 ( q 2 ) oraz q 1 ( q 2 ); jest ona równoważna podanej wyżej uniwersalnej składni q 1 q 2 i q 1 q 2. Jeżeli q jest zapytaniem, zaś n N, wówczas q as n jest zapytaniem. Operator as jest unarnym operatorem algebraicznym parametryzowanym nazwą n. Operator ten będziemy wykorzystywać w większości sytuacji wymagających zdefiniowania pomocniczej nazwy, w szczególności, jako zmienne korelacyjne (tzw. synonimy nazw tabel) w SQL, jako zmienne związane kwantyfikatorami, jako kursory w iteratorach, itd. Pojęcia zmiennej nie będziemy jednak wprowadzać, jest ono dla definicja naszego języka zapytań zbędne, gdyż zastępujemy je znacznie bardziej precyzyjnymi konstrukcjami semantycznymi. Jeżeli q jest zapytaniem, zaś n N, wówczas q group as n jest zapytaniem. Operator group as jest unarnym operatorem algebraicznym parametryzowanym nazwą n. Jego semantyka jest nieco inna od semantyki operatora as. Jeżeli q jest zapytaniem, to (q) jest zapytaniem. Ta reguła składniowa pozwala na dowolne używanie nawiasów. Większość nawiasów będziemy pomijać 41

42 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) przyjmując powszechne reguły priorytetu operatorów lub kolejność wykonywania operatorów od lewej do prawej. 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. Jeżeli q 1, q 2, q 3 są zapytaniami, to if q 1 then q 2 else q 3 jest zapytaniem. Zapytanie to na podstawie warunku określonego przez q 1 wybiera q 2 lub q 3. Konstrukcje zapytań będą podlegać ograniczeniom typologicznym. Poza tymi ograniczeniami wszelkie kombinacje zapytań i operatorów są dozwolone. Przykładowe zapytania zgodne z powyższą składnią są następujące: 8000 Zar Osoba 7+1 Zar > 6000 Prac where (Zar > 5200) (Prac where Zar <3000).(Zar + x /y) ((Prac as p) join (p.pracujew.dział as d)).(p.nazwisko, d.nazwa) Wśród algebraicznych operatorów binarnych mogą znaleźć się operatory sumy bagów ( ), konkatenacji ciągów elementów, oraz iloczynu kartezjańskiego. Dla tych operatorów zastosowano składnię ze słowami kluczowymi struct, bag i sequence, dla oznaczenia tych operatorów, przy czym są one n-arne, zaś ich argumenty będą oddzielone przecinkami, tj. zapytaniami będą następujące konstrukcje: struct( q 1, q 2, q 3,...) bag(q 1, q 2, q 3,...) sequence(q 1, q 2, q 3, ) W składni struct( q 1, q 2, q 3,...) można pominąć słowo kluczowe struct. Konstrukcja struct( q 1, q 2, q 3,...) oznacza specyficzny iloczyn kartezjański bagów, bag(q 1, q 2, q 3,...) oznacza sumę bagów, zaś sequence(q 1, q 2, q 3,...) oznacza konkatenację sekwencji. Stos rezultatów (QRES) Tak jak w większości języków, wszelkie pośrednie i końcowe rezultaty zapytań (wyrażeń) są odkładane na stosie rezultatów, który będziemy oznaczać QRES (ang. 42

43 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) Query REsult Stack). Stos rezultatów jest prostym uogólnieniem stosu arytmetycznego występującego w implementacji języków programowania. Rys.3.4 ilustruje działanie stosu rezultatów dla prostego wyrażenia arytmetycznego. Wyrażenie Odwrotna notacja polska (3 * ((1 + 3) / 2)) / * / * Rys.3.4. Stany stosu rezultatów dla prostego wyrażenia Po tych operacjach wierzchołek stosu rezultatów zawiera obliczony rezultat wyrażenia. Operacje na stosie arytmetycznym odpowiadają nawiasowaniu wyrażenia. Stos rezultatów dla języka zapytań będzie opierał się o identyczną zasadę jak zilustrowana powyżej, z następującymi różnicami: Elementami stosu będą rezultaty zapytań opisane wcześniej. Stos przechowuje także wszelkie pomocnicze elementy niezbędne do obliczania zapytań, w szczególności liczniki pętli iteracyjnych implikowanych przez operatory działające na kolekcjach. Stos QRES jest abstrakcyjną strukturą danych obsługiwaną przez cztery operatory: push (włóż nowy element na wierzchołek stosu), pop (zdejmij jeden element z wierzchołka stosu), top (odczytaj wierzchołek stosu), empty (sprawdź czy stos jest pusty). Operacje na stosie odbywają się zawsze na jego wierzchołku (uwzględniają jeden lub dwa wierzchołkowe elementy). Dostęp do pozostałych elementów stosu będzie możliwy dopiero wtedy, gdy zdejmiemy pewną liczbę elementów z jego wierzchołka. Rys.3.5 przedstawia przykładowy stan stosu rezultatów w podejściu stosowym. 43

44 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) Rys.3.5. Przykładowy stan stosu rezultatów QRES Ogólna architektura mechanizmu przetwarzania zapytań Rys.3.6. Architektura mechanizmu przetwarzania zapytań Architektura mechanizmu przetwarzania zapytań jest przedstawiona na Rys.3.6. Ewaluacja zapytań wykorzystuje skład oraz stos środowisk ENVS, wkładając rezultaty ewaluacji na stos rezultatów QRES. Stos środowisk jest zapełniany na podstawie składu oraz aktualnie przetwarzanego rezultatu znajdującego się na stosie QRES. Obydwa stosy odwołują się do składu obiektów trwałych i ulotnych poprzez referencje do obiektów. Semantyka operacyjna języka zapytań jest definiowana poprzez rekurencyjną procedurę, którą nazwaliśmy eval. Argumentem tej procedury jest dowolne zapytanie, zaś wynikiem jest rezultat tego zapytania włożony na wierzchołek stosu rezultatów QRES. Procedura eval korzysta ze składu obiektów, ENVS oraz QRES. Dla zapytań bez efektów ubocznych (nie wywołujących metod lub funkcji, które mogą zmienić stan), zachowane będą następujące założenia: Procedura eval nie zmienia stanu składu obiektów. Procedura eval w trakcie ewaluacji zapytania q może zmieniać stan ENVS, ale po zakończeniu ewaluacji q stan ten będzie dokładnie taki sam, jak przed rozpoczęciem ewaluacji. Procedura eval w trakcie ewaluacji zapytania q nigdy nie zmienia tej części stosu rezultatów, którą zastała w momencie rozpoczęcia ewaluacji. Może wkładać na ten stos i zdejmować wiele różnych elementów, ale po zakończeniu 44

45 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) ewaluacji stos ten będzie posiadać dokładnie jeden element więcej niż przed ewaluacją, tym elementem będzie rezultat zapytania q, co jest przedstawione na Rys.3.7. Procedura eval jest wspomagana przez analizator gramatyczny (parser), który dokonuje hierarchicznego rozbioru gramatycznego zapytania na podzapytania oraz operatory łączące podzapytania. Następnie, każde podzapytanie zwróci swój rezultat, zaś rezultat całego zapytania jest obliczany na podstawie rezultatów zwróconych przez jego składowe podzapytania. Ta własność składni i semantyki nosi nazwę modularności lub kompozycyjności. rezultat zapytania q poprzedni stan QRES eval( q ) poprzedni stan QRES Rys.3.7. Rezultat działania procedury eval na stosie rezultatów QRES Rezultaty zapytań znajdujące się na stosie rezultatów są konsumowane przez operatory danego języka lub interfejsu, dla których zapytania były parametrami. Takim operatorem może być znany z SQL operator delete oznaczający usunięcie danych, np.: delete Prac where Nazwisko = Nowak Proces wykonania tego polecenia oznacza ewaluację przez procedurę eval zapytania: Prac where Nazwisko = Nowak, w wyniku czego na wierzchołku stosu rezultatów pojawi się wynik tej ewaluacji w postaci referencji do obiektu Nowaka (tj. jego identyfikatora). Następnie operator delete przechwyci ten rezultat, oraz dokona usunięcia go ze stosu. Elementarnym zapytaniem w języku SBQL może być leksykalny reprezentant wartości l L lub zewnętrzna nazwa n N. Jeżeli zapytanie jest literałem l L to procedura eval wkłada odpowiadającą mu wartość atomową l V na wierzchołek stosu QRES. Jeżeli zapytanie jest zewnętrzną nazwą n N, to procedura eval dokonuje wiązania tej nazwy na ENVS (funkcja bind), a następnie wynik tego wiązania wkłada na wierzchołek stosu QRES. W pozostałych przypadkach procedura eval dokonuje rozbioru gramatycznego celem wyodrębnienia z każdego zapytania jednego lub dwóch podzapytań oraz 45

46 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) operatora, który jest zastosowany do tych podzapytań. Następnie oblicza rezultaty tych podzapytań (w kolejności określonej operatorem, dla niektórych operatorów - wielokrotnie) i łączy je w rezultat całego zapytania zgodnie z łączącym je operatorem. Ten proces jest rekurencyjny, tj. podzapytania są rozkładane na podpodzapytania, itd. aż do podanych wyżej przypadków zapytań elementarnych. Operatory łączące zapytania dzielimy na algebraiczne i niealgebraiczne, przy czym istotą podejścia stosowego są operatory niealgebraiczne. Główną różnica pomiędzy operatorami algebraicznymi i niealgebraicznymi jest ich stosunek do stosu środowisk. Operatory algebraiczne nie używają ENVS: działanie takiego operatora dotyczy wyłącznie stosu QRES (zwykle jego jednego lub dwóch wierzchołkowych elementów). Natomiast operatory niealgebraiczne, oprócz działań na QRES, bezpośrednio odwołują się do konstrukcji i operacji zachodzących na ENVS. Operatory algebraiczne Cechą podstawową dowolnej algebry jest m.in. to, że w wyrażeniu x 1 x 2 (gdzie jest operatorem algebry) kolejność ewaluacji argumentów x 1 oraz x 2 tego operatora nie ma znaczenia. Jest to zasadnicza różnica w porównaniu do operatorów niealgebraicznych. W matematycznym (denotacyjnym) sformułowaniu wynik zapytań (q 1 ) oraz q 1 q 2 można zapisać jako: wynik( (q 1 ) ) = ( wynik( q 1 ) ) wynik( q 1 q 2 ) = wynik( q 1 ) wynik( q 2 ) Funkcja wynik jest parametryzowana stanem (składem obiektów oraz stosem środowisk), ale nie zmienia tego stanu. Języki zapytań wprowadzają wiele operatorów, które można określić jako algebraiczne. Istnieje nieco rozmyta granica pomiędzy operatorami wbudowanymi w dany język (zwanymi operatorami generycznymi), a operatorami bibliotecznymi, czyli funkcjami dobudowanymi na wierzchołku danego języka. Liczba operatorów wbudowanych powinna być minimalna. Prawie wszystkie operatory powinny wchodzić w skład bibliotek, które nie należą do definicji języka, lecz uzupełniają pewien jego aspekt lub dziedzinę zastosowań. Typowe operatory algebraiczne: Generyczny operator sprawdzenia równości, oznaczany zwykle =, oraz operator odwrotny oznaczany, np. Nazwisko = Nowak, x = y, Zarobek 3000, itd. Operatory te są zwykle zdefiniowane dla wszystkich typów wprowadzanych w danym języku, włączając liczby całkowite, liczby rzeczywiste, stringi, struktury, zbiory, sekwencje, identyfikatory obiektów, itd. 46

47 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) Porównania i operatory dotyczące liczba całkowitych i rzeczywistych: <,, >,, +, -, *, /. Np. Zarobek < 2000, 2+6, -(glebokosc + x), itd. Funkcje liczbowe: część całkowita liczby, zaokrąglenie liczby, wartość bezwzględna liczby, sinus, kosinus, tangens, kotangens, logarytm dziesiętny, logarytm naturalny, funkcja wykładnicza, pierwiastek kwadratowy, itd.; Np. sqrt( x 2 + y 2), sin(x+45), itd. Porównania, operatory i funkcje na stringach znaków: porównanie na porządek leksykograficzny stringów, zawieranie się stringów, obcięcie stringów, konkatenacja stringów, zamiana wszystkich liter na wersaliki i odwrotnie, określenie długości łańcucha znakowego, itd. Porównanie, operatory i funkcje na datach i czasie godzinowym: porównanie dat, porównanie czasu, zmiana czasu na liczbę sekund, itd. Funkcje arytmetyczne zagregowane: sum (suma liczb), avg (średnia), min (liczba minimalna), max (liczba maksymalna), itd. Argumentem takiej funkcji jest kolekcja liczb, zaś wynikiem - pojedyncza liczba. Funkcja zliczająca liczbę elementów w kolekcji (w SQL - count), funkcja usuwająca duplikaty z kolekcji (w SQL - distinct), funkcja sprawdzająca, czy kolekcja nie jest pusta (w SQL - exists), która zwraca true, jeżeli kolekcja będąca jej argumentem nie jest pusta, oraz false w przeciwnym wypadku. Przykłady: count(prac) distinct(prac.zar) exists(prac where PracujeW.Dział.Nazwa = Produkcja ) Konstruktory wartość złożonych: zmiana pewnej liczby wartości na strukturę z etykietowanymi polami, zmiana pewnej liczby wartości na bag (lub sekwencję), itd. Operatory zmiany typu i rzutowania (koercje). Operatory mnogościowe na strukturach i kolekcjach (suma, iloczyn, różnica, zawieranie). Operatory definiowania nazwy pomocniczej (as i group as). Operatory niealgebraiczne Istotą podejścia stosowego są operatory niealgebraiczne. Do nich należą: operator selekcji (where), operator nawigacji (.), kwantyfikatory ogólny i egzystencjalny, zależne złączenie (join), operator sortowania kolekcji i inne. Wszystkie operatory są binarne. Mimo podobieństwa do operatorów algebraicznych, semantyka operatorów niealgebraicznych nie da się w prosty sposób sprowadzić do algebry. W przypadku tych 47

48 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) operatorów stan stosu środowisk zmienia się podczas ewaluacji zapytania, ale po jej zakończeniu jest taki sam, jak przed rozpoczęciem ewaluacji (dla zapytań bez efektów ubocznych). W matematycznym (denotacyjnym) sformułowaniu wynik zapytania q 1 θ q 2 możemy zapisać w postaci wzoru: wynik( q 1 θ q 2, ENVS ) = U θ połącz θ ( e, wynik( q 2, push( ENVS, nested(e))) e wynik( q 1, ENVS ) Podane wyrażenie jest parametryzowane stanem składu obiektów poprzez funkcję nested. Semantyka ewaluacji zapytania q 1 θ q 2 sprowadza się do wykonania następujących czynności: 1. Dokonaj ewaluacji zapytania q 1. Zapytanie to zwraca kolekcję (bag) elementów. 2. Dla każdego elementu e należącego do wyniku q 1 wykonaj następujące czynności: Oblicz wartość funkcji nested( e ). Wynik jest zbiorem binderów. Włóż obliczony zbiór binderów jako nową sekcje na wierzchołek stosu środowisk ENVS. Dokonaj ewaluacji zapytania q 2 w tym nowym środowisku. Oblicz wynik pośredni dla danego elementu e poprzez połączenie e z wynikiem zwróconym przez q 2. Funkcja łącząca połącz zależy od rodzaju operatora niealgebraicznego θ. Usuń nowo wstawioną sekcję ze stosu środowisk. 3. Zsumuj wszystkie wyniki pośrednie w wynik końcowy. Sposób sumowania sumuj ( U ) zależy od rodzaju operatora θ. Operatory niealgebraiczne mają więc bardzo podobną semantykę i różnią się między sobą wyłącznie sposobem obliczania wyniku pośredniego (funkcja połącz) oraz sposobem sumowania wyników pośrednich w wynik końcowy (funkcja sumuj). Definicje najważniejszych operatorów są następujące: 1. Operator selekcji (where): składnia: q 1 where q 2 ograniczenie: pod-zapytanie q 2 zwraca wartość prawda (true) lub fałsz (false) funkcja połącz: dla danego e należącego do wyniku q 1 zwraca jednoelementowy bag{e} w przypadku, gdy dla tego e pod-zapytanie q 2 zwróciło prawda, lub pusty bag{ }, jeżeli pod-zapytanie q 2 zwróciło fałsz funkcja sumuj: sumuje (mnogościowo) wszystkie wyniki pośrednie przykład: Prac where (Zar > 1000) 48

49 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) 2. Operator nawigacji (kropka): składnia: q 1. q 2 funkcja połącz: ignoruje e, zwraca wynik pod-zapytania q 2 funkcja sumuj: sumuje (mnogościowo) wszystkie wyniki pośrednie przykład: Prac.Zar 3. Operator zależnego złączenia (join): składnia: q 1 join q 2 funkcja połącz: zarówno e jak i każdy element e 2 zwracany przez q 2 traktuje jako struktury (jednoelementowe lub wieloelementowe); dla każdego e 2 zwracanego przez q 2 tworzy strukturę poprzez połączenie e oraz e 2 ; wynikiem pośrednim jest kolekcja wszystkich takich struktur funkcja sumuj: sumuje (mnogościowo) wszystkie wyniki pośrednie przykład: Prac join Zar 4. Operator-kwantyfikator egzystencjalny ( ): składnia: q 1 (q 2 ) lub q 1 q 2 ograniczenie: pod-zapytanie q 2 zwraca wartość prawda (true) lub fałsz (false) funkcja połącz: ignoruje e, zwraca wynik pod-zapytania q 2 funkcja sumuj: zwraca prawda jeżeli co najmniej jeden wynik pośredni zwrócony przez q 2 jest prawda; w przeciwnym przypadku zwraca fałsz przykład: Prac (Zar > 1000) 5. Operator-kwantyfikator ogólny ( ): składnia: q 1 (q 2 ) lub q 1 q 2 ograniczenie: pod-zapytanie q 2 zwraca wartość prawda (true) lub fałsz (false) funkcja połącz: ignoruje e, zwraca wynik pod-zapytania q 2 funkcja sumuj: zwraca fałsz jeżeli co najmniej jeden wynik pośredni zwrócony przez q 2 jest fałsz; w przeciwnym przypadku zwraca prawda przykład: Prac (Zar > 1000) Jak widać podana koncepcja operatorów nie-algebraicznych w SBQL jest w gruncie rzeczy bardzo prosta, posiada dobrze ugruntowane korzenie w semantyce języków programowania, oraz może podlegać rozszerzeniom (m.in. w celu zdefiniowania takich własności jak klasy, dziedziczenie i hermetyzacja). Na Rys.3.8 przedstawiono działanie operatora where dla przykładowego zapytania ewaluowanego na stanie bazy danych przedstawionym na Rys

50 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) Rezultat zwracany przez zapytanie Prac (wiązanie Prac) Iteracja po elementach e poprzedniego rezultatu: na ENVS wkłada si ę nested (e) Rezultat Rezultat zwracany dereferencji przez wymuszanej zapytanie Zar przez (wiązanie Zar) operator > Rezultat zwracany przez zapytanie 1000 Rezultat zwracany przez zapytanie Zar>1000 Końcowy rezultat zapytania i 1 Nazwisko(i 2 ) Zar(i 3 ) PracujeW (i 4 ) Prac(i 1 ) Prac(i 5 ) Prac(i 9 ) Dział(i 17 ) Dzia ł(i 22 ) i prawda i 1 Stan stosu ENVS przed ewaluacj ą i 5 Nazwisko(i 6 ) Zar(i 7 ) PracujeW (i 8 ) Prac(i 1 ) Prac(i 5 ) Prac(i 9 ) Dział(i 17 ) Dzia ł(i 22 ) i prawda i 5 Prac(i 1 ) Prac(i 5 ) Prac(i 9 ) Dział(i 17 ) Dzia ł(i 22 ) i 9 Nazwisko(i 10 ) Zar(i 11 ) Adres(i 12 ) PracujeW (i 16 ) Prac(i 1 ) Prac(i 5 ) Prac(i 9 ) Dział(i 17 ) Dzia ł(i 22 ) i fałsz Prac where ( Zar > 1000 ) Rys.3.8. Kroki ewaluacji zapytania: Prac where (Zar > 1000) 3.5 Statyczna ewaluacja zapytania i kontrola typologiczna W celu wykrycia potencjalnych niezgodności typologicznych oraz uzupełnienia drzewa syntaktycznego zapytania dodatkowymi niejawnymi operacjami oraz informacjami ułatwiającymi optymalizację zapytań wykonywana jest statyczna ewaluacja zapytania połączona z mechanizmem półmocnej kontroli typologicznej. Niniejszy podrozdział streszcza zagadnienia dotyczące tych procesów opisane dokładnie w [Sub04] i [Ste06]. Półmocna kontrola typów polega na dokładnej symulacji rzeczywistych obliczeń czasu wykonania zapytania, która korzysta z metabazy (opisanej w rozdziale 3.3) i przebiega na statycznych odpowiednikach stosów ENVS i QRES. Są to obliczenia wykonywane na tzw. sygnaturach, które opisują wartości przetwarzane w czasie wykonania. W miejsce rezultatów zapytań stosy statyczne zawierają więc sygnatury tych rezultatów. Lista prawidłowych sygnatur jest ograniczona i definiowana rekurencyjnie podobnie jak lista możliwych rezultatów. Sygnaturą może być: każdy typ elementarny, np. int, real, bool, itp.; każda referencja do węzła grafu schematu; każdy literał (opcja ta jest potrzebna dla typów wyliczeniowych (ang. enumeration) określanych niżej jako variant); 50

51 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) para n(x), gdzie x jest sygnaturą, zaś n N (taką nazwaną sygnaturę będziemy nazywać statycznym binderem; struct{ x 1, x 2, }, gdzie x 1, x 2,... są sygnaturami; variant{ x 1, x 2, }, gdzie x 1, x 2,... są sygnaturami (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); bag{x}, gdzie x jest sygnaturą; sequence{x}, gdzie x jest sygnaturą. Sygnaturą mogą być dodatkowo wyposażone w atrybuty, np. informację o liczności obiektów opisywanych daną sygnaturą. Każda z powyższych sygnatur może pojawić się jako element statycznego stosu S_QRES. Sekcja przechowywana na statycznym stosie S_ENVS jest zbiorem statycznych binderów. Bindery te mogą przyjmować następującą postać: n(typ): ten statyczny binder odpowiada sytuacji, gdy na stosie ENVS ma pojawić się binder n(v), gdzie n N, v 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 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), gdzie sygn jest sygnaturą: odpowiada to sytuacji, gdy na stosie ENVS ma pojawić się binder złożony n(rezultat), gdzie rezultat jest wynikiem dowolnego zapytania. Postać ta generalizuje dwie poprzednie. Analogicznie, sygnatury przechowywane na stosie S_QRES odpowiadają zdefiniowanemu wcześniej zbiorowi rezultatów, gdzie zamiast wartości v podstawiamy odpowiedni typ tego v, 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. Sygnatura przykładowego rezultatu zapytania zaprezentowanego w rozdziale 3.2 ma postać: bag{struct{dział(i Dział ), Prac(bag{struct{n(string), s(i Zar )}})}} Statyczną ewaluacją drzewa syntaktycznego zapytania zajmuje się się funkcja static_eval symulująca wykonanie zapytania na stosach S_ENVS i S_QRES. Efektem działania tej funkcji jest: wyznaczenie sygnatury wyników zapytania; przypisanie numerów będących rozmiarem stosu do wszystkich operatorów niealgebraicznych, oraz przypisanie par numerów <wysokość stosu, nr sekcji 51

52 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) wiązania> do tych nazw występujących w zapytaniu, które będą przedmiotem wiązania na stosie ENVS. Funkcja static_eval podczas analizy operatorów niealgebraicznych umieszcza kolejne bindery statyczne na stosie S_ENVS. W tym celu wywołuje statyczną funkcję nested. Wyniki i zmiany dokonane w drzewie syntaktycznym podczas wykonania funkcji static_eval pozwalają zatem na późniejsze przepisanie zapytania przy użyciu wybranych metod optymalizacyjnych. Statyczna funkcja nested Analogicznie do funkcji nested, definiujemy static_nested, której zadaniem będzie przygotowanie sekcji na stosie S_ENVS w postaci zbioru statycznych binderów. Funkcja static_nested działa na dowolnej sygnaturze, ale jej wynik jest niepusty dla następujących przypadków: 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 bytów podrzędnych. Np. static_nested(i Prac ) = { NrP(i Nrp ), Stan(i Stan ), Zar(i Zar ), PracujeW(i PracujeW ), Kieruje(i Kieruje ), ZmieńZar(i ZmieńZar ), ZarNetto(i ZarNetto ) } 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 Szef ) = { Prac(i Prac ) } Dla sygnatury będącej statycznym binderem static_nested zwraca zbiór zawierający ten sam binder. Dla statycznych struktur: static_nested(struct{s 1,..., s k }) = static_nested(s 1 )... static_nested(s k ) Statyczna kontrola typów Sercem aparatu statycznej kontroli typów jest funkcja static_type_check. Jej argumentem jest drzewo syntaktyczne zapytania. Pobiera ona dane z metabazy, odczytuje i modyfikuje stan obu stosów statycznych S_ENVS i S_QRES. Realizuje ona następujące zadania: Kontroluje typologicznie drzewo syntaktyczne poprzez symulację na stosach S_ENVS i S_QRES działań mechanizmu ewaluacji zapytań. 52

53 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) Wyprowadza na zewnątrz komunikaty o błędach typologicznych. Jest to realizowane na podstawie tzw. reguł wnioskowania o typie zapisanych w tabelach decyzyjnych, np.: (int + float) float (dla operatora sumy) bag{t1} join bag{t2} bag{struct{t1, T2}} (dla operatora złączenia) Lewa strona takiej reguły przedstawia pewną sytuację podczas działania procedury static_type_check, natomiast prawa strona - wynik tego działania. Elementami tej arytmetyki są sygnatury. Dla dowolnego operatora algebraicznego lub niealgebraicznego wprowadzonego w języku zapytań w tabelach decyzyjnych musi być jednoznacznie przypisany zestaw takich reguł. Ustala typy literałów występujących w zapytaniu. Rozstrzyga, który z przeciążonych operatorów ma zostać użyty, np. czy w danym zapytaniu + oznacza sumę arytmetyczną, czy też złączenie łańcuchów znakowych. Dokonuje zmian drzewa syntaktycznego celem wprowadzenia tam operatorów automatycznej dereferencji oraz operatorów koercji. Dokonuje zmian drzewa syntaktycznego celem wprowadzenie do niego operatorów dynamicznej kontroli typologicznej (realizowanej w czasie wykonania zapytania) jeżeli statycznie nie można ustalić poprawności typu. Po wykryciu i zakomunikowaniu błędu typologicznego dokonuje naprawy drzewa syntaktycznego i/lub stosów statycznych (według pewnych reguł wyznaczających najbardziej prawdopodobną poprawną wynikową sygnaturę), aby umożliwić wykrycie więcej niż jednego błędu typologicznego w ramach jednego przebiegu sprawdzania kodu. Procedura static_type_check jest w typowym przypadku uruchamiana wcześniej niż static_eval, gdyż nie ma sensu optymalizować zapytania, które jest niepoprawne typologicznie i/lub nie ma dostawionych odpowiednich operatorów dereferencji lub koercji. 3.6 Przykładowa baza danych W celu zobrazowania procesu optymalizacji poprzez zapamiętywanie wyników zapytań posłużymy się przykładową obiektową bazą danych małej uczelni o schemacie przedstawionym na Rys

54 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) Person [0..*] name: string birthday: date age(): integer employs [0..*] Dept [0..*] dname: string boss [0..1] works_in Student [0..*] Emp [0..*] year: integer grades [0..*]: integer avggrade(): real receives [1..*] received_by [1..*] Training [0..*] subject: string duration: integer supervises [0..*] supervised_by job: string salary: real rating: real prev_job [0..*] company: string years: string manages [0..1] Rys.3.9. Diagram klas przykładowej obiektowej bazy danych Schemat definiuje pięć klas obiektów: Training, Student, Emp, Person, and Dept. Klasy Training, Student, Emp i Dept modelują studentów (Student) uczestniczących w zajęciach (Training), które są prowadzone przez pracowników - wykładowców (Emp) zrzeszonych w wydziałach - departamentach (Dept) oferujących te zajęcia. Klasa Person jest nadklasą klas Student i Emp. Obiekty klasy Emp mogą zawierać wiele złożonych podobiektów o nazwie prev_job (dane dotyczące poprzednich miejsc pracy pracownika). Przy nazwach klas, jak również atrybutów i powiązań (referencji), umieszczona jest w nawiasach kwadratowych dopuszczalna liczebność w bazie tych obiektów jeżeli nie została podana oznacza to dokładnie jedno wystąpienie. Symbol gwiazdki (*) oznacza nieograniczoną liczbę wystąpień. Klasa Person definiuje jedną metodę age, która zgodnie z mechanizmami dziedziczenia jest metodą obu klas specjalizowanych, przy czym klasa Student ma dodatkową metodę avggrade (zwracającą średnią ocen). Wszystkie atrybuty i metody są publiczne. 54

55 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) Obiekty: <i 1, Emp, {<i 2, name, Smith >, <i 3, birthday, >, <i 4, salary, 3500>, <i 5, job, designer >, <i 6, rating, 7.9>, <i 7, works_in, i 21 >, <i 8, supervises, i 25 >}> <i 9, Emp, {<i 10, name, White >,<i 11, birthday, >,<i 12, salary, 2900>, <i 13, job, consultant >, <i 14, rating, 5.2>, <i 15, works_in, i 21 >, <i 16, supervises, i 30 >, < i 17, supervises, i 35 >, <i 18, prev_job, {<i 19, company, Sybase >, <i 20, years, >}>}> <i 21, Dept, {<i 22, dname, Database >, <i 23, employs, i 1 >, <i 24, employs, i 9 >}> <i 25, Training,, {<i 26, subject, views >,<i 27, duration, 5>, <i 28, supervised_by, i 1 >,<i 29, received_by, i 48 >} <i 30, Training, {<i 31, subject, roles >,<i 32, duration, 3>, <i 33, supervised_by, i 9 >,<i 34, received_by,i 40 >}> <i 35, Training, {<i 36, subject, roles >,<i 37, duration, 1>, <i 38, supervised_by, i 9 >,<i 39, received_by,i 40 >}> <i 40, Student, {<i 41, name, Russell >, <i 42, birthday, >, <i 43, year, 2>, <i 44, grades, 5>, <i 45, grades, 3>, <i 46, receives, i 30 >, < i 47, receives, i 35 >}> <i 48, Student, {<i 49, name, Black >, <i 50, birthday, >, <i 51, year, 1>, <i 52, receives, i 25 >}> <i 53, ClassPerson, { <i 54, age, ( kod metody...)>}> <i 55, ClassStudent, {<i 56, avggrade, ( kod metody...)>}> <i 57, ClassEmp, {}> <i 58, ClassTraining, {}> <i 59, ClassDept, {}> INHER = {<i 55, i 53 >, <i 57, i 53 >} INST = {<i 1, i 57 >, <i 9, i 57 >, <i 21, i 59 >, <i 25, i 58 >, <i 30, i 58 >, <i 35, i 58 >, <i 40, i 55 >, <i 48, i 55 >} ROOT = {i 1, i 9, i 21, i 25, i 30, i 35, i 40, i 48 } Rys Skład danych Rys.3.10 przedstawia skład danych dla bazy o schemacie z Rys.3.9 zgodny z podejściem SBA, natomiast Rys.3.11 obrazuje ten skład w formie grafu powiązań między obiektami. 55

56 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) i 53 ClassPerson i 54 age (...code...) i 55 ClassStudent i 57 ClassEmp i 56 avggrade (...code...) i 40 Student i 41 name Russell i 43 year 2 i 49 i 48 Student name Black i 51 year 1 i 52 receives i 25 i 1 Emp i 9 Emp i 2 name Smith i 10 name White i 42 birthday i 50 birthday i i 11 birthday birthday i 44 receives i 30 i 45 receives i 35 i 46 grades 5 i 47 grades 3 i 4 salary 3500 i 5 job designer i 6 rating 7.9 i 7 works_in i 21 i 8 supervises i 25 i 12 salary 2900 i 13 job consultant i 14 rating 5.2 i 15 works_in i 21 i 16 supervises i 30 i 17 supervises i 35 i 26 i 25 Training subject views i 27 duration 5 i 31 i 30 Training subject roles i 32 duration 3 i 35 Training i 36 subject roles i 37 duration 1 i 18 prev_job i 19 company Sybase i 20 years i 28 supervised_by i 1 i 33 supervised_by i 9 i 38 supervised_by i 9 i 29 received_by i 48 i 34 received_by i 40 i 39 received_by i 40 i 22 i 21 Dept dname Database i 23 employs i 1 i 58 ClassTraining i 59 ClassDept i 24 employs i 9 Skład jest zorganizowany następująco: Rys Skład danych w formie grafu Dla każdej klasy w diagramie klas skład zawiera jeden obiekt modelujący tę klasę (zawiera on wszystkie inwarianty tej klasy, czyli np. definicje metod oraz nazwę obiektów tej klasy). Nazwa tego obiektu jest tworzona poprzez konkatenację nazwy klasy z diagramu oraz prefiksu Class. Kolejnymi obiektami składu są obiekty modelujące instancje klas z atrybutami modelowanymi jako ich podobiekty (nazwy takich obiektów są nazwami ich klas). Liczba obiektów instancji klasy jest wyznaczana w diagramie przez liczebność klasy. Dodatkowo skład zawiera obiekty przynależności obiektu do danej klasy realizowane jako pary <i o, i c >, gdzie i c jest identyfikatorem obiektu modelującego klasę, a i o jest identyfikatorem obiektu jej instancji. Na Rys.3.10 taka zależność jest umieszczana w zbiorze INST, natomiast na Rys.3.11 jako szeroka czarna strzałka. Dla każdej relacji dziedziczenia pomiędzy klasami Class1 i Class2 (gdzie Class1 jest podklasą klasy Class2), skład zawiera jedną parę <i c, i sc >, gdzie i c jest identyfikatorem obiektu modelującego klasę Class1, a i sc jest identyfikatorem obiektu modelującego klasę Class2 (na Rys.3.10 taka para 56

57 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) znajduje się w zbiorze INHER, a na Rys.3.11 jest oznaczona szeroką szarą strzałką). Powiązania (referencje) są dwukierunkowe. Dla każdego powiązania skład zawiera zbiór podobiektów linkujących, które na Rys.3.11 są wizualizowane za pomocą przerywanych czarnych strzałek. Liczebność takiej relacji zdefiniowana w diagramie wyznacza maksymalną liczbę takich podobiektów. Specjalny zbiór o nazwie ROOT zawiera identyfikatory tzw. obiektów korzeniowych, od nazw których mogą rozpoczynać się zapytania do składu i które umieszczane są w pierwszej sekcji stosu wywołań. Na Rys.3.10 i 3.11 identyfikatory obiektów korzeniowych (obiektów klas Person, Student, Emp, Training i Dept) są wyróżnione tłustą czcionką. Graf schematu, przechowywany w matabazie, właściwy dla diagramu klas z Rys.3.9 jest przedstawiony na Rys Dla przejrzystości nie uwzględniono w nim identyfikatorów węzłów grafu. Student 0..* Person 0..* Emp 0..* grades 0..* integer year 1 integer avggrade 1 void real name 1 string birthday 1 date age 1 void integer job 1 string salary 1 real rating 1 real receives 1..* supervises 0..* prev_job 0..* received_by 1..* supervised_by 1 works_in 1 employs 0..* company 1 string years 1 string Training 0..* manages 0..1 boss 0..1 subject 1 string duration 1 integer Dept 0..* dname 1 string Rys Graf schematu przykładowej bazy danych 57

58 3. Stack-Based Approach (SBA) oraz Stack-Based Query Language (SBQL) Każdy węzeł reprezentowany poprzez prostokąt jest podzielony na trzy części. Pierwsza z nich zawiera zewnętrzną nazwę obiektu (encji występującej w bazie danych), druga zaś jego liczność (dla metod oznacza ona liczbę zwracanych wartości). Trzecia część znajdująca się na rysunku na dole prostokąta węzła informuje o typie obiektu: nazwę typu dla obiektów atomowych, zbiór referencji do podobiektów obiektu złożonego, referencję do obiektu wskazywanego przez obiekt linkujący lub sygnaturę metody definiowaną jako para input output, gdzie input jest sekwencją nazw i typów parametrów tej metody (void dla pustej sekwencji), a output jest typem zwracanej wartości (void dla metod proceduralnych). Jeżeli typ parametru lub wartości zwracanej przez metodę nie będzie typem atomowym, będzie wtedy zastąpiony referencją do odpowiedniego węzła grafu definiującego ten typ. Na powyższym rysunku prostokąty z pogrubioną ramką oznaczają węzły definiujące obiekty korzeniowe. 58

59 4. Koncepcja mechanizmu zapamiętanych zapytań 4. Koncepcja mechanizmu zapamiętanych zapytań Metoda optymalizacji poprzez zapamiętywanie wyników zapytań, jakkolwiek pozornie łatwa i oczywista, rodzi szereg problemów. Jeżeli zapamiętanych zapytań będą tysiące lub miliony, wówczas konieczne jest zorganizowanie odpowiedniego rejestru zapamiętanych zapytań tak, aby umożliwić sprawne odnalezienie właściwego zapamiętanego zapytania lub stwierdzenia, że takiego zapytania w rejestrze nie ma. Zapamiętane zapytania muszą także podlegać pewnym procesom normalizacyjnym, które zapewnią ich formę niezależną od mniejszych lub większych różnic syntaktycznych i semantycznych, np. białych znaków lub przestawienia kolejności argumentów operatorów. Konieczne jest także opracowanie algorytmów ustalających, czy danego zapytanie warto zapamiętywać ze względu na jego złożoność, małe prawdopodobieństwo ponownego wystąpienia, mały zysk ze względu na niski koszt pełnej ewaluacji zapytania, i inne czynniki. Może się również zdarzyć, że warto zapamiętywać wyniki nie całych zapytań, lecz pewnych ich fragmentów. To założenie implikuje metody dekompozycji zapytania na podzapytania. Rozwiązania opisane w tym rozdziale są realizacją koncepcyjną tych zagadnień. 4.1 Architektura systemu przetwarzania zapytań Uwzględnienie optymalizacji zapytań oraz architektury klient-serwer prowadzi do nowej wizji mechanizmu przetwarzania zapytań. W wielu systemach baz danych, np. używających języka zapytań SQL, całość przetwarzania zapytań odbywa się po stronie serwera. W podejściu SBA większość zadań związanych z wykonaniem zapytania jest przesuniętych do części klienckiej, co pozwala uniknąć przeciążenia serwera wykorzystując moc obliczeniową stacji klienckich (obecnie dość silnych komputerów). Procesy optymalizacji i interpretacji wyników zapytań muszą więc być dostosowane do tej architektury podczas stosowania omawianego mechanizmu optymalizacyjnego. Podczas wykonywania zapytania najpierw przeprowadzane jest ono przez szereg optymalizatorów pracujących w części klienckiej systemu, a następnie sterowanie przenoszone jest do interpretera (również pracującego w systemie klienckim), który zwraca rezultat tego zapytania. Optymalizacja przez przepisywanie zapytań, a mechanizm zapamiętanych zapytań jest przykładem takiej optymalizacji, wymaga wprowadzenia statycznego stosu środowiskowego (statyczny ENVS, S_ENVS), 59

60 4. Koncepcja mechanizmu zapamiętanych zapytań statycznego stosu rezultatów (statyczny QRES, S_QRES) i metabazy (opisanych w rozdziale 3.5). Elementy te są używane podczas analizy statycznej drzewa syntaktycznego zapytania celem jego przekształcenia do innej, semantycznie równoważnej postaci rokującej lepszy czas wykonania. Interpreter podczas ewaluacji zapytania korzysta z lokalnych dynamicznych struktur ENVS i QRES oraz innych danych lokalnych. Składowe tej architektury przetwarzania zapytań zostały bardziej szczegółowo opisane w poprzednim rozdziale. Optymalizacja poprzez zapamiętane zapytania wymaga dodatkowo dostępu do rejestru zapamiętanych zapytań znajdującego się na serwerze i zarządzanego przez mechanizmy serwera bazy danych. Głównym elementem jest tutaj menadżer zapamiętanych zapytań wykorzystujący system zarządzania obiektami znajdującymi się w bazie w celu pobierania i tworzenia nowych zapamiętanych zapytań w rejestrze. Poza tym optymalizacja ta wymaga dostępu do innych metadanych znajdujących się na serwerze. Podczas optymalizacji konieczna jest więc komunikacja z serwerem bazy danych w celu przeszukania rejestru zapytań oraz ewentualnie sygnalizowania potrzeby utworzenia nowych. W kontekście mechanizmu wykorzystania zapamiętanych zapytań, interpreter komunikuje się również z częścią serwerową pobierając zapamiętane wyniki i wysyłając obliczone wyniki dla nowych bądź wymagających aktualizacji zapytań. Architektura uwzględniająca te elementy jest przedstawiona na Rys.4.1. Środowisko rozwoju oprogramowania (edytor, kompilator,...) 1 Parser zapytań/programów 2 Klient 3 Statyczny ewaluator typologiczny Drzewo syntaktyczne zapytania/programu 4 Optymalizator przepisujący 6 Interpreter zapytań/programów Statyczny ENVS Optymalizator cache'ujący ENVS Statyczny QRES QRES 5 7 Dane lokalne Serwer Menadżer zapamiętanych zapytań Baza danych: metabaza, trwałe obiekty, indeksy, rejestr zapamiętanych zapytań,... Zarządzanie obiektami Przetwarzanie trwałych abstrakcji (transakcji, procedur,...) Rys.4.1. Architektura uwzględniająca przetwarzanie i optymalizację zapytań/programów 60

61 4. Koncepcja mechanizmu zapamiętanych zapytań W szczególności proces optymalizacji poprzez zapamiętane zapytania odbywa się wg następującego scenariusza: 1. Aplikacja przesyła zapytanie do interfejsu bazy danych po stronie klienckiej. 2. Parser transformuje je do postaci drzewa syntaktycznego (ang. syntactic tree). 3. Zapytanie jest ewaluowane statycznie pod kątem poprawności typologicznej z użyciem statycznych stosów ENVS i QRES oraz schematu bazy danych zapisanego w metabazie znajdującej się na serwerze. Po udanej ewaluacji węzły drzewa wzbogacane są o sygnatury typów stanowiące podstawę dalszego wnioskowania optymalizatora. 4. Przetworzone drzewo zapytania przekazywane jest do serii optymalizatorów, a wśród nich optymalizatorów przepisujących znajdujących się po stronie klienckiej systemu. 5. Jednym z nich jest optymalizator cache'ujący 1 (ang. cache optimizer), który modyfikuje je używając pewnych optymalizacyjnych reguł przepisywania i transformuje, jeżeli jest to konieczne, do znormalizowanej postaci. Optymalizator ten korzysta z rejestru zapamiętanych wyników zapytań (ang. query cache registry) zapisanych po stronie serwera. Komunikuje się w tym celu z menadżerem zapamiętanych zapytań (ang. cache manager), analizując drzewo zapytania pod kątem wykorzystania zbioru zapytań zapamiętanych w rejestrze zapytań. Optymalizator wyszukuje wśród drzew syntaktycznych zapamiętanych w rejestrze zapytań pasujących do optymalizowanego zapytania i ewentualnie, po znalezieniu, zastępuje całe lub część drzewa zapytania odwołaniem do specjalnej funkcji cache'ującej (ang. cache function) parametryzowanej informacją pozwalającą jednoznacznie określić wybrane (dopasowane) zapamiętane zapytanie. Optymalizator powtarza zastępowanie aż do momentu, gdy nie ma już możliwości wykorzystania kolejnego zapamiętanego zapytania. Optymalizator analizuje również końcowe zapytanie pod kątem jego przyszłej przydatności jako zapytania zapamiętanego. Jest ono, o ile to możliwe, wirtualnie dekomponowane na wiele prostszych zależnych lub niezależnych zapytań, które podlegają dalszej analogicznej analizie i w ostateczności są specjalnie oznaczane jako pozycje do zapamiętania w rejestrze zapytań. Może być to zrealizowane poprzez zastąpienie takiego podzapytania wywołaniem funkcji cache'ującej z nowym, przygotowanym przez menadżera rejestru zapytań, parametrem jednoznacznie identyfikującym to zapytanie. Takie zapytanie nie jest jednak jeszcze na tym etapie pełnym zapamiętanym zapytaniem, gdyż nie zostały jeszcze wyliczone jego wyniki. Musi zostać ono zatem specjalnie oznaczone jako takie w rejestrze zapytań. 1Ze względu na brak w języku polskim odpowiedniego zwięzłego, a jednocześnie jednoznacznego określenia dla zapisywania danych w pamięci podręcznej, zastosowano w pracy mieszaną formę przymiotnika cache'ujący. 61

62 4. Koncepcja mechanizmu zapamiętanych zapytań 6. Zmodyfikowane przez optymalizator, przepisane drzewo syntaktyczne jest kompilowane do postaci gotowej do wykonania w postaci planu wykonania zapytania (ang. query evaluation plan) i przekazywane do interpretera. 7. Interpreter wykonuje skompilowany plan, przy czym każde wywołanie funkcji cache'ującej, które pojawi się w takim planie powoduje natychmiastowe wstawienie zapamiętanych w rejestrze wyników skojarzonego z tym wywołaniem zapamiętanego zapytania. Po ewaluacji całego zapytania interpreter przekazuje wyniki użytkownikowi. W trakcie przetwarzania wszystkie podzapytania wcześniej oznaczone przez optymalizator (tzw. niepełne - zapisane w rejestrze bez wyników) są wiązane z ich wynikami, przekazywane na serwer do rejestru zapytań w celu zapamiętania oraz oznaczane jako pełnoprawne zapamiętane zapytania do wykorzystania podczas optymalizacji kolejnych zapytań. Menadżer rejestru zapytań generuje dla każdego zapamiętywanego zapytania dodatkowe dane opisujące podschemat bazy danych związany z obszarem działań tych zapytań. Jest to realizowane na podstawie sygnatur elementów drzewa syntaktycznego zapytania w kontekście metabazy i ma na celu zgromadzenie niezbędnych informacji dla wydajnego utrzymania aktualności wyników zapamiętanych zapytań po zmianach w bazie danych. Poza aktualizacją wyników zapytań, rejestr zajmuje się również gospodarką zasobami dostępnymi dla pamięci podręcznej poprzez statystyczne analizy użycia wyników wybranych zapytań, a co za tym idzie, usuwanie tych najrzadziej wykorzystywanych. Powyższy scenariusz w sposób ogólny prezentuje cykl życia zapamiętanych zapytań wykorzystywanych przez optymalizator cache'ujący. Poszczególne etapy tego cyklu zostały szczegółowo opisane w kolejnych podrozdziałach tego rozdziału oraz w następnym rozdziale dotyczącym aktualizacji zapamiętanych wyników. 4.2 Menadżer zapamiętanych zapytań W poprzednim podrozdziale (punkty 5. i 7.) jako jeden z najważniejszych elementów architektury omawianej metody optymalizacyjnej wymieniony został menadżer zapamiętanych zapytań. W niniejszej pracy występuje on alternatywnie również pod nazwami menadżera pamięci podręcznej wyników lub menadżera rejestru zapytań. Jest to moduł pracujący w ramach serwera bazy danych umożliwiający realizację wszelkich operacji na zapamiętanych zapytaniach i ich wynikach zarówno podczas optymalizacji zapytania jak i jego ewaluacji przez interpreter zapytań. Menadżer ten wykonuje również dodatkowe działania będące skutkiem pracy interpretera, takie jak aktualizacja wyników zapytań i optymalizacja zasobów sprzętowych przydzielonych dla rejestru wyników (cache'u). 62

63 4. Koncepcja mechanizmu zapamiętanych zapytań Rejestr wyników zapytań Jednym z kluczowych elementów opisywanej metody optymalizacyjnej jest sprawne wyszukanie zapytania w rejestrze zapytań zakładając, że zapamiętanych zapytań może być bardzo dużo. Niemniej istotna jest również szybkość wstawiania nowego zapytania oraz usunięcia go z rejestru. Najskuteczniejsze są w tej sytuacji struktury indeksowe oparte na pojedynczym kluczu wyszukiwania, np. B+-drzewa [Knu73, Com79] lub tablice haszujące [Knu73, Lit80]. Wyszukiwanie z pojedynczym kluczem jest łatwo implementowalne i uniezależnia szybkość tego procesu od rodzaju zindeksowanego zapytania czas wyszukiwania jest zawsze taki sam niezależnie od złożoności zapytania. Taki pojedynczy klucz może być zorganizowany na wiele sposobów, np. jako znormalizowane drzewo syntaktyczne zakodowane do postaci tekstowej za pomocą odwrotnej notacji polskiej. Dla proponowanego rozwiązania wybrana została najprostsza postać tekstowa reprezentacja zapytania budowana na podstawie znormalizowanego i zdekomponowanego drzewa syntaktycznego zapewniającego jednoznaczność semantyczną zapytania. Sposoby normalizacji i dekompozycji zapytania zostały opisane w dalszych rozdziałach pracy. Ze względu na konieczność minimalizacji objętości indeksu zapytań umożliwiającą wydajną modyfikację takiego indeksu wraz z tekstową postacią zapytania zapamiętane są tylko odwołania (identyfikatory) do pozostałych elementów wchodzących w skład rejestru zapytań zawierających dane skojarzone z zapamiętanym zapytaniem. W szczególności w opracowanym rozwiązaniu w indeksie zapamiętanych zapytań wraz z indeksowaną tekstową formą zapytania został umieszczony identyfikator węzła metabazy reprezentującego to zapytanie. Taka organizacja była możliwa dzięki zastosowaniu do przechowywania wyników zapytań standardowego mechanizmu składowania danych w bazie zapytanie traktowane jest jak każdy inny obiekt bazy danych. Ma więc swoją nazwę będącą nazwą funkcji cache'ującej odpowiednio parametryzowanej, typ będący typem jego wyników oraz odpowiednie identyfikatory w bazie i matabazie nadawane standardowo przez system zarządzania obiektami. Metabaza ma tutaj istotne znaczenie ze względu na konieczność zapewnienia niezmienności mechanizmu kontroli typologicznej zoptymalizowanego zapytania (więcej o tym w opisie samej optymalizacji). Dla każdego zapamiętanego w rejestrze zapytania tworzone są przez menadżera trzy elementy: 1. Obiekt w bazie danych (węzeł danych zapamiętanego zapytania) przechowujący: oryginalne zapytanie w postaci umożliwiającej jego ewaluację (np. skompilowany plan ewaluacji zapytania); zapamiętane wyniki zapytania; statystyki użycia zapytania (liczniki użycia i aktualizacji wyników); 63

64 4. Koncepcja mechanizmu zapamiętanych zapytań flaga informująca o konieczności ponownej ewaluacji zapytania przy najbliższej próbie jego wykorzystania; dodatkowe struktury przechowujące informacje o zakresie danych, którego dotyczy zapytanie (generowane na podstawie drzewa syntaktycznego podczas umieszczania zapytania w rejestrze; potrzebne dla celów aktualizacji wyników po zmianach w bazie, opisane dokładniej w rozdziale 5); 2. Wpis w metabazie (węzeł metadanych zapamiętanego zapytania) zawierający: sygnaturę typu wyników zapytania; informację o miejscu przechowywania danych zapamiętanego zapytania (pamięć trwała, ulotna) więcej o tym dalej; identyfikatory węzłów danych tego zapytania w pamięci trwałej i ulotnej; 3. Wpis w indeksie zapamiętanych zapytań będącym dodatkową strukturą przechowywaną w rejestrze indeksującą metodą listy haszującej tekstowe formy zapytań z przypisanymi im identyfikatorami węzłów metadanych zapewniającymi szybki dostęp do danych zapamiętanego zapytania (struktura ta została opisana wcześniej). Menadżer zapamiętanych wyników zarządza bezpośrednio tylko indeksem rejestrowym, natomiast pozostałymi elementami zajmują się dwa moduły pomocnicze: Menadżer metadanych - odpowiedzialny za metadane zapamiętanych zapytań; Menadżer danych - odpowiedzialny za właściwe dane zapytań. Operacje związane z zapamiętanymi wynikami zapytań w ramach opisanych dalej mechanizmów optymalizacji i interpretacji są zawsze wykonywane za pośrednictwem jednego z modułów menadżera zapamiętanych zapytań. W celu zwiększenia wydajności dostępu do zapamiętanych wyników w miejsce zapisywania tych wyników w bazie danych (pamięci trwałej), możliwe jest przechowywanie ich w pamięci operacyjnej (ulotnej). Pamięć trwała umożliwia wykorzystanie rejestru zapamiętanych zapytań po ponownym uruchomieniu systemu, ale dostęp do niej jest stosunkowo czasochłonny. Pamięć ulotna zaś cechuje się bardzo szybkim dostępem. Wyniki zapytania mogą być zatem umieszczane tylko w pamięci trwałej, tylko ulotnej lub jednocześnie redundantnie w obu miejscach. Administrator powinien decydować o trybie zapisywania danych w rejestrze zapytań. Parametry konfiguracyjne menadżera pamięci podręcznej mogą być zdublowane i ustawiane niezależnie dla każdego miejsca przechowywania (dla pamięci trwałej i ulotnej), dzięki czemu możliwe jest umieszczanie w pamięci ulotnej wyników większości przetwarzanych zapytań, natomiast w pamięci trwałej tylko tych rokujących późniejsze wykorzystanie i rzadko (lub łatwo) aktualizowanych. Oba miejsca przechowywania danych zawierają analogiczny skład danych, więc ich wykorzystanie równoległe nie wymaga odmiennych algorytmów wykorzystywanych w ramach Menadżera danych. Wzbogacenie węzła metadanych zapamiętanego zapytania informacją o miejscu składowania danego zapytania umożliwia więc Menadżerowi 64

65 4. Koncepcja mechanizmu zapamiętanych zapytań danych i Menadżerowi metadanych wykorzystanie analogiczne zarówno pamięci trwałej jak i ulotnej zgodnie z konfiguracją systemu. Szczegółowy opis zadań rejestru zapamiętanych wyników został przedstawiony w rozdziałach 4.3 i 4.4, gdzie przedstawiono funkcjonalność rejestru w rozbiciu na funkcje biorące udział odpowiednio w samej optymalizacji zapytań i ich ewaluacji z wykorzystaniem zapamiętanych wyników Statystyki dotyczące zapamiętanych zapytań W celu optymalizacji dostępnych zasobów menadżer zapamiętanych zapytań utrzymuje dodatkowo liczniki statystyczne zliczające odpowiednio użycia oraz aktualizacje wyników każdego zapytania oraz czasy ostatniego wystąpienia takich zdarzeń dla danego zapytania. Wykorzystane tutaj mogą być algorytmy oparte na listach LRU/MRU (ang. least/most recently used). Listy te, utrzymywane osobno dla zapytań zapisanych w pamięci trwałej i ulotnej, są listami priorytetowymi poziomów użycia zapytań, gdzie przez poziom rozumiany jest zakres liczbowy liczników użyć zapamiętanych zapytań, np. 1..2, 3..5, 5..20, , Zakresy te wyznaczają kolejność umieszczania zapytań w liście priorytetowej. Dla każdego poziomu w liście priorytetowej przechowywane są: definiujący go zakres wartości liczników użycia; liczba zapytań w danym poziomie, czyli takich, których liczniki użycia należą do zakresu tego poziomu; całkowity rozmiar wyników zapytań danego poziomu. Zakresy poziomów są predefiniowane i mogą być konfigurowane przez administratora systemu. Modyfikacja licznika użycia danego zapytania może powodować zmianę poziomu użycia tego zapytania, a przez to redukować liczbę zapytań jednego poziomu na rzecz kolejnego oraz w przypadku ewaluacji lub aktualizacji wyników zapytania zmieniać obszar pamięci zajmowany przez zapytania tego poziomu (ten element statystyki aktualizowany jest po obliczeniu przez interpreter nowych wyników zapytania). Informacje zapisane w tych listach statystycznych będą wykorzystywane w momencie przekroczenia przez pamięć podręczną pewnego procentowego progu maksymalnie przydzielonych w systemie zasobów oba te parametry, czyli maksymalne rozmiary wykorzystywanej i przydzielonej pamięci mogą być konfigurowane przez administratora. Po osiągnięciu tego progu usuwane będą zapytania najrzadziej używane, w ilości wystarczającej na odzyskanie tylu zasobów pamięciowych, aby aktualne ich użycie nie przekraczało tzw. progu optymalnego, również konfigurowanego. Szczegółowo zostało to przedstawione w rozdziale 4.4 w umieszczonym tam opisie oczyszczania rejestru zapytań jako efektu ubocznego pracy interpretera zapytań. 65

66 4. Koncepcja mechanizmu zapamiętanych zapytań Konfiguracja działań menadżera Zgodnie z opisem opisanego powyżej zakresu działań menadżera zapamiętanych wyników konieczne jest umożliwienie administratorowi bazy danych konfiguracji parametrów pracy tego mechanizmu zarządzania pamięcią podręczną wyników. Podsumowując, konieczne są następujące parametry: tryb działania pracy pamięci podręcznej, związany z miejscem zapisywania rezultatów: w pamięci trwałej, ulotnej, zarówno trwałej jak i ulotnej; minimalny czas wykonania zapytania, po którym należy zapisać rezultat w pamięci trwałej/ulotnej; minimalna liczba użyć zapytania, po której należy zapisać rezultat w pamięci trwałej/ulotnej; rozmiar magazynu pamięci trwałej/ulotnej; procent zajętości, po przekroczeniu którego należy dokonać oczyszczenia pamięci wyników; docelowy procent zajętości, który ma zostać uzyskany po procesie czyszczenia (zajętość optymalna); maksymalny rozmiar rezultatu pojedynczego zapytania, który może zostać zapamiętany; lista progów użyć zapytań wyznaczająca poziomy (grupy) używane przy oczyszczaniu statystyk. Pewne z tych parametrów powinny być ustawiane w startowej konfiguracji systemu, np. w plikach startowych. Są to ustawienia trybu pracy oraz konfiguracja poziomów statystyk użycia zapytań. Pozostałe mogą być zmieniane podczas działania systemu, np. z linii poleceń lub za pomocą narzędzia konfiguracyjnego. 4.3 Optymalizacja zapytań Niniejsza rozprawa koncentruje się wokół optymalizacji zapytań do obiektowych baz danych, zatem optymalizator zapytań jest najistotniejszym elementem mechanizmu opisywanej optymalizacji. Optymalizator wprowadzający zapamiętywanie wyników zapytań jest przykładem optymalizatora przepisującego (ang. query rewriter), gdyż transformuje otrzymane zapytanie (przekazywane w formie drzewa syntaktycznego) do innej, zoptymalizowanej postaci, która rokuje krótszy czas ewaluacji tego zapytania na etapie interpretacji. Optymalizatorów przepisujących w systemie bazodanowym może być wiele (Rys.4.2), np. optymalizator wykorzystujący indeksy gęste lub rzadkie dla obiektów atomowych (wartości skalarnych), optymalizator wykorzystujący reguły niezależnych lub martwych podzapytań i inne (wybrane metody optymalizacji zapytań zostały szerzej opisane w rozdziale 2). Jednak w odróżnieniu od większości 66

67 4. Koncepcja mechanizmu zapamiętanych zapytań optymalizatorów przepisujących omawiany optymalizator podczas przepisywania zapytania wstawia do drzewa syntaktycznego własne wewnętrzne metody (wywołania funkcji cache'ującej) przezroczyste i nieosiągalne dla twórcy zapytania. W tym względzie optymalizator ten działa podobnie do optymalizatora korzystającego z indeksów redundantnych struktur, do których odwołanie wymaga przemycenia do drzewa syntaktycznego odpowiednich sugestii dla interpretera w celu szybszego uzyskania wyników zapytania. Takie dodatkowe wstawki wymagają wprowadzenia dodatkowych mechanizmów kontrolnych w celu dostosowania się do wymogów przyjętego procesu ewaluacji zapytania, jak zapewnienie odpowiedniego wnioskowania o typie wyniku. Rys.4.2. Uproszczony schemat ewaluacji zapytania przesłanego do bazy danych Kolejność oraz zestaw wykorzystywanych w systemie optymalizacji powinny być konfigurowane przez administratora systemu na podstawie wiedzy wynikającej z analizy specyfiki bazy danych i rodzaju zapytań, które najczęściej są do niej kierowane. Niemniej jednak optymalizacja zapytań z wykorzystaniem zapamiętywania ich wyników powinna być realizowana na jak najpóźniejszym etapie procesu optymalizacji, co zapobiegnie analizie i ewaluacji nieoptymalnych form zapytania - każde zapytanie musi być przecież przed zapamiętaniem wyników przynajmniej raz wykonane bez 67

68 4. Koncepcja mechanizmu zapamiętanych zapytań wykorzystania pamięci podręcznej wyników zapytań, a w przypadku zmian w bazie może być konieczna jego reewaluacja. Przed przystąpieniem do optymalizacji zapytanie w formie drzewa syntaktycznego jest ewaluowane statycznie pod kątem poprawności typologicznej z użyciem statycznych stosów S_ENVS i S_QRES oraz schematu bazy danych zapisanego w metabazie znajdującej się na serwerze. Dokonuje tego statyczny ewaluator typologiczny zgodnie z zasadami półmocnej kontroli typów opisanej szczegółowo w rozdziale 3.5 oraz w [Ste06]. W trakcie tej analizy w drzewie mogą pojawić się dodatkowe węzły odpowiadające niejawnym operacjom często pomijanym w tekście zapytania, np. koercjom, czyli konwersjom typów, operatorom tworzenia kolekcji oraz dereferencjom. Na Rys.4.3 przedstawiono drzewo syntaktyczne przykładowego zapytania zwracającego nazwiska oraz nazwy departamentów pracowników o pensji większej niż ((Emp where salary > 3000) join works_in.dept).(name, dname) Start. join struct where. name dname Emp > Dept works_in deref 3000 salary Rys.4.3. Drzewo syntaktyczne zapytania Jako nadrzędne węzły drzewa w tym przykładzie pojawiły się wywołania binarnych operatorów niealgebraicznych nawigacji (.), złączenia (join) i selekcji (where). W pierwszej kolejności ewaluowany jest operator selekcji (jako najbardziej zagnieżdżony występujący po lewej stronie), który dla każdego obiektu klasy Emp uruchamia binarny operator algebraiczny porównania (>) wartości pensji pracownika ze stałym literałem całkowitym Porównanie to wymaga wstawienia do drzewa operatora algebraicznego dereferencji (deref) zamieniającego identyfikator obiektu salary na jego wartość. Kolejnym operatorem niewystępującym jawnie w zapytaniu, a dodawanym do drzewa syntaktycznego jest w tym zapytaniu algebraiczny operator tworzenia kolekcji będącej strukturą (struct), uruchamiany na koniec ewaluacji zapytania. Jest to domyślny operator wymuszany na skutek zastosowania w treści zapytania wyliczenia elementów z użyciem przecinka w tym przypadku tworzone są struktury zawierające pary odpowiadających sobie obiektów name i dname. 68

69 4. Koncepcja mechanizmu zapamiętanych zapytań Po udanej statycznej ewaluacji wykonanej zgodnie z opisem zaprezentowanym w rozdziale 3.5 (w przypadku braku błędów syntaktycznych) węzły drzewa wzbogacane są o: sygnatury typów; wysokości stosu ENVS w momencie wiązania danej nazwy oraz sekcję stosu, na której ta nazwa jest wiązana (dotyczy węzłów zawierających nazwy obiektów bazy danych); numery otwieranych sekcji stosu ENVS (dotyczy węzłów odpowiadających operatorom niealgebraicznym). Wynikiem statycznej ewaluacji jest również sygnatura wyników zwracanych przez to zapytanie. Informacje te stanowią podstawę dalszego wnioskowania optymalizatorów, również optymalizatora cache'ującego. Każdy optymalizator po wykonaniu swoich zadań przekazuje zoptymalizowane drzewo syntaktyczne do kolejnego optymalizatora. Drzewo to zawiera zestaw sygnatur właściwych dla zoptymalizowanej formy, przez co kolejna optymalizacja dysponuje pełną informacją dotyczącą zapytania i kolejność optymalizacji może być dowolna. Ostatecznie skompilowane drzewo zapytania trafia do interpretera odpowiedzialnego za jego ewaluację i zwrócenie wyników. Proces optymalizacji z wykorzystaniem zapamiętanych wyników jest procesem wieloetapowym. Etapy te zostały wstępnie opisane w rozdziale 4.1 w punkcie 5. opisanego tam ogólnego scenariusza optymalizacji. Na Rys.4.4 przedstawiono szczegółowy schemat działania optymalizacji z podziałem na kolejne etapy, które opisano w następnych podrozdziałach: normalizacja zapytania; dekompozycja zapytania; analiza zapytania z przepisaniem go w celu użycia zapamiętanych wyników; aktualizacja typologiczna zapytania. 69

70 4. Koncepcja mechanizmu zapamiętanych zapytań Rys.4.4. Schemat przebiegu optymalizacji zapytania Normalizacja zapytania Aby uniknąć umieszczania w pamięci podręcznej wyników zapytań o różnej formie syntaktycznej (tekstowej) lecz tym samym znaczeniu semantycznym konieczne jest wprowadzenie reguł syntaktycznej modyfikacji zapytania normalizującej postać zapytań. Normalizacja powinna być przeprowadzana przed ewaluacją każdego zapytania trafiającego do systemu (Rys.4.4 Optymalizator cache'ujący: Normalizacja). 70

71 4. Koncepcja mechanizmu zapamiętanych zapytań Mechanizm ten przetwarza przekazane do optymalizatora drzewo syntaktyczne zapytania modyfikując kolejność i niekiedy zawartość węzłów tego drzewa. Kompilacja zapytania z postaci tekstowej do postaci drzewa syntaktycznego niweluje różnice syntaktyczne dotyczące użycia opcjonalnych nawiasów niezmieniających priorytetów operatorów oraz białych znaków pomiędzy elementami leksykalnymi. Stąd te właśnie fragmenty zapytania nie podlegają normalizacji. Poniżej przedstawiono proponowane metody normalizacji zapytania. Zamiana kolejności operandów Dla każdego operatora wieloargumentowego, dla którego kolejność argumentów (operandów) nie jest istotna, operandy zamieniane są wg porządku alfabetycznego, np. rosnąco. Dotyczy to zarówno operandów będących literałami liczbowymi i tekstowymi, wyrażeniami nazwowymi (binderami nazw obiektów) oraz rozbudowanymi podzapytaniami. W tym celu konieczna jest możliwość przekształcania dowolnego fragmentu drzewa syntaktycznego na formę tekstową. Zamiana operandów jest możliwa dla operatorów przemiennych, do których zaliczamy niektóre operatory algebraiczne takie jak: operatory porównania (=, ), operatory arytmetyczne dodawania i mnożenia (+, *), operatory logiczne (and, or), operatory mnogościowe sumy i iloczynu (, ) oraz operator konstruujący strukturę ( struct(element1, element2, ) ) z założeniem, że elementy tej struktury są zawsze nazywane za pomocą binderów, czyli nie jest istotna ich kolejność. Zastosowanie rekurencyjne takiej zamiany dla sekwencji jednego z wymienionych operatorów da uporządkowanie alfabetyczne wszystkich operandów w takiej sekwencji. Zmiana kolejności operandów może być zastosowana również dla prawych operandów (odjemników i dzielników) operatorów arytmetycznych odejmowania ( ) i dzielenia (/) oraz ewentualnie różnicy mnogościowej (\), o ile występują w sekwencji przynajmniej dwóch operatorów tego samego typu. Wynika to z faktu, iż odejmowanie jest równoważne dodawaniu drugiego operandu z przeciwnym znakiem, dzielenie mnożeniu przez odwrotność, a odejmowanie mnogościowe iloczynowi mnogościowemu z dopełnieniem drugiego zbioru. Przykłady: Emp where name Doe Emp where Doe name Emp where salary rating 100 = 1000 Emp where 1000 = salary 100 rating Emp where salary = 1100 or salary = 1000 Emp where 1000 = salary or 1100 = salary 71

72 4. Koncepcja mechanizmu zapamiętanych zapytań (Emp join (works_in.dept)).(name, dname) (Emp join (works_in.dept)).(dname, name) Mechanizm ten można również zastosować dla logicznych operatorów porównania (<, <=, >, >=), pamiętając o dodatkowej zamianie operatora na przeciwny w przypadku zmiany kolejności operandów: Emp where salary > 1000 Emp where 1000 < salary W przypadku operatorów porównania możliwa jest normalizacja alternatywna, która w miejsce sortowania alfabetycznego operandów ujednolica użycie tych operatorów poprzez zamianę każdego operatora większości (>, >=) na operator mniejszości (<, <=) lub odwrotnie, co również wiąże się w takiej sytuacji z zamianą kolejności operandów. Taka modyfikacja dla ostatniego przykładu da dokładnie identyczne drzewo wynikowe, ale w ogólnym przypadku nie gwarantuje zachowania alfabetycznego porządku operandów: Emp where rating * 200 >= salary Emp where salary <= rating * 200 Generalny algorytm normalizacji drzewa syntaktycznego zapytania z wykorzystaniem zamiany kolejności operandów zachowującej semantykę tego zapytania ma następującą postać: 1. Przeglądamy w głąb drzewo syntaktyczne zapytania zatrzymując się w każdym węźle, który nie jest liściem drzewa (ma węzły podrzędne) rozpoczynając od węzła startowego. 2. Jeżeli w aktualnym węźle znajduje się jeden z operatorów porównania (<, <=, >, >=), w zależności od ustawień wybieramy jedną z opcji 2a lub 2b. W przeciwnym wypadku przechodzimy do kroku 3. 2a. w przypadku operatora większości (>, >=) zamieniamy go na odpowiedni operator mniejszości (<,<=) i zamieniamy kolejnością prawą i lewą gałąź węzła operatora, a następnie przechodzimy do kroku 8.; 2b. zamieniamy lewą i prawą gałąź na wartość tekstową i porównujemy je ze sobą jeżeli prawa jest mniejsza od lewej, zamieniamy operator na przeciwny i zamieniamy kolejnością lewą gałąź z prawą, a następnie przechodzimy do kroku 8.; 3. Jeżeli w aktualnym węźle znajduje się jeden z operatorów: = lub, zamieniamy lewą i prawą gałąź drzewa zapytania na wartość tekstową i porównujemy je ze sobą, w przeciwnym wypadku przechodzimy do kroku 4. Jeżeli prawa jest mniejsza od lewej, zamieniamy kolejnością lewą gałąź z prawą. Przechodzimy do kroku 8. 72

73 4. Koncepcja mechanizmu zapamiętanych zapytań 4. Jeżeli w aktualnym węźle znajduje się operator konstrukcji struktury struct, tworzymy kolejkę priorytetową odpowiedzialną za przechowywanie dowolnych zapytań (tekstowych odpowiedników drzew syntaktycznych) ustawiając je automatycznie przy wstawianiu w kolejności alfabetycznej. W przeciwnym wypadku przechodzimy do kroku 5. Dokonujemy normalizacji każdego podzapytania stanowiącego gałąź (może być ich dowolnie dużo) aktualnego węzła przeprowadzając rekurencyjnie cały proces począwszy od punktu 1. Dodajemy do kolejki każde, w ten sposób znormalizowane, poddrzewo będące gałęzią aktualnego węzła, a następnie pobierając z kolejki kolejne poddrzewa wstawiamy je jako nowe gałęzie tego węzła począwszy od pierwszej do ostatniej. Przechodzimy do kroku Jeżeli w aktualnym węźle znajduje się jeden z operatorów: or, and, +,, *, /,,, \, tworzymy kolejkę priorytetową taką jak w kroku poprzednim. W przeciwnym wypadku przechodzimy do kroku 8. Dokonujemy normalizacji podzapytania stanowiącego prawą gałąź przeprowadzając rekurencyjnie cały proces począwszy od punktu 1. Dodajemy do kolejki znormalizowane poddrzewo będące prawą gałęzią aktualnego węzła. 6. Sprawdzamy czy lewa gałąź rozpoczyna się od węzła zawierającego ten sam operator. Jeżeli tak, przechodzimy do tego węzła, dokonujemy normalizacji podzapytania stanowiącego prawą gałąź przeprowadzając rekurencyjnie cały proces począwszy od punktu 1., dodajemy prawą gałąź tego węzła do kolejki i ponownie wykonujemy krok 6. Jeśli nie, dokonujemy normalizacji podzapytania stanowiącego lewą gałąź przeprowadzając rekurencyjnie cały proces począwszy od punktu 1. Po zakończeniu analizy lewej części dodajemy całe zapytanie z lewej gałęzi do kolejki, ale pod warunkiem, że operator w aktualnym węźle nie jest operatorem odejmowania ( ), dzielenia (/), ani różnicy mnogościowej (\). Wtedy też, pobieramy pierwsze zapytanie z kolejki i wstawiamy je jako lewą gałąź aktualnego węzła. 7. Pobieramy kolejne zapytanie z kolejki i wstawiamy je jako prawą gałąź aktualnego węzła. Jeżeli kolejka nadal jest niepusta, przechodzimy do węzła nadrzędnego i ponownie wykonujemy krok 7. W przeciwnym wypadku uznajemy całe poddrzewo rozpoczynające się od aktualnego węzła jako przeanalizowane w głąb i przechodzimy do kroku Przechodzimy w głąb drzewa do następnego nieodwiedzonego węzła nieliściowego i wracamy do kroku 2. Jeżeli nie ma już węzłów nieliściowych proces normalizacji się kończy. Efektem działania powyższego algorytmu (w szczególności kroków 5., 6. i 7.) będzie następująca transformacja (za pomocą nawiasów zaznaczono dla przejrzystości kolejność wykonywania operatorów, czyli jednoznaczną postać drzewa syntaktycznego): 73

74 4. Koncepcja mechanizmu zapamiętanych zapytań (((e b) a) + d) + c (c + d) + (( e a) b) Zamiana kolejności operatorów W przypadku sekwencji operatorów algebraicznych addytywnych lub multiplikatywnych, czyli gałęzi drzewa syntaktycznego złożonej wyłącznie z operatorów + i lub odpowiednio * i /, następuje pogrupowanie tych operatorów tak, aby operatory + były umieszczone przed operatorami, a * przed /. Zgodność semantyczną gwarantuje możliwość zamiany odejmowania na dodawanie i dzielenia na mnożenie wyjaśniona podczas omawiania poprzedniej metody normalizacyjnej. Przykłady transformacji: a + b c + d e a + b + d c e a / b / c * d / e a * d / b / c / e Aby uzyskać takie transformacje konieczna jest jako pierwszy krok przed zastosowaniem algorytmu normalizacji przez zamianę operandów (przedstawionego wyżej) zamiana binarnego operatora odejmowania na sekwencję operatorów: binarnego operatora dodawania i unarnego operatora wartości przeciwnej. Analogicznie zamieniany jest binarny operator dzielenia z wykorzystaniem sekwencji binarnych operatorów mnożenia i dzielenia (uzyskując odwrotność drugiego operandu poprzez dzielenie przez niego wartości 1). W ten sposób w pierwszym kroku transformacji powyższych przykładowych zapytań uzyskuje się więc: a + b c + d e a + b + ( c) + d + ( e) a / b / c * d / e a * (1 / b) * (1 / c) * d * (1 / e) Dalej konieczna jest modyfikacja funkcji zamieniającej zapytanie na postać tekstową tak, aby na początku formy tekstowej każdego podzapytania rozpoczynającego się unarnym operatorem wartości przeciwnej lub binarnym operatorem dzielenia (po wcześniejszej zamianie w drzewie syntaktycznym pozostaną tylko operacje dzielenia wartości 1) wstawiony został specjalny znak, który spowoduje, że takie zapytania będą traktowane jako największe alfabetycznie. Znak ten pojawia się zatem tylko w postaci tekstowej sortowanych podzapytań nie w ich drzewach syntaktycznych. Po tych modyfikacjach drzewo zapytania przechodzi proces normalizacji z zamianą operandów, co dla powyższych przykładów daje następujące wyniki (dla przykładu 74

75 4. Koncepcja mechanizmu zapamiętanych zapytań użyto znaku specjalnego ~ otaczając nawiasem klamrowym zapytanie, do postaci tekstowej którego został doklejony): a + b + ( c) + d + ( e) a + b + d + (~{ c}) + (~{ e}) a * (1 / b) * (1 / c) * d * (1 / e) a * d * (~{1 / b}) * (~{1 / c}) * (~{1 / e}) Ostatecznie dokonywana jest transformacja drzewa odwrotna niż w pierwszym kroku zmiana operatorów na na ich oryginalne odpowiedniki: a + b + d + ( c) + ( e) a + b + d c e a * d * (1 / b) * (1 / c) * (1 / e) a * d / b / c / e Mechanizm ten przenosi się także na operatory sumy i różnicy mnogościowej, przy czym przy nieobecności w SBQL operatora dopełnienia zbioru i niewygodniej dla algorytmu zamianie różnicy na iloczyn mnogościowy proponowana jest chwilowa zamiana jej na sumę mnogościową i oznaczenie specjalne w drzewie syntaktycznym węzła tego operatora tak, aby możliwy był po zastosowaniu algorytmu powrót do oryginalnego operatora różnicy. Ujednolicenie nazw pomocniczych Każda nazwa pomocnicza stosowana w zapytaniu dla operatorów as i group as jest zastępowana odpowiednią nazwą ujednoliconą z przyrostkiem numerującym taką nazwę jednoznacznie, dzięki czemu następuje uniezależnienie treści zapytania od doboru, często przypadkowego, dokonywanego przez twórcę takich zapytań. Przykładowymi substytutami mogą być nazwy typu $cache_aux1, $cache_group_aux2, itp. Efekt działania takiej metody normalizacyjnej widać na poniższych dwóch zapytaniach. Pierwsze pobiera z bazy nazwiska i nazwy departamentów dla wszystkich pracowników zarabiających więcej niż 1000, a drugie zwraca departamenty wraz z liczbą pracowników w nich zatrudnionych zarabiających więcej niż szef ich departamentu: (((Emp where salary > 1000) as e) join (works_in.dept as d)).(e.name, d.dname) (((Emp where salary > 1000) as $cache_aux1) join (works_in.dept as $cache_aux2)).($cache_aux1.dname, $cache_aux2.name) Dept join (((boss.emp.salary) group as s). count(employs.emp where salary > s)) Dept join (((boss.emp.salary) group as $cache_group_aux1). 75

76 4. Koncepcja mechanizmu zapamiętanych zapytań count(employs.emp where salary > $cache_group_aux1)) Takie modyfikacje są możliwe tylko i wyłącznie gdy nazwy pomocnicze nie są zwracane w wyniku zapytania jako bindery do dalszego wykorzystania w szerszej perspektywie programu. Oznacza to, że nazwy te są w zapytaniu niewykorzystane albo wykorzystane i całkowicie konsumowane (co ma miejsce w typowym ich zastosowaniu). Zamianie nie podlegają więc nazwy pomocnicze pozostające w zwracanych wynikach zapytania. Nazwy te można wykryć analizując sygnaturę wyniku zapytania wyznaczoną podczas statycznej ewaluacji drzewa syntaktycznego (zgodnie z wcześniejszym opisem optymalizator ma dostęp do tych informacji). Nazwy te, to wszystkie znajdujące się w sygnaturze wyniku bindery statyczne postaci n(x). W typowych zapytaniach bindery statyczne pojawiają się w wynikach tych zapytań, gdy operatory nazwy pomocniczej znajdują się w jednym z następujących węzłów: startowym węźle zapytania, np. dla zapytania: (Emp where salary > 1000) as e sygnatura wyniku ma postać: bag{e(i Emp )}; węźle bezpośrednio podrzędnym startowemu węzłowi zapytania, np. dla zapytania: Emp.(salary group as s) sygnatura wyniku ma postać: s(bag{i salary }); węzłach bezpośrednio podrzędnych sekwencji węzłów zawierających operatory algebraiczne konstrukcji kolekcji (struct, bag, sequence), gdy sekwencja ta rozpoczyna się od węzła startowego, np. dla zapytania: ((Emp as e, Dept as d), test as t) group as g sygnatura wyniku ma postać: g(bag{struct{struct{e(i Emp ), d(i Dept )}, t(string)}}); węzłach bezpośrednio podrzędnych sekwencji węzłów zapytania zawierających operatory algebraiczne konstrukcji kolekcji (struct, bag, sequence), gdy sekwencja ta jest lewym podzapytaniem węzła startowego zapytania zawierającego operator niealgebraiczny selekcji (where), np. dla zapytania: (Emp as e) where e.salary < 2000 sygnatura wyniku ma tak jak w pierwszym przykładzie postać: bag{e(i Emp )}; 76

77 4. Koncepcja mechanizmu zapamiętanych zapytań węzłach bezpośrednio podrzędnych sekwencji węzłów zapytania zawierających operatory algebraiczne konstrukcji kolekcji (struct, bag, sequence), gdy sekwencja ta jest prawym podzapytaniem węzła startowego zapytania zawierającego operator niealgebraiczny nawigacji (.), np. dla zapytania: ((Emp as e) join (works_in.dept as d)).(e.name as n, d.dname as dn) sygnatura wyniku ma postać: bag{struct{n(i name ), dn(i dname )}}, przy czym nazwy pomocnicze e i d mogą zostać zmienione; węzłach bezpośrednio podrzędnych sekwencji węzłów zapytania zawierających operatory algebraiczne konstrukcji kolekcji (struct, bag, sequence), gdy sekwencje te są podzapytaniami sekwencji węzłów zapytania, rozpoczynającej się od węzła startowego, zawierających tylko i wyłącznie operatory niealgebraiczne złączenia (join), np. dla zapytania: (Emp as e) join (e.works_in.dept as d) join (e.name as n, d.dname as dn) sygnatura wyniku ma postać: bag{struct{struct{e(i Emp ), d(i Dept )}, struct{n(i name ), dn(i dname )}}}. Oto kolejne etapy tej metody normalizacyjnej: 1. Tworzymy sortowaną listę wszystkich nazw n binderów statycznych znajdujących się w sygnaturze wyniku oraz drugą sortowaną listę mapowania nazw pomocniczych dla powiązania każdej takiej nazwy z jej znormalizowaną wersją. 2. Tworzymy dwa liczniki (osobno dla operatora as i operatora group as) inicjalizując je wartością Przeglądamy całe drzewo syntaktyczne zapytania w poszukiwaniu węzła zawierającego jeden z tych operatorów lub zawierającego wiązanie nazwy. 4. W przypadku znalezienia operatora nazwy pomocniczej sprawdzamy, czy parametryzująca go nazwa występuje w liście binderów statycznych (ze względu na posortowaną zawartość wyszukiwanie jest bardzo szybkie). Jeżeli nie, zamieniamy ją na nazwę znormalizowaną z przyrostkiem pobieranym z odpowiedniego licznika (w zależności od operatora). Po zamianie nazwy licznik ten zwiększamy o 1, a w liście mapowania nazw wiążemy odnalezioną nazwę oryginalną z przypisaną jej nazwą znormalizowaną. W ten sposób zamieniane są tylko właściwe nazwy pomocnicze w węzłach, w których powstają. 5. W przypadku znalezienia w drzewie syntaktycznym węzła zawierającego odwołanie do jednej z nazw znajdujących się w liście mapowania (wśród oryginalnych binderów statycznych), następuje zamiana jej na przypisaną jej wcześniej nazwę znormalizowaną zapamiętaną w liście. W ten sposób 77

78 4. Koncepcja mechanizmu zapamiętanych zapytań zamieniane są nazwy pomocnicze w węzłach, w których są wykorzystywane (wiązane). Algorytm ten normalizuje nazwy pomocnicze zastosowane w zapytaniach, zarówno w węzłach definicji tych nazw, jak i w węzłach, w których następuje do nich odwołanie. Ujednolicenie nazw obiektów (słownik nazw) Jeżeli system bazy danych podczas wiązania nazw obiektów występujących w zapytaniu nie rozróżnia wielkości liter w tych nazwach, możliwe jest ujednolicenie tych nazw poprzez ich konwersję do wybranego formatu, np. z użyciem wyłącznie małych liter. Idąc dalej, dla przyspieszenia operacji na zapamiętanych zapytaniach, szczególnie ich wyszukiwania, rejestr zapytań może być rozbudowany o dodatkową redundantną strukturę zwaną słownikiem leksykalnym. Struktura ta zawiera mapowania pomiędzy nazwami obiektów bazy danych oraz unikalnymi skrótami tekstowymi lub identyfikatorami generowanymi podczas tworzenia schematu bazy danych. Może być ona również przechowywana razem z grafem schematu w metabazie. Dzięki zmniejszeniu rozmiaru nazw uzyskuje się oszczędność pamięci niezbędnej do przechowania zapamiętanych zapytań, a także przyspieszenie normalizacji z sortowaniem operandów oraz tekstowego wyszukiwania tych zapytań Dekompozycja zapytania Dekompozycja zapytania jest mechanizmem przyspieszającym ewaluację zapytania jako takiego oraz umożliwia optymalizację dużo większej liczby nowych zapytań. Jeżeli zapamiętamy wyniki mniejszego niezależnego podzapytania w miejsce całego złożonego, wzrośnie znacznie prawdopodobieństwo ponownego wykorzystania tych wyników. Dodatkowo prostsza semantyka zdekomponowanego zapytania zmniejsza koszt aktualizacji jego zapamiętanych wyników. Dekompozycja jest przeprowadzana przez optymalizator zaraz po normalizacji drzewa syntaktycznego analizowanego zapytania (Rys.4.4 Optymalizator cache'ujący: Dekompozycja). Podczas tego procesu zapytanie przekształcane jest do postaci umożliwiającej wydobycie z niego mniej skomplikowanych zapytań i przekazanie ich niezależnie (tak, jakby były osobnymi zapytaniami kierowanymi do bazy) do etapu analizy pod kątem przydatności jako zapytań zapamiętanych (Rys.4.4 Dekompozytor zapytań: Analiza). Jeżeli analizator zapytania uzna wybrane podzapytanie jako warte zapamiętania, następuje zamiana tego podzapytania w drzewie syntaktycznym wywołaniem odpowiedniej funkcji cache'ującej (szerszy opis podany jest w kolejnym podrozdziale). Proces dekompozycji jest przeprowadzany do momentu, aż zapytanie nie może być już dalej dekomponowane znanymi metodami. Poniżej przedstawiono proponowane metody dekompozycji zapytania. 78

79 4. Koncepcja mechanizmu zapamiętanych zapytań Wyodrębnienie podzapytań niezależnych W sytuacji gdy wewnętrzne zapytanie w zapytaniu zagnieżdżonym jest niezależne od ewaluacji zapytania zewnętrznego formującego ostateczne wyniki, możliwe jest zapamiętanie wyników dwóch niezależnych zapytań w miejsce jednego złożonego. Metoda ta jest sama w sobie metodą optymalizacyjną i została dokładne zbadana oraz opisana w [Plo00, Sub04]. W skrócie polega ona na zdiagnozowaniu (wykorzystując informacje o numerach sekcji wiązania nazw na stosie ENVS umieszczone w drzewie syntaktycznym na etapie ewaluacji statycznej) zależności podzapytań od operatorów niealgebraicznych użytych w zapytaniu. Dla zobrazowania jej użyteczności w świetle optymalizacji przez zapamiętywanie wyników rozważmy zapytanie postaci: q: Emp where salary < (Emp where name = Smith ).salary Zapytanie to pobiera listę pracowników, których pensja jest mniejsza od pensji pracownika o nazwisku Smith. Zauważmy, że podzapytanie znajdujące się po operatorze porównania jest wykonywane tyle razy ile jest obiektów typu Emp, a przecież wynik tego podzapytania nie zmienia się podczas każdej iteracji. Formalnie wynika to z faktu, iż wewnętrzne zapytanie (Emp where name = Smith ).salary jest niezależne od pierwszego operatora where (wszystkie nazwy obiektów użyte w tym zapytaniu wiązane są w innych sekcjach stosu ENVS niż sekcja otwierana przez ten operator niealgebraiczny). Warto więc, zgodnie z propozycją opisaną w [Plo00], zapytanie takie wyciągnąć przed nawias (przed operator, od którego to zapytanie nie jest zależne) wykorzystując operator grupowej nazwy pomocniczej i wykonać je tylko raz: q : (((Emp where name = Smith ).salary) group as v).(emp where salary < v) W ten sposób wyodrębnione zostały dwa zdekomponowane zapytania, które mogą być wykonywane niezależnie i jako takie zapamiętane w pamięci podręcznej: q 1 : q 2 : (Emp where name = Smith ).salary Emp where salary < f cache Zapytania q 1 i q 2 są łatwe do wyodrębnienia jako odpowiednio lewy i prawy operand operatora nawigacji (.) pojawiającego się w węźle startowym drzewa syntaktycznego wskutek transformacji zapytania metodą niezależnych podzapytań (w lewym podzapytaniu należy ominąć znajdujący się w jego węźle startowym unarny 79

80 4. Koncepcja mechanizmu zapamiętanych zapytań operator nazwy pomocniczej group as). Zapytanie q 1 przesyłane jest do etapu analizy (opisanego w kolejnym podrozdziale) i jeżeli istniało już w rejestrze zapamiętanych zapytań lub zostało zaakceptowane jako nowe zapamiętane zapytanie, do etapu dekompozycji przekazywane jest w postaci wywołania funkcji cache'ującej. Wywołanie tej funkcje może być teraz łatwo wprowadzone w miejsce stałej f cache w zapytaniu q 2, które jest semantycznie zgodne z zapytaniami q oraz q, ostatecznie również przesyłane do analizy jako wynik dekompozycji zapytania q i potencjalnie również może zostać zapamiętane. Wartość f cache jest więc tutaj zapamiętanym wynikiem ewaluacji pierwszego, oryginalnie wewnętrznego, niezależnego zapytania. Jeżeli zapytanie q 1 nie zostanie zamienione na wywołanie funkcji cache'ującej, do kolejnego etapu przechodzi zoptymalizowane zapytanie w postaci q. Kolejnym przykładem jest zapytanie pobierające pracowników ze stawką większą od najmniejszej spośród stawek szefów departamentów, w których maksymalna pensja jest mniejsza niż 2000: q: Emp where rating > min((dept where max(employs.emp.salary) < 2000).boss.Emp.rating) Zapytanie w nawiasach jest zapytaniem niezależnym od pierwszego operatora where, więc zakładając, że minimum ze stawek szefów departamentów, w których maksymalna pensja jest mniejsza niż 2000, czyli wynik niezależnego zapytania q 1 w postaci odpowiednio parametryzowanej funkcji cache'ującej wynosi f cache, uzyskujemy dwa zapytania: q 1 : q 2 : min((dept where max(employs.emp.salary) < 2000).boss.Emp.rating) Emp where rating > f cache Pierwsze zapytanie zawiera czasochłonne przy ewaluacji operacje agregacji, więc po zapamiętaniu jego wyniku operacje te nie będą w przyszłości musiały być wykonywane ponownie. Wynik ten posłuży również jako zapamiętana stała f cache podczas modyfikacji oryginalnego zapytania q do postaci drugiego zapytania, którego wyniki mogą zostać zapamiętane niezależnie. Użycie tej metody w kontekście zapamiętywania wyników zapytań jest możliwe o ile podzapytanie niezależne jest wyciągane na początek całego przetwarzanego zapytania, a nie jego fragmentu (podzapytania wykonywanego w ramach szerszego zapytania). Wynika to z faktu, iż takie niezależne podzapytanie musi być całkowicie niezależne, aby mogło być wykonane samodzielnie, co oznacza, że jego ewaluacja musi rozpoczynać się od obiektu korzeniowego. 80

81 4. Koncepcja mechanizmu zapamiętanych zapytań Dla przykładu rozpatrzmy zapytanie pozyskujące obiekty zajęć oraz dla każdego z nich liczbę studentów uczestniczących w tych zajęciach o nazwisku identycznym z nazwiskiem prowadzącego te zajęcia: q: Training join count(received_by.student where name = supervised_by.emp.name) Zauważmy, że wewnętrzne podzapytanie supervised_by.emp.name jest niezależne od operatora selekcji where, ale jest jednak zależne od wcześniejszego operatora złączenia join (podzapytanie to pobiera nazwiska prowadzących odpowiednie zajęcia). Metoda optymalizacji z wykorzystaniem wyciągania niezależnych zapytań spowoduję przestawienie tego podzapytania przed operator selekcji, ale nie będzie ono wyciągnięte na początek całego zapytania: q : Training join count(((supervised_by.emp.name) as n).(received_by.student where name = n)) Wykryte niezależne podzapytanie nie jest więc zapytaniem całkowicie niezależnym i nie może zostać przekazane do analizy jako kandydat do zapamiętania nie jest poprawnym, samodzielnym zapytaniem. Wyznacznikiem jest tutaj obecność w najgłębszym lewym węźle drzewa syntaktycznego tego zapytania nazwy jednego z obiektów korzeniowych. W rozpatrywanym przykładzie znajduje się tutaj nazwa supervised_by obiektu niekorzeniowego, co jednoznacznie wyklucza takie zapytanie. Efektem dekompozycji zapytania q będzie zapytanie q przekazane kompletnie do ewentualnego zapamiętania w rejestrze zapamiętanych zapytań. Wyodrębnienie zapytań agregujących Ewaluacja funkcji arytmetycznych zagregowanych bardzo często jest czasochłonna, szczególnie gdy jest wykonywana wielokrotnie, np. dla obiektu, którego instancji w bazie danych jest wiele. W języku SBQL takie funkcje są algebraicznymi operatorami arytmetycznymi przyjmującymi kolekcję wartości liczbowych i zwracającymi jedną wartość liczbową. Należą do nich operatory obliczające: sumę (sum), wartość średnią (avg), wartość minimalną (min), wartość maksymalną (max) i liczbę elementów kolekcji (count). Operatorów takich może być więcej. Ich głównym zastosowaniem jest wyznaczenie pewnych statystycznych wartości dla obiektów pewniej klasy. Przykładem może być zapytanie: 81

82 4. Koncepcja mechanizmu zapamiętanych zapytań q: Dept join avg(employs.emp.salary) Zapytanie to pobiera identyfikatory departamentów wraz z powiązanymi z nimi wartościami średniego wynagrodzenia pracowników w nich zatrudnionych. Koszt wielokrotnej ewaluacji użytej funkcji agregującej dla każdego departamentu jest wysoki, dlatego też zapamiętanie wyników tego zapytania może być bardzo opłacalne również dla przyspieszenia ewaluacji bardziej skomplikowanych zapytań. Taka zagregowana, zapamiętana wartość średniego wynagrodzenia może być interpretowana jako wirtualny atrybut obiektów klasy Dept, czyli niejako metoda tej klasy tyle, że niejawna utworzona automatycznie przez optymalizator w postaci funkcji cache'ującej. W tej sytuacji, gdy wynikiem zapytania jest bag struktur (tutaj par złożonych z identyfikatora departamentu i średniej wynagrodzenia), warto wykorzystać ten zapamiętany wynik również do szybkiej ewaluacji zapytań pobierających drugie elementy z wynikowych struktur dla jednego lub wielu identyfikatorów będących jednym z ich pierwszych elementów. Przykładem może być zapytanie pozyskujące tylko średnie wartości wynagrodzenia dla każdego departamentu: q 1 : Dept.avg(employs.Emp.salary) Wywołanie funkcji agregującej przypomina odwołanie do metody klasy. Wynik takiego zapytania jest projekcją na drugi element wyników zapamiętanego zapytania q. Innym przykładem jest zapytanie zawracające pracowników, których wynagrodzenie jest większe od średniego wynagrodzenia wszystkich pracowników pracujących w ich departamencie: q 2 : Emp where salary > (works_in.dept.avg(employs.emp.salary)) W tej sytuacji również można wykorzystać zapamiętane wyniki zapytania q, gdyż dla każdego pracownika średnia jego departamentu może być pobrana jako projekcja na drugi element struktur wchodzących w skład tych zapamiętanych wyników po wyselekcjonowaniu spośród nich wcześniej struktury zawierającej w pierwszym elemencie identyfikator departamentu właściwego dla danego pracownika (jednoznacznie wyznaczanego przez referencję works_in). Zakładając, że zapamiętanemu zapytaniu q odpowiadają wyniki symbolizowane przez stałą f cache (będące wynikami zwracanego przez analizator zapytań drzewa syntaktycznego zawierającego wywołanie odpowiedniej funkcji cache'ującej), możliwe jest dodatkowe sparametryzowanie tej funkcji odpowiednim podzbiorem identyfikatorów obiektów spośród pierwszych elementów wynikowych struktur. Spowoduje to podczas interpretacji pozyskanie z tych wyników tylko odpowiednich drugich elementów. Aby wykorzystanie tych wyników się odbyło, konieczne jest 82

83 4. Koncepcja mechanizmu zapamiętanych zapytań wyodrębnienie (dekompozycja) z potencjalnych nowych zapytań takich podzapytań, które zawierają selekcję zagregowanej wartości dla jakiegoś obiektu korzeniowego. W przypadku zapytania q 1 zostanie ono przekazane do analizy w całości (całe jest taką selekcją), natomiast z zapytania q 2 wyodrębnione będzie podzapytanie syntaktycznie zgodne z zapytaniem q 1. Po analizie tych zapytań zostaną one na etapie analizy zoptymalizowane z wykorzystaniem wyników zapytania q do postaci odpowiednio: q 1 : q 2 : f cache (Dept) Emp where salary > (works_in.f cache (Dept)) W pierwszym przypadku parametr funkcji cache'ującej zawiera identyfikatory wszystkich obiektów klasy Dept (wiązanych podczas ewaluacji w sekcji bazowej stosu ENVS), w drugim tylko jeden (wiązany w sekcji stosu utworzonej przez funkcję nested dla odpowiedniego obiektu works_in na skutek działania operatora nawigacji kropka). Warto zauważyć, że zapytanie q 1 może być alternatywnie zoptymalizowane z wykorzystaniem funkcji cache'ującej bez dodatkowego parametru, o ile w całości jest zapamiętane w rejestrze zapytań. Wynika to z faktu, iż jest samodzielnym zapytaniem i podlega mechanizmom zapamiętania wyników. Jeżeli jednak nie było wcześniej zapamiętane, analizator będzie mógł przepisać je z wykorzystaniem wyników zapamiętanego zapytania q zgodnie z powyższym (o ile takie zapytanie było zapamiętane). Podzapytanie zapytania q 2 zawierające funkcję agregującą nie jest samodzielnym zapytaniem (jest wyrwane z kontekstu), więc jego przepisanie nie może być wykonane przez analizator z wykorzystaniem wyników ewentualnie zapamiętanego zapytania q 1, a jedynie zapytania q, więc wysyłając takie zapytanie do analizy dekompozytor musi przekazać je wraz z dodatkową informacją, iż należy je przepisać z wykorzystaniem funkcji cache'ującej z parametrem filtrującym wyniki. Zaprezentowana na powyższych przykładach dekompozycja odbywa się następująco: 1. Przeglądamy drzewo syntaktyczne zapytania w poszukiwaniu węzła zawierającego operator algebraiczny funkcji agregującej (sum, avg, min, max lub count) będący prawym podrzędnym węzłem węzła zawierającego operator niealgebraiczny nawigacji (.). Jeżeli nie znajdziemy, kończymy proces dekompozycji przechodząc do kroku Sprawdzamy czy lewym podrzędnym węzłem wykrytego operatora nawigacji jest węzeł zawierający wiązanie nazwy obiektu korzeniowego. Jeżeli tak, przechodzimy do kroku 3. Jeżeli nie sprawdzamy czy w tym lewym węźle jest kolejny operator nawigacji (nawigacja podrzędna) i jeżeli tak, to czy w jego prawym węźle podrzędnym znajduje się wiązanie nazwy obiektu korzeniowego. Jeżeli tak, przechodzimy do kroku 4. Jeżeli w żadnym z powyższych 83

84 4. Koncepcja mechanizmu zapamiętanych zapytań przypadków nie wykryto nazwy obiektu korzeniowego, kończymy proces dekompozycji przechodząc do kroku Przekazujemy do analizy podzapytanie, którego węzłem startowym jest wykryty w kroku 1. węzeł z operatorem nawigacji, przy czym jeżeli węzeł ten nie jest węzłem startowym całego zapytania (mamy do czynienia z niesamodzielnym podzapytaniem) informujemy analizator o wymogu wykorzystania wyłącznie funkcji cache'ującej z filtrowaniem wyników. Jeżeli nastąpi zastąpienie podzapytania wywołaniem funkcji cache'ującej, zastępujemy fragment drzewa przekazany do analizy drzewem zawierającym to wywołanie. W przeciwnym wypadku zwracamy oryginalne zapytanie. Proces dekompozycji jest kończony. 4. Przekazujemy do analizy podzapytanie, którego węzłem startowym jest wykryty w kroku 1. węzeł z operatorem nawigacji wstawiając w jego lewy węzeł podrzędny wiązanie nazwy obiektu korzeniowego wykrytego w kroku 2. w prawym węźle podrzędnego operatora nawigacji. Mamy do czynienia z niesamodzielnym podzapytaniem, więc informujemy analizator o wymogu wykorzystania wyłącznie funkcji cache'ującej z filtrowaniem wyników. Jeżeli nastąpi zastąpienie podzapytania wywołaniem funkcji cache'ującej, wstawiamy w prawym węźle podrzędnym operatora nawigacji wykrytego w kroku 1. drzewo zawierające to wywołanie, a w lewym lewe poddrzewo podrzędnego operatora nawigacji (wykrytego w kroku 2.) z oryginalnego zapytania. W przeciwnym wypadku zwracamy oryginalne zapytanie. Proces dekompozycji jest kończony. 5. Przekazujemy całe zapytanie do etapu analizy, czego efektem może być zastąpienie go wywołaniem funkcji cache'ującej. Jeżeli to nastąpi zwracamy jako końcowy wynik dekompozycji drzewo zawierające to wywołanie. W przeciwnym wypadku zwracamy oryginalne zapytanie. Wykorzystanie tej metody dekompozycyjnej podczas optymalizacji z wykorzystaniem zapamiętanych wyników jest efektywne, gdy w rejestrze zapytań zapamiętane zostały wyniki zapytań tworzących wirtualne atrybuty obiektów korzeniowych będące wynikami funkcji agregujących. Są to zapytania w stylu zapytania q przytoczonego wcześniej, czyli takie, w których drzewie syntaktycznym w węźle startowym znajduje się niealgebraiczny operator zależnego złączenia (join), jego lewy podwęzeł zawiera wiązanie nazwy obiektu korzeniowego, a prawy jeden z algebraicznych operatorów agregujących. Najprostszym sposobem umieszczenia takiego zapytania w rejestrze jest wywołanie takiego zapytania, nawet jeżeli jego wyniki są w danym momencie niepotrzebne. Wywołanie odpowiedniego zestawu takich zapytań (dobranego przez administratora bazy danych lub projektanta aplikacji) i automatyczne zapamiętanie ich wyników, spowoduje utworzenie i utrzymywanie przez menadżera zapamiętanych zapytań odpowiedniego zestawu pożytecznych atrybutów wirtualnych obiektów bazy danych, które będą automatycznie wykorzystywane zgodnie 84

85 4. Koncepcja mechanizmu zapamiętanych zapytań z powyższym algorytmem bez konieczności wielokrotnej ewaluacji funkcji agregujących. Zestaw taki może być konfigurowany za pomocą specjalnego narzędzia ułatwiającego generowanie takich zapytań. Pominięcie wyrażeń ścieżkowych kończących ewaluację zapytania Wyrażenia ścieżkowe to sekwencje operacji realizowanych za pomocą operatora niealgebraicznego nawigacji (operator kropki) są w wielu przypadkach realizowane w krótkim czasie. Wynika to z referencyjnej specyfiki obiektowych baz danych, a dokładnie fizycznej wskaźnikowej (w optymalnej formie opartej na adresach w pamięci) organizacji nawigowanych obiektów. W modelu relacyjnym takie odwołania wymagają czasochłonnych powiązań zwrotnych pomiędzy tabelami (ang. back-joins). Szczególnie dotyczy to wyrażeń ścieżkowych jednoznacznych, czyli takich, w których wszystkie nawigowane podobiekty występują pojedynczo w obiektach nadrzędnych. Ewaluacja takich wyrażeń ścieżkowych nie wymaga wtedy wykonania wielu iteracji po wielu obiektach stanowiących lewy operand operatora kropki wystarczy jedna. Dla przykładu zamiast zapamiętywania wyników całego zapytania zwracającego pensje szefów departamentów zatrudniających więcej niż jednego pracownika lub bez pracowników: (Dept where count(employs) 1).boss.Emp.salary polecane jest pominięcie całego wyrażenia ścieżkowego boss.emp.salary. W przyszłości może być ono łatwo wyliczone, gdyż podobiekty o nazwie boss mają liczność 0 lub 1 w obiektach Dept. Dodatkowo zdekomponowane zapytanie: Dept where count(employs) 1 jest znacznie prostsze, a przez to bardziej uniwersalne i może być z większym prawdopodobieństwem wykorzystane podczas ewaluacji innych zapytań dotyczących własności departamentów. Wykryte prostsze podzapytanie (bez końcowego wyrażenia ścieżkowego), po przekazaniu do etapu analizy i ewentualnym zastąpieniu wywołaniem funkcji cache'ującej, zostanie w drzewie syntaktycznym oryginalnego zapytania również zastąpione wywołaniem tej funkcji (jej wyniki symbolizuje stała f cache ): f cache.boss.emp.salary Analogicznie pomijane przy zapamiętywaniu wyników mogą być równoległe wyrażenia ścieżkowe kończące zapytanie, konstruowane za pomocą niejawnego operatora algebraicznego tworzenia struktury. Dla zapytania: 85

86 4. Koncepcja mechanizmu zapamiętanych zapytań (Training where count(received_by) > 12). (subject, duration, supervised_by.emp.salary) polecane jest pominięcie struktury wynikowej złożonej z potrójnego wyrażenia ścieżkowego (subject, duration, supervised_by.emp.salary). Zapytanie to zwraca temat, czas trwania i pensję prowadzącego dla wszystkich kursów o liczbie kursantów większej od 12. Każdy z podobiektów wyrażeń ścieżkowych wchodzących w skład wynikowej struktury występuje pojedynczo w obiektach klasy Training. Po dekompozycji zapamiętane zostaną wyniki podzapytania: Training where count(received_by) > 12 i ostatecznie oryginalne zapytanie przyjmie analogicznie jak w poprzednim przykładzie postać: f cache.(subject, duration, supervised_by.emp.salary) Usuwanie z zapytania końcowych wyrażeń ścieżkowych rozwidlających się, czyli dających w wyniku więcej niż jedną wartość, może być mniej korzystne. Wykonanie wielu iteracji w takiej sytuacji może być bardziej czasochłonne takie zapytania mogą być warte zapamiętania w całości. Przykładem może być zapytanie pobierające listę poprzednich miejsc pracy pracowników zatrudnionych aktualnie w departamencie o nazwie Database : (Dept where dname = Database ).employs.emp.prev_job Podobiekt employs w klasie Dept może występować wielokrotnie podobnie jak podobiekt prev_job w nawigowanych obiektach klasy Emp, co w wyniku takiego zapytanie daje bag bagów identyfikatorów obiektów prev_job. Jest to więc wynik warty zapamiętania, o ile zapytanie takie jest popularne. Wykrycie jednoznacznych wyrażeń ścieżkowych kończących ewaluację zapytania wymaga wykorzystania informacji o liczności obiektów zapisanej w grafie schematu bazy danych. Dekompozycja odbywa się następująco: 1. Sprawdzamy, czy w węźle startowych drzewa syntaktycznego zapytania znajduje się operator niealgebraiczny nawigacji (.). Jeżeli nie kończymy proces dekompozycji przechodząc do kroku Gdy znaleziono operator nawigacji, sprawdzamy czy prawy węzeł podrzędny węzła startowego zawiera wiązanie nazwy lub operator konstrukcji struktury (struct). Jeżeli nie kończymy proces dekompozycji przechodząc do kroku 8. 86

87 4. Koncepcja mechanizmu zapamiętanych zapytań 3. Jeżeli prawy węzeł zawiera wiązanie nazwy, sprawdzamy w grafie schematu liczność obiektu o tej nazwie. Jeżeli jest równa 1 lub 0..1 uruchamiamy rekurencyjnie algorytm począwszy od kroku 1. dla lewego podzapytania węzła startowego i przechodzimy do kroku 9. W przeciwnym wypadku sprawdzamy czy lewy węzeł zawiera również wiązanie nazwy o liczności 1 lub Jeżeli tak, uruchamiamy rekurencyjnie algorytm począwszy od kroku 1. dla lewego podzapytania węzła startowego i przechodzimy do kroku 9. W przeciwnym wypadku kończymy proces dekompozycji przechodząc do kroku Jeżeli prawy węzeł zawiera operator struct, sprawdzamy czy wszystkie węzły podrzędne tego węzła zawierają wiązanie nazwy lub operator nawigacji. Jeżeli przynajmniej jeden zawiera co innego, kończymy proces dekompozycji przechodząc do kroku Analizujemy kolejny węzeł podrzędny węzła operatora struct. Jeżeli nie ma już następnego takiego węzła (co oznacza, że wszystkie węzły zostały przeanalizowane z wynikiem pozytywnym), uruchamiamy rekurencyjnie algorytm począwszy od kroku 1. dla lewego podzapytania węzła startowego i przechodzimy do kroku Jeżeli znajduje się w nim wiązanie nazwy, sprawdzamy w grafie schematu liczność obiektu o tej nazwie. Jeżeli jest inna niż 1 lub 0..1, kończymy proces dekompozycji przechodząc do kroku 8. W przeciwnym wypadku przechodzimy do kroku Jeżeli znajduje się w nim operator nawigacji, uruchamiamy rekurencyjnie algorytm począwszy od kroku 1. dla podzapytania z tym operatorem jako węzłem startowym. Jeżeli dekompozycja tego podzapytania zwróci zapytanie zawierające cokolwiek innego, niż pojedynczy węzeł wiązania nazwy lub taki węzeł z licznością tej nazwy inną niż 1 lub 0..1, kończymy proces dekompozycji przechodząc do kroku 8. W przeciwnym wypadku przechodzimy do kroku Przekazujemy całe zapytanie do etapu analizy, czego efektem może być zastąpienie go wywołaniem funkcji cache'ującej. Jeżeli to nastąpi zwracamy jako końcowy wynik dekompozycji drzewo zawierające to wywołanie. W przeciwnym wypadku zwracamy oryginalne zapytanie. 9. Zastępujemy lewe podzapytanie zapytaniem zwróconym przez rekurencyjne wywołanie algorytmu dekompozycji i zwracamy jako końcowy wynik dekompozycji powstałe drzewo syntaktyczne. Przekazanie zdekomponowanego podzapytania do analizy pod kątem wykorzystania zapamiętanych wyników następuje w powyższym algorytmie w wielu etapach. W każdym takim przypadku po zwróceniu z etapu analizy wywołania funkcji cache'ującej następuje zamiana w kroku 9. początkowej części oryginalnego zapytania, bez końcowego wyrażenia ścieżkowego, wywołaniem tej funkcji. 87

88 4. Koncepcja mechanizmu zapamiętanych zapytań Przekształcenia oparte na wyrażeniach logicznych i operacjach mnogościowych Wynikiem zapytań bardzo często są zbiory identyfikatorów obiektów lub wartości opakowane w formę bagów (ang. bags). Operacje mnogościowe na zbiorach (suma, cześć wspólna i różnica) są wykonywane bardzo szybko, więc skomplikowane wyrażenia logiczne stojące pomiędzy algebraicznymi operatorami i tworzące razem prawy operand niealgebraicznego operatora where mogą być zastąpione prostszymi operacjami na zbiorach wyników prostszych zapytań z mniej skomplikowanymi predykatami. Taka dekompozycja zapytania nie zmienia jego semantyki ze względu na cechę rozdzielności operatora niealgebraicznego where [Sub04]. Prostsze zapytania gwarantują lepszą utylizację pamięci podręcznej wyników i wydajność ich aktualizacji po zmianach w bazie. Proponowane metody dekompozycji tego typu dotyczą: zastąpienia zapytań typu (q where p 1 or p 2 ) sumą mnogościową dwóch zapytań (q where p 1 ) i (q where p 2 ), np. zapytanie: Emp where job ( clerk, consultant ) równoznaczne zapytaniu: Emp where (job = clerk ) or (job = consultant ) może być zastąpione kolejną tożsamościową formą: (Emp where job = clerk ) (Emp where job = consultant ) w wyniku czego pojawiają się dwa prostsze zapytania warte zapamiętania, a całe zapytanie przyjmie postać: f cache1 f cache2 zastąpienia zapytań typu (q where p 1 and p 2 ) iloczynem mnogościowym dwóch zapytań (q where p 1 ) i (q where p 2 ), np. zapytanie: Emp where (job = clerk ) and (salary > 1000) przepisujemy do postaci: (Emp where job = clerk ) (Emp where salary > 1000) 88

89 4. Koncepcja mechanizmu zapamiętanych zapytań uzyskując, analogicznie jak w poprzednim przypadku, dwa bardziej uniwersalne i rokujące większe szanse na wykorzystanie podzapytania. zastąpienia zapytań typu (q where not p) mnogościową różnicą dwóch zapytań (q) i (q where p), np. zapytanie: Emp where salary 1000 równoznaczne zapytaniu: Emp where not (salary = 1000) może być zastąpione kolejną tożsamościową formą: (Emp) \ (Emp where salary = 1000) w wyniku czego uzyskuje się dwa zapytania, z których drugie, bardziej popularne od oryginalnego, staje się kandydatem do umieszczenia w rejestrze zapamiętanych zapytań. Algorytm metody sprowadza się do wykrycia w drzewie syntaktycznym zapytania lub podzapytania węzła zawierającego operator niealgebraiczny selekcji (where) i sprawdzenia czy jego prawy operand zawiera logiczny operator algebraiczny or, and lub not. Jeżeli tak, całe (pod)zapytanie podmieniane jest na zapytanie zawierające w węźle startowym mnogościowy binarny operator, lub \ odpowiednio, którego operandami stają się dwa zapytania z operatorem selekcji jako startowym (w przypadku różnicy mnogościowej dotyczy to tylko drugiego zapytania), którego lewym operandem staje się lewy operand oryginalnego operatora where, a prawym: dla pierwszego zapytania lewy operand oryginalnego operatora logicznego or lub and (dla różnicy mnogościowej pierwsze zapytanie nie ma prawego operandu); dla drugiego zapytania prawy operand oryginalnego operatora logicznego or lub and (dla różnicy mnogościowej jedyny operand oryginalnego operatora logicznego not); Analiza zapytania W trakcie i po etapie dekompozycji część lub całe zapytanie przekazywane jest do głównego modułu analitycznego optymalizatora (Rys.4.4 Analizator zapytań). Ma on trzy podstawowe zadania analizę zapytania pod kątem jego przyszłej przydatności jako zapytania zapamiętanego, sprawdzenie występowania takiego zapytania lub jego 89

90 4. Koncepcja mechanizmu zapamiętanych zapytań fragmentu w rejestrze zapytań oraz umieszczenie nowego zapytania w tym rejestrze. Analizator otrzymuje do analizy drzewo syntaktyczne zapytania stanowiące całość zapytania wejściowego przekazanego do optymalizatora lub część (w przypadku, gdy została ona wyodrębniona na etapie dekompozycji zapytania). Jako wynik analizy zwracane jest niezmienione drzewo lub zoptymalizowane drzewo zawierające wywołanie funkcji cache'ującej z wynikami zapamiętanego zapytania. Pierwszym etapem jest sprawdzenie czy zapytanie jest wystarczająco złożone, co ma zapobiec zapamiętywaniu wyników zapytań, które szybko uzyskuje się w procesie tradycyjnej ewaluacji. Następuje tutaj weryfikacja zawartości drzewa syntaktycznego zapytania i odrzucane są następujące zapytania: zapytania, w których nie pojawia się żadna nazwa obiektu bazy danych ignorowane są zatem zapytania postaci: * 3; zapytania, w których nie pojawia się jakikolwiek operator niealgebraiczny, czyli operator zależnego złączenia (join), selekcji warunkowej (where), nawigacji (operator kropki), kwantyfikator lub inny oraz operator algebraiczny konstrukcji kolekcji (struct, bag, sequence) pomijane są zapytania będące niejako kopią danych, zawierające tylko odwołanie do pojedynczego obiektu korzeniowego, np. zapytanie: Emp; zapytania zawierające w drzewie syntaktycznym odwołanie do funkcji cache'ującej (opisanej szczegółowo niżej), co oznacza, że fragment tego zapytania został już zoptymalizowany na etapie dekompozycji i całe zapytanie nie zostanie z pewnością odnalezione w rejestrze zapytań. Jeśli na etapie weryfikacji zostanie uzyskany jeden z powyższych przypadków, analiza jest kończona do dalszych etapów optymalizacji przekazywane jest oryginalne zapytanie. W przeciwnym wypadku analizator podejmuje próbę wyszukania zapytania w rejestrze zapytań. Jeżeli zostanie ono znalezione jest przepisywane wywołaniem funkcji cache'ującej pozwalającej na użycie zapamiętanych wyników, w przeciwnym wypadku staje się kandydatem na nowe zapamiętane zapytanie. Te etapy analizy optymalizacyjnej zapytania zostały szczegółowo zaprezentowane w dalszej części rozdziału. Ich realizacja wymaga komunikacji z rejestrem zapytań, który zarządzany jest przez podsystem serwera bazy danych zwany menadżerem zapamiętanych zapytań (Rys.4.4 Menadżer zapamiętanych zapytań) opisany szczegółowo w rozdziale 4.2. Wyszukiwanie zapytania w rejestrze Pierwszym krokiem po analizie złożoności zapytania, podczas której została podjęta decyzja o kontynuacji procesu, jest wyszukanie zapytania w rejestrze, a dokładniej w indeksie zapamiętanych zapytań utrzymywanym przez menadżera (Rys.4.4 Menadżer zapamiętanych zapytań: Wyszukiwanie zapytania). Podczas wyszukiwania wykonywana jest konwersja drzewa syntaktycznego do postaci tekstowej (zgodnej z formatem wartości kluczowych indeksu zapytań). Konwersja ta realizowana jest 90

91 4. Koncepcja mechanizmu zapamiętanych zapytań dokładnie takim samym sposobem, jaki jest używany podczas normalizacji zapytania oraz jego umieszczania w indeksie, dzięki czemu wygenerowany łańcuch znakowy jest zawsze taki sam dla dwóch zgodnych semantycznie zapytań nie mają znaczenia elementy lukru syntaktycznego takie jak nawiasy, białe znaki, kolejności operatorów i operandów, nazwy własne i pomocnicze obiektów. Wyszukiwanie zapytania odbywa się wg następującego algorytmu: 1. Jeżeli wraz z drzewem syntaktycznym została przekazana do analizatora informacja o konieczności wykorzystania częściowych zapamiętanych wyników (np. na skutek wykrycia na etapie dekompozycji podzapytania agregującego), następuje przejście do kroku Jeżeli nie następuje zamiana drzewa syntaktycznego do postaci tekstowej, która przekazywana jest do menadżera zapamiętanych zapytań w celu wyszukania w indeksie. 3. Jeżeli takie zapytanie zostanie znalezione w rejestrze, jego drzewo syntaktyczne jest zastępowane wywołaniem funkcji cache'ującej parametryzowanej identyfikatorami węzła tego zapytania w metabazie (jest on dostępny w indeksie zarządzanym przez menadżera natychmiast po znalezieniu zapytania) oraz węzła w bazie danych przechowującego wyniki zapytania. Parametry te są przekazywane przez menadżera pamięci podręcznej na podstawie danych zapisanych w rejestrze zapamiętanych zapytań. Szczegóły dotyczące funkcji cache'ującej zostały podane dalej. Analiza zapytania jest kończona zwracane jest zoptymalizowane drzewo syntaktyczne zapytania. 4. Następuje sprawdzenie czy zapytanie spełnia warunki wykorzystania częściowych wyników innego zapytania. Jest to możliwe gdy, w węźle startowym znajduje się niealgebraiczny operator nawigacji (.), jego lewy podwęzeł zawiera wiązanie nazwy obiektu korzeniowego, a prawy jeden z algebraicznych operatorów agregujących (sum, avg, min, max lub count). Jeżeli test zwróci wynik negatywny, następuje przejście do kroku Tworzona jest kopia zapytania wejściowego z podmienionym w węźle startowym operatorem nawigacji na operator złączenia (join). Następuje zamiana nowego drzewa syntaktycznego do postaci tekstowej, która przekazywana jest do menadżera zapamiętanych zapytań w celu wyszukania w indeksie. Jeżeli takie zapytanie zostanie znalezione, drzewo syntaktyczne oryginalnego zapytania jest zastępowane wywołaniem funkcji cache'ującej parametryzowanej identyfikatorami węzła znalezionego zapytania w metabazie, węzła w bazie danych przechowującego wyniki tego zapytania oraz dodatkowo trzecim parametrem będącym nazwą obiektu korzeniowego wykrytego w poprzednim kroku. Ten dodatkowy parametr pozwoli wyodrębnić z wyników zapamiętanego zapytania tą część, która jest wyznaczana przez zbiór identyfikatorów obiektów korzeniowych, które zostaną obliczone podczas 91

92 4. Koncepcja mechanizmu zapamiętanych zapytań ewaluacji zapytania na skutek wiązania tej nazwy. Szczegóły dotyczące funkcji cache'ującej zostały podane dalej. Analiza zapytania jest kończona zwracane jest zoptymalizowane drzewo syntaktyczne zapytania. 6. Jeżeli zapytanie nie zostało znalezione w rejestrze, jako ostateczny krok następuje umieszczenie go w rejestrze (zgodnie ze scenariuszem opisanym w dalszej części rozdziału) i analogicznie jak w kroku 3. zastąpienie jego drzewa syntaktycznego wywołaniem funkcji cache'ującej z odpowiednimi parametrami wygenerowanymi przez menadżera dla nowo utworzonego zapamiętanego zapytania. W najczęstszym przypadku wykorzystania zapamiętanych zapytań, o ile do serwera bazy danych często kierowane są te same zapytania, na etapie analizy nastąpi zastąpienie całego zapytania wywołaniem funkcji cache'ującej, lecz w niektórych przypadkach będzie zastępowane tylko poddrzewo zapytania. Wywołanie funkcji cache'ującej jest standardowym wywołaniem zgodnym z semantyką wywołania funkcji i procedur w SBA. W związku z tym funkcja ta jest przechowywana w bazie danych na najwyższym poziomie hierarchii obiektów, ma specjalną, unikalną nazwę (np. $cache_fun) i posiada dwa lub trzy parametry: identyfikator i Q M zapamiętanego zapytania przechowywany w indeksie zapytań będący referencją węzła w metabazie odpowiadającego obiektowi zapytania utworzonemu do zapamiętania wyników (parametr obowiązkowy, będący liczbą całkowitą); identyfikator i Q zapamiętanego zapytania przechowywany w węźle metabazy zapytania będący referencją obiektu zapytania utworzonemu do zapamiętania wyników (parametr obowiązkowy, będący liczbą całkowitą) w zależności od opcji zapisanej w tym węźle może to być identyfikator obiektu zapytania przechowywanego w pamięci trwałej (gdy wyniki są tylko w pamięci trwałej) lub ulotnej (gdy wyniki są w pamięci ulotnej lub dodatkowo też w trwałej); nazwa obiektu korzeniowego przekazywany w celu okrojenia wyników (parametr opcjonalny, ewaluowany podczas interpretacji jako bag(i O1, i O2,, i On ), czyli lista identyfikatorów obiektów będąca podzbiorem identyfikatorów tworzących pierwsze elementy struktur wchodzących w skład zbioru wyników zapytania). Wywołanie tej funkcji przyjmuje więc jedną z dwóch postaci: $cache_fun(i QM, i Q ) $cache_fun(i QM, i Q, NazwaObiektuKorzeniowego) Zastąpienie drzewa syntaktycznego zapytania takim wywołaniem skutkuje pojawieniem się w jego miejsce nowego drzewa syntaktycznego z operatorem wywołania funkcji jako węzłem startowym i dwoma węzłami podrzędnymi: lewym 92

93 4. Koncepcja mechanizmu zapamiętanych zapytań zawierającym nazwę funkcji i prawym zawierającym operator niealgebraiczny konstrukcji struktury zawierającej parametry funkcji. Poniżej zaprezentowano kilka przykładów zamiany syntaktycznej zapytania. Przykład 4.1 Załóżmy, że w rejestrze zapytań zapamiętane jest zapytanie Q 1 postaci: Emp join (works_in.dept) zwracające pracowników wraz z departamentami, w których są zatrudnieni. Rozważmy bieżące zapytanie poddawane procesowi optymalizacji: (Emp join (works_in.dept)).(name, dname) Zapytanie to zwraca nazwiska i nazwy odpowiednio pracowników i ich departamentów. Proces dekompozycji tego zapytania doprowadzi do wyodrębnienia z drzewa syntaktycznego tylko początkowej części stojącej przed operatorem nawigacji (kropka) bez końcowego wyrażenia ścieżkowego. Takie podzapytanie przekazane zostanie do etapu analizy jest ono zapytaniem złożonym, więc zostanie przekazane do menadżera zapytań w celu odnalezienia go w rejestrze. Znalezione zostanie zapytanie Q 1 i wskazane poddrzewo zapytania będzie zastąpione wywołaniem funkcji cache'ującej. Modyfikacja ta jest przedstawiona na Rys.4.5, gdzie wywołanie funkcji cache'ującej $cache_fun(i Q1M, i Q1 ) z odpowiednimi identyfikatorami przechowywanymi w rejestrze oznaczono nazwą cacheq1, reprezentującą wyniki zapamiętanego zapytania Q 1. (Emp join works_in.dept).(name, dname) Start. Start. join struct cacheq1 struct Emp. name dname name dname works_in Dept Drzewo wejściowe Drzewo wynikowe Rys.4.5. Modyfikacja syntaktyczna zapytania z użyciem zapamiętanego zapytania 93

94 4. Koncepcja mechanizmu zapamiętanych zapytań Przykład 4.2 Przyjrzyjmy się kolejnemu zapamiętanemu zapytaniu Q 2 : Dept join avg(employs.emp.salary) Zapytanie to pobiera dla każdego departamentu średnie wynagrodzenie pracowników w nim zatrudnionych i było zaprezentowane wcześniej jako przykład dekompozycji z wyodrębnieniem funkcji agregujących. Załóżmy, że w rejestrze zapytań nie zostało zapamiętane zapytanie: Emp where salary > (works_in.dept.avg(employs.emp.salary)) Zgodnie z wspomnianą regułą dekompozycji wyodrębnione zostanie podzapytanie: Dept.avg(employs.Emp.salary) Wyniki tego podzapytania będą wyznaczone jako podzbiór wyników zapytania Q 2. Rys4.6 przedstawia zamianę fragmentu drzewa syntaktycznego zapytania z wykorzystaniem parametryzowanej funkcji cache'ującej. Emp where salary > works_in.dept.avg(employs.emp.salary) Start Start where where Emp > Emp > deref. deref. salary. avg salary works_in cacheq2(dept) works_in Dept deref.. salary employs Emp Drzewo wejściowe Drzewo wynikowe Rys.4.6. Modyfikacja syntaktyczna zapytania z użyciem części wyników zapamiętanego zapytania Wywołanie tej funkcji cache'ującej $cache_fun(i Q2M, i Q2, Dept) oznaczono w sposób uproszczony cacheq2(dept). Tym razem funkcja ma dodatkowy parametr reprezentujący obiekty klasy Dept. Podczas ewaluacji zoptymalizowanego zapytania na stosie ENVS na skutek działania operatora kropki pojawi się binder odpowiedniego 94

95 4. Koncepcja mechanizmu zapamiętanych zapytań obiektu tej klasy (reprezentującego departament zatrudniający danego pracownika), którego wiązanie nastąpi podczas ewaluacji parametru funkcji cache'ującej. Ten wyznaczony identyfikator posłuży do odfiltrowania z wyników zapamiętanego zapytania Q 2 tylko tych, które są z nim powiązane. Szczegóły tej operacji wyjaśnione są w następnym rozdziale dotyczącym interpretacji. Dodawanie zapytania do rejestru W przypadku braku analizowanego zapytania w rejestrze zapytań, uruchamiany jest proces dodania nowego zapamiętanego zapytania (Rys Menadżer zapamiętanych zapytań: Dodawanie zapytania). Na etapie optymalizacji nie są dostępne wyniki takiego zapytania, więc w rejestrze umieszczane są wszystkie znane informacje o zapytaniu z flagą informującą, że zapytanie musi zostać ewaluowane przy najbliższej próbie wykorzystania (jest to tzw. zapytanie niepełne ). Ewaluacja ta, której skutkiem będzie umieszczenie właściwych wyników w rejestrze, zostanie wykonana przez interpreter. Podczas tworzenia zapytania niepełnego menadżer zapamiętanych zapytań wykonuje następujące operacje: Menadżer danych tworzy nowy obiekt w bazie danych (w pamięci ulotnej, trwałej lub obu jednocześnie w zależności od ustawień systemu) zapisując w nim oryginalne zapytanie oraz dodatkowe struktury na potrzeby aktualizacji wyników zapytania, inicjalizuje w nim statystyki użycia zapytania oraz ustawia flagę konieczności aktualizacji wyników. Identyfikator tego obiektu jest zapamiętywany jako i Q (osobno dla pamięci ulotnej i trwałej w zależności od ustawień mogą zostać wyznaczone oba identyfikatory lub tylko jeden z nich); Menadżer metadanych tworzy nowy węzeł w metabazie zapisując w nim identyfikator obiektu utworzonego w bazie danych (i Q w pamięci trwałej, ulotnej lub oba), informację o miejscu zapisania danych oraz sygnaturę zapytania sygnatura ta jest jednoznaczną informacją o typie wyników zapytania potrzebną podczas kontroli typologicznej i jest wyliczana za pomocą statycznej ewaluacji zapamiętywanego zapytania. Identyfikator tego węzła w metabazie jest zapamiętywany jako i QM. Dla zapytania Q 1 z Przykładu 4.1 zostanie zapisana sygnatura bag{struct{i Emp, i Dept }}, natomiast dla zapytania Q 2 z Przykładu 4.2: bag{struct{i Dept, real}}; Menadżer zapamiętanych zapytań zamienia zapytanie na postać tekstową i dodaje je do indeksu zapytań razem z identyfikatorem węzła tego zapytania utworzonego w metabazie (i QM ). Po zarejestrowaniu nowego zapytania w rejestrze następuje zamiana drzewa syntaktycznego tego zapytania na odwołanie do funkcji cache'ującej z identyfikatorami węzłów tego zapytania w metabazie i bazie jako parametrami. Tak zmodyfikowane 95

96 4. Koncepcja mechanizmu zapamiętanych zapytań drzewo syntaktyczne przekazywane jest do ostatniego etapu optymalizacji aktualizacji typologicznej Aktualizacja typologiczna zapytania Po zakończeniu analizy i ewentualnej modyfikacji zapytania jest ono przekazywane do ostatniego etapu procesu optymalizacji polegającego na zaktualizowaniu typologicznym drzewa syntaktycznego zapytania w nowej postaci (Rys.4.4 Optymalizator cache'ujący: Aktualizacja typów). Jest to konieczne przed przekazaniem go do kolejnych etapów optymalizacji lub do ostatecznego wykonania przez interpreter. W tym celu następuje uruchomienie statycznego analizatora typologicznego przetwarzającego drzewo jak przed optymalizacją. Jeśli podczas ewaluacji typologicznej nastąpi wykrycie w planie ewaluacji zapytania sygnatury funkcji cache'ującej, następuje odwołanie do modułu Menadżera metadanych w celu wydobycia sygnatury oryginalnego zapamiętanego zapytania. Sygnatura ta jest pozyskiwana jednoznacznie na podstawie identyfikatora węzła tego zapytania zapisanego w metabazie, a parametryzującego wywołanie funkcji cache'ującej (i QM ). Została ona tam umieszczona podczas dodawania zapytania do rejestru zapamiętanych zapytań jako informacja o typie wyników tego zapytania. Pozwala to w kolejnych etapach przetwarzania zapytania dokonać analizy typologicznej całego zapytania bez konieczności ponownej statycznej analizy zapamiętanego podzapytania, co wpływa na przezroczystość opisywanej optymalizacji dla innych optymalizatorów i dodatkowo przyspiesza wykonanie zapytania. Szczegółowy opis mechanizmu stosowanej tradycyjnej analizy typologicznej zapytań znajduje się w rozdziale 3.5. Optymalizator wykorzystujący zapamiętane zapytania wprowadza tutaj jedną zmianę w przypadku znalezienia w drzewie syntaktycznym zapytania odwołania do funkcji cache'ującej $cache_fun sygnatura dla takiego węzła pobierana jest z odpowiedniego miejsca metabazy i następnie: jeżeli funkcja cache'ująca nie miała trzeciego parametru, węzłowi temu przypisywana jest sygnatura dokładnie zgodna z zapamiętaną w metabazie dla funkcji reprezentującej pobranie wyników zapytania Q 1 z Przykładu 4.1 będzie to: bag{struct{i Emp, i Dept }}; 96

97 4. Koncepcja mechanizmu zapamiętanych zapytań jeżeli funkcja cache'ująca miała trzeci parametr, następuje usunięcie z zapamiętanej w matabazie sygnatury pierwszego elementu najbardziej zewnętrznej struktury (pierwszej kolekcji struct w sygnaturze) i jeżeli po takiej operacji w tej strukturze pozostanie tylko jeden element całkowite pominięcie konstruktora tej kolekcji. Dla funkcji reprezentującej pobranie wyników zapytania Q 2 z Przykładu 4.2 z dodatkowym parametrem Dept mamy: sygnatura zapytania Q 2 wynosi bag{struct{i Dept, real}}, po odrzuceniu pierwszego elementu struktury jest redukowana do bag{struct{real}} i ostatecznie do postaci bag{real}. Po zaktualizowaniu typologicznym każdego węzła w drzewie syntaktycznym zapytania zawierającego odwołanie do funkcji cache'ującej drzewo to jest odpowiednio przygotowane do przekształcenia go do postaci skompilowanej nadającej się do wykonania przez interpreter zapytań. 4.4 Interpretacja zapytań i zarządzanie zasobami Finalnym etapem przetwarzania zapytania kierowanego do bazy danych, zgodnie z Rys.4.1 i Rys.4.2, jest jego ewaluacja wykonywana przez podsystem zwany interpreterem. Zgodnie z planem ewaluacji zapytania przygotowanym na wcześniejszych etapach, przede wszystkim na skutek pracy szeregu optymalizatorów, generowane są wyniki zapytania w oparciu o aktualny stan bazy danych, czyli stosy ENVS i QRES (punkt 7. opisanego w rozdziale 4.1 scenariusza przetwarzania zapytań). W kontekście optymalizacji wykorzystującej zapamiętywanie wyników główną zmianą wnoszoną przez tą optymalizację do procesu interpretacji jest konieczność odwołania do menadżera zapamiętanych zapytań w przypadku pojawienia się w planie ewaluacji wywołania funkcji cache'ującej wstawionej przez optymalizator (Rys.4.2 Interpretacja: Pobranie danych z cache'u). Menadżer pamięci podręcznej w pierwszej kolejności pozyskuje wyniki wskazanego zapamiętanego zapytania, a następnie, jako efekt uboczny tego zadania, aktualizuje statystyki dotyczące wykorzystania danego zapytania. W konsekwencji utrzymywanie takich statystyk umożliwia optymalne wykorzystanie zasobów przydzielonych dla pamięci podręcznej wyników oraz zwiększa prawdopodobieństwo wykorzystania tej metody optymalizacyjnej. W ramach dostosowywania się pamięci podręcznej do aktualnych potrzeb konieczne jest wprowadzenie mechanizmu przeczyszczania pamięci podręcznej z danych o małej wartości optymalizacyjnej. Rozszerzanie pamięci podręcznej wynikami kolejnych zapytań zwiększa prawdopodobieństwo wykorzystania tych danych podczas ewaluacji kolejnych zapytań, jednak z drugiej strony zmniejsza wydajność wyszukiwania odpowiednich zapytań oraz ich aktualizacji po zmianach w bazie. Stąd niektóre zapytania nie powinny być materializowane (część takich zapytań jest 97

98 4. Koncepcja mechanizmu zapamiętanych zapytań odfiltrowywana już na etapie optymalizacji opisanej wcześniej), a inne powinny być usuwane z pamięci podręcznej w celu udostępnienia wolnych zasobów. Do pierwszej grupy należą: zapytania, których wyniki mają zbyt duży rozmiar, czyli najczęściej proste zapytania, łatwe w ewaluacji, zwracające wiele obiektów danego typu, najczęściej po prostu identyfikatory tych obiektów (takie zapytania nie zawierają operatorów niealgebraicznych); zapytania, których wyniki są szybko ewaluowane i nie wymagają zapamiętywania (np. zapytania nieodwołujące się do bazy danych złożone z samych literałów i operatorów algebraicznych); zapytania, po wyniki których nie odwołano się wystarczającą liczbę razy aby nie zapisywać wyników unikalnych zapytań; zapytania angażujące podczas ewaluacji rozległy obszar (schemat) bazy danych, szacowany i zapisywany wraz z wynikami podczas umieszczania zapytania w rejestrze zapytań wyniki takich zapytań byłyby zapewne zbyt często aktualizowane po prawie każdej modyfikacji danych; zapytania, które nie spełniają innych warunków określanych przez administratora systemu, np. zapytania, których wyniki nie mogą być częściowo zaktualizowane i wymagają zawsze pełnej reewaluacji. Dzięki występowaniu w rejestrze zapytań tzw. zapytań niepełnych - kandydatów do zapamiętania, czyli zapytań umieszczonych w rejestrze na etapie optymalizacji bez zapamiętanych jeszcze wyników, interpreter może na tym etapie podjąć decyzje dotyczące faktycznej konieczności zapisania wyników po pierwszej i kolejnych pełnych ewaluacjach takich zapytań. W podjęciu takich decyzji mogą wspierać go parametry konfiguracyjne optymalizacji ustawiane przez administratora systemu, czyli np. maksymalny rozmiar zapytania, minimalny czas ewaluacji zapytania, minimalna liczba prób ewaluacji przed zapamiętaniem wyników (opisane w rozdziale 4.2). Drugim aspektem adaptacyjnym jest pozbywanie się z rejestru niewygodnych zapamiętanych zapytań, do których należą: zapytania rzadko używane lub nieużywane przez długi czas; zapytania o często aktualizowanych wynikach. Ta optymalizacja zasobów odbywa się na poziomie menadżera zapamiętanych wyników z wykorzystaniem gromadzonych przez niego na bieżąco statystyk opisanych w rozdziale 4.2. Na Rys.4.7 przedstawiono schemat działania omówionych dalej aspektów interpretacji zapytań wprowadzających wykorzystanie zapamiętanych wyników wcześniejszych zapytań. Skróty DB i TMP na tym rysunku oznaczają operacje wykonywane w obszarze odpowiednio pamięci trwałej i ulotnej opisanej w rozdziale 4.2. Rysunek przedstawia kolejne etapy interpretacji oraz zarządzania rejestrem zapytań 98

99 4. Koncepcja mechanizmu zapamiętanych zapytań realizowane głównie przez menadżera zapamiętanych zapytań. W kolejnych podrozdziałach znajduje się ich szczegółowy opis. Rys.4.7. Schemat przebiegu interpretacji zapytania 99

100 4. Koncepcja mechanizmu zapamiętanych zapytań Użycie zapamiętanych wyników Wykrycie w planie ewaluacji zapytania (najczęściej jest to skompilowane drzewo syntaktyczne) wywołania funkcji cache'ującej $cache_fun(i QM, i Q, NazwaObiektuKorzeniowego) podczas interpretacji zoptymalizowanego zapytania prowadzi do przekazania zlecenia do menadżera pamięci podręcznej w celu pobrania zapamiętanych w bazie danych wyników zapytania (Rys.4.7 Menadżer zapamiętanych zapytań: Pobieranie zapamiętanych wyników). Wyniki te, o ile były już ewaluowane, są zgromadzone w odpowiednim obiekcie bazy danych, którego identyfikator przekazywany jest jako drugi parametr funkcji cache'ującej (i Q ). Poza wynikami obiekt ten zawiera również plan ewaluacji oryginalnego zapytania, informację o ewentualnej konieczności aktualizacji zapytania (dla zapytań jeszcze nie wykorzystywanych lub wymagających aktualizacji po zmianach w bazie), licznik użycia zapytania oraz dodatkowe struktury na potrzeby aktualizacji wyników. Zarządzaniem tymi danymi zajmuje się w obrębie menadżera zapamiętanych zapytań podsystem Menadżer danych. Wyniki przekazane interpreterowi są przez niego umieszczane na stosie QRES w dokładnie taki sam sposób jak w sytuacji gdy w miejsce wywołania funkcji cache'ującej nastąpiła ewaluacja reprezentowanego przez nie drzewa syntaktycznego oryginalnego zapamiętanego zapytania. Zgodność semantyczna i typologiczna została zagwarantowana na etapie optymalizacji i aktualizacji typologicznej. Podczas realizacji żądania przekazania interpreterowi zapamiętanych wyników wskazanego zapytania Menadżer danych wykonuje następujące kroki operując na danych zapisanych w pamięci w obiekcie identyfikowanym przez referencję i Q przekazaną w parametrze funkcji cache'ującej: 1. W pierwszej kolejności następuje zwiększenie licznika użycia zapytania, a następnie w oparciu o ten licznik aktualizowane są globalne statystyki użycia wyników zapytań oparte na listach MRU, utrzymywanych przez menadżera zapytań niezależnie dla danych zapamiętanych w pamięci trwałej i ulotnej. Struktura list MRU została opisana w rozdziale 4.2. Jeżeli licznik po zwiększeniu osiągnie wartość z zakresu kolejnego poziomu spośród zdefiniowanych przez administratora, zapytanie to jest przesuwane do tego poziomu wraz z przesunięciem zajętości pomięci pomiędzy tymi poziomami (o ile wyniki są już zapamiętane flaga konieczności aktualizacji jest wyłączona). 2. W zależności od ustawienia flagi konieczności aktualizacji zapytania następuje wygenerowanie nowych danych (wykonywany jest krok 3., Rys.4.7 Menadżer zapamiętanych zapytań: Aktualizacja wyników) lub pobranie danych 100

101 4. Koncepcja mechanizmu zapamiętanych zapytań zapamiętanych w węźle bazy danych (wykonywany jest krok 8., Rys.4.7 Menadżer zapamiętanych zapytań: Odczyt zapamiętanych wyników). 3. Następuje usunięcie wyników zapytania zarówno z pamięci trwałej jak i ulotnej, a w związku z tym również aktualizacja w statystyce MRU rozmiaru pamięci zajmowanej przez poziom, do którego aktualnie należy zapytanie. Rozmiary te są zmniejszane o wielkość pamięci zajmowaną wcześniej przez wyniki tego zapytania. 4. Zwiększany jest licznik aktualizacji wyników zapytania chyba, że licznik użycia jest równy 1, co oznacza pierwszą ewaluację zapytania. Flaga konieczności aktualizacji jest wyłączana. 5. Uruchamiany jest proces tradycyjnej (ewentualnie zmienionej na podstawie informacji aktualizacyjnych) ewaluacji zapytania. Jest ona dokonywana przez interpreter poprzez wykonanie planu ewaluacji zapamiętanego oryginalnego zapytania. Plan ten jest przekazywany do nowej instancji interpretera powołanej dla reewaluacji wyników zapamiętanego zapytania. 6. Po uzyskaniu wyników obliczany jest ich rozmiar oraz czas ewaluacji i wraz z wcześniej obliczonym licznikiem użycia zapytania dane te przekazywane są do zapisania niezależnie w statystykach dla pamięci trwałej i ulotnej. 7. Dla każdego zleconego miejsca przechowywania (dla pamięci trwałej lub ulotnej) sprawdzana jest informacja czy jest ono aktywne, czy liczba prób użycia zapytania jest wystarczająca do jego zapamiętania, czy czas ewaluacji nie jest zbyt krótki oraz rozmiar zbyt duży. Wszystkie te brzegowe parametry są pobierane z ustawień menadżera. Jeżeli wszystkie warunki są spełnione, wyniki są zapisywane i aktualizowana jest odpowiednia (dla pamięci trwałej lub ulotnej) statystyka MRU. Następuje zwiększenie wartości zajmowanego rozmiaru pamięci dla składnika odpowiedniej statystyki MRU reprezentującego poziom użycie danego zapytania. W przeciwnym wypadku wyniki nie są zapamiętywane w danym miejscu przechowywania. W szczególnej sytuacji wyniki mogą nie zostać zapamiętane gdziekolwiek. Niezależnie od miejsca składowania obliczone wyniki są przekazywane jako rezultat działania menadżera pamięci podręcznej wyników. 8. Gdy ponowne generowanie wyników zapytania nie jest konieczne, następuje sprawdzenie czy znajdują się one w pamięci ulotnej. Jeżeli tak, są one zwracane i jednocześnie przekazywane wraz z towarzyszącymi im parametrami i licznikami do zapisania w pamięci trwałej zgodnie z mechanizmem opisanym wyżej (uruchamiany jest krok 7. ze zleceniem zapisu wyników w pamięci trwałej). W przeciwnym wypadku odbywa się wyszukanie wyników w pamięci trwałej i, jeżeli zostaną znalezione, analogiczne zwrócenie ich i przekazanie do zapisania w pamięci ulotnej (uruchamiany jest krok 7. ze zleceniem zapisu wyników w pamięci ulotnej). Ostatecznie, gdy jednak wyników nie odnaleziono, 101

102 4. Koncepcja mechanizmu zapamiętanych zapytań interpreter uruchamia oryginalne zapytanie w celu ich uzyskania zgodnie z wcześniejszym opisem (uruchamiany jest krok 5.). Sytuacja taka dotyczy zapytań, które ze względu na niespełnienie warunków konfiguracyjnych nie mogą nigdy (np. ze względu na zbyt duży rozmiar wyników) lub jeszcze nie mogą (np. ze względu na zbyt niską liczbę ponownych użyć) zostać zapamiętane. Powyższy algorytm zakłada możliwość odwołania zwrotnego ze strony menadżera zapamiętanych zapytań do modułu interpretera zlokalizowanego w części klienckiej systemu w celu pełnej ewaluacji wyników zapamiętanego zapytania. Dotyczy to sytuacji, w których zapytanie nie było jeszcze ewaluowane (zapytanie niepełne ) lub konieczna jest aktualizacja wyników. W tej sytuacji serwer bazy danych, a dokładnie pracujący w ramach niego moduł menadżera pamięci podręcznej jest niejako własnym klientem zlecającym wykonanie wybranego zapytania, którego treść została zapamiętana Optymalizacja wykorzystania zasobów Tuż przed zakończeniem przetwarzania funkcji cache'ującej i zwróceniem jej rezultatu, czyli wyników zapamiętanego zapytania, uruchamiany jest mechanizm optymalizacji zajętości pamięci podręcznej wyników, której objętość mogła zmienić się na skutek ewaluacji lub aktualizacji zapytania (Rys.4.7 Menadżer zapamiętanych zapytań: Zarządzanie zasobami). Na podstawie konfiguracji systemu, czyli parametru maksymalnej dopuszczalnej zajmowanej części pamięci podręcznej, następuje sprawdzenie czy rejestr zapytań wymaga objętościowego zredukowania i jeżeli tak to w jakim stopniu. Oto kolejne kroki optymalizacji wykorzystania pamięci podręcznej wykonywanej przez menadżera zapamiętanych zapytań jako efekt uboczny wydobywania wyników: 1. W pierwszej kolejności zarówno dla pamięci trwałej jak i ulotnej obliczany jest tzw. indeks redukcji. Podstawowymi elementami umożliwiającymi wygenerowanie tego indeksu są listy poziomów użycia zapamiętanych zapytań tworzące statystyki MRU użycia zapytań. Zgodnie z definicją tych statystyk opisaną w rozdziale 4.2, dla każdego poziomu użycia pamiętany jest i aktualizowany na bieżąco rozmiar wszystkich zapamiętanych zapytań o licznikach użycia z zakresu definiującego ten poziom. Następuje zsumowanie rozmiarów wyników zapytań dla wszystkich poziomów obecnych w listach MRU. 2. Jeżeli sumaryczny rozmiar nie przekracza dopuszczalnego zarówno dla pamięci trwałej jak i ulotnej, indeks redukcji ustawiany jest na wartość -1, która oznacza brak redukcji zajętości. Proces kończy się. 102

103 4. Koncepcja mechanizmu zapamiętanych zapytań 3. W przeciwnym wypadku następuje wyznaczenie tych poziomów użycia, począwszy od najniższych, dla których sumaryczny rozmiar ich zapytań jest wystarczający, aby po ich usunięciu zajętość pamięci nie przekraczała dopuszczalnej i jednocześnie była niewiększa od tzw. optymalnej zajętości. Ten parametr też jest konfigurowany przez administratora bazy danych, aby zapobiec zbyt częstym redukcjom zajętości pamięci podręcznej. W pierwszej kolejności będą więc usuwane zapamiętane zapytania o najmniejszej liczbie użyć, co pozwoli wykorzystać dostępne zasoby dla wyników wartościowszych, częściej wykorzystywanych podczas optymalizacji zapytań. Górne ograniczenie zakresu najwyższego z wybranych poziomów jest przyjmowane jako indeks redukcji. Wybierany jest większy z indeksów redukcji obliczonych dla pamięci trwałej i ulotnej. Jeżeli obliczonym indeksem jest pseudowartość (będąca górnym ograniczeniem ostatniego poziomu), oznaczająca konieczność usunięcia wszystkich zapytań, indeks redukcji przyjmuje wartość Jeśli indeks redukcji przyjmie wartość 0, czyszczona jest cała zawartość pamięci podręcznej wyników zapytań wszystkie węzły zapamiętanych zapytań z metabazy oraz bazy danych, indeks zapytań oraz statystyki użycia zapytań z pamięci trwałej i ulotnej (Rys.4.7 Menadżer zapamiętanych zapytań: Czyszczenie cache'u). Proces kończy się. 5. W przeciwnym wypadku z pamięci usuwane są dane tych zapytań, których licznik użycia jest mniejszy od wyznaczonego indeksu redukcji (Rys.4.7 Menadżer zapamiętanych zapytań: Redukcja cache'u). Dla każdego takiego zapytania Menadżer danych usuwa jego obiekt w bazie danych (zarówno w pamięci trwałej jak i ulotnej), Menadżer metadanych usuwa węzeł w metabazie i ostatecznie usuwany jest wpis w indeksie zapytań. Są to wszystkie zapytania z wybranych wcześniej poziomów użycia, więc statystyki dotyczące tych zakresów są również czyszczone Menadżer danych zeruje rozmiar i liczbę zapytań dla każdego takiego poziomu w obrębie statystyk dla pamięci trwałej i ulotnej. Proces kończy się. Zapytania, które przetrwały proces redukcji pamięci podręcznej, czyli miały odpowiednio wysokie liczniki użycia, mogą finalnie zostać zdegradowane (opcjonalnie, wg ustawień administratora) poprzez wyzerowanie ich liczników użycia. Pociąga to za sobą wyczyszczenie całkowite statystyk użycia zapytań. Zachowanie takie ma na celu równouprawnienie kolejnych nowych zapytań zapamiętywanych w pamięci podręcznej, co zapobiega tendencji do usuwania zapytań często wykorzystywanych w ostatnim czasie, ale nie częściej niż tych, które miałyby wyższe liczniki użycia wyprodukowane dużo wcześniej i niezwiększane od dłuższego czasu. Dzięki temu zasoby pamięci podręcznej mogą być wykorzystanie optymalnie i mogą dostosowywać się automatycznie do charakteru zapytań kierowanych aktualnie do bazy danych najczęściej. 103

104 5. Aktualizacja wyników zapamiętanych zapytań 5. Aktualizacja wyników zapamiętanych zapytań Kolejnym problemem, który dotyczy metody optymalizacyjnej polegającej na zapamiętywaniu zapytań, jest korekta wyników zapamiętanych zapytań w odpowiedzi na aktualizację bazy danych. Po zmianach w bazie zapamiętane wyniki niektórych zapytań mogą być niepoprawne. Problem ten rozpada się na dwa niezależne podproblemy: wyeliminowanie z procesu korekty tych zapamiętanych zapytań, które z pewnością nie zmieniły swojego wyniku; korekta pozostałych zapytań lub ich usunięcie z rejestru. Zapamiętanych zapytań mogą być miliony, więc aktualizacja tak ogromnej ilości danych po modyfikacji bazy danych mogłaby zaburzyć cały proces optymalizacyjny, zwiększając znacznie koszt przetwarzania zapytań. Istotne jest zatem opracowanie metody unikania tak dużego kosztu reewaluacji zapytań poprzez wyeliminowanie z tego procesu jak największej ich liczby, których modyfikacja nie dotyczyła i w dalszej kolejności optymalna korekta wyników pozostałych. Oczywiście zbyt częste modyfikacje danych w bazie całkowicie podważają ideę zapamiętywania wyników zapytań, stąd istotne jest również określenie kryterium pozwalającego podjąć decyzję o sensowności tej metody optymalizacyjnej w konkretnym środowisku bazy danych. 5.1 Metoda eliminacji Najważniejszą pożądaną cechą metody eliminacyjnej jest możliwość szybkiego, automatycznego rozpoznania na jej podstawie możliwie najmniejszej grupy zapamiętanych zapytań, których wyniki mogły zostać zmienione po modyfikacji bazy danych. W niniejszej rozprawie wzorowano się w tej kwestii na pracach zaprezentowanych w [SR87, RS88]. Metoda tam przedstawiona, zwana metodą eliminacji, ma zastosowanie praktycznie do każdego języka zapytań. Zaadoptowano ten mechanizm do języka SBQL w kontekście podejmowanego podejścia stosowego do języków zapytań w obiektowych bazach danych. Pewne specyficzne reguły dotyczące obiektowej bazy danych wymuszają opracowanie odpowiedniej architektury i struktur danych dla wydajnego utrzymania stanu pamięci podręcznej wyników w zgodzie ze stanem bazy danych. 104

105 5. Aktualizacja wyników zapamiętanych zapytań Metoda eliminacji zakłada, że całkowita liczba zapamiętanych zapytań jest znacznie większa niż zapytań, których wyniki zmieniają się po modyfikacji danych. Stąd podstawą strategii jest odfiltrowanie z procesu aktualizacji wyników zapytań, które na pewno nie miały związku z konkretną zmianą w bazie. W zmienionej bazie danych ich wyniki są nadal aktualne, więc nie muszą być brane pod uwagę podczas procesu synchronizacji danych, co znacznie przyspiesza ten proces. W celu realizacji tego podejścia opracowano koncepcję odpowiedniego podschematu zarówno dla zapamiętanego zapytania, jak i zapytania wymuszającego zmianę w bazie danych. Podschemat odpowiadający zapamiętanemu zapytaniu definiuje tą część schematu bazy danych, która jest odpowiednia i niezbędna do uzyskania wyników tego zapytania. Innymi słowy, obiekty bazy danych opisane poprzez podschemat są wystarczające (być może również konieczne) do kompletnej ewaluacji danego zapytania. Inne elementy bazy danych, nieuwzględnione w podschemacie, mogą być zignorowane podczas ewaluacji zapytania, ze względu na ich brak wpływu na wyniki zapytania. Podobnie, podschemat odpowiedni dla modyfikacji bazy danych zawiera specyfikację schematu fragmentu danych opisującego część bazy danych, która mogła być zmieniona, usunięta lub dodana na skutek tej modyfikacji. Podschematy są generowane dla każdego zapytania zapamiętywanego w rejestrze zapytań oraz każdego żądania modyfikacji bazy danych. W przypadku zapytania podschemat generowany jest w momencie dodawania tego zapytania do rejestru zapytań i jest przechowywany w odpowiednim węźle bazy danych razem z oryginalnym zapytaniem, wynikami oraz innym informacjami statystycznymi i zarządczymi. Taki podschemat jest opracowywany na podstawie drzewa syntaktycznego zapytania i schematu bazy zapisanego w metabazie. Podschemat modyfikacji jest generowany jako efekt uboczny tej modyfikacji podczas jej realizacji dotyczy to instrukcji imperatywnych takich jak tworzenie obiektu, modyfikacja jego wartości, wstawienie obiektu do innego obiektu i usunięcie obiektu (instrukcje create, update, insert, delete). Może on być pamiętany tylko chwilowo na czas natychmiastowej aktualizacji wyników zapamiętanych zapytań po zmianie w bazie lub przechowany w liście modyfikacji (wraz z podschematami innych modyfikacji) dla późniejszego zbiorczego ich wykorzystania podczas periodycznej synchronizacji stanu pamięci podręcznej i bazy danych. Podobnie jak w przypadku zapytania, podschematy modyfikacji tworzone są na podstawie drzewa syntaktycznego modyfikacji (jest ona przecież szczególnym przypadkiem zapytania do bazy danych) oraz metabazy. Wykorzystanie podschematów polega na sprawdzaniu, czy podschemat modyfikacji jest rozłączny (w odpowiednim znaczeniu zależnym od ich definicji) z podschematem zapamiętanego zapytania. Jeżeli tak, oznacza to niezależność danego zapytania od tej 105

106 5. Aktualizacja wyników zapamiętanych zapytań modyfikacji bazy danych, czyli brak wpływu modyfikacji bazy danych na wyniki zapytania. Takie zapytanie jest eliminowane z procesu aktualizacji wyników. Porównanie podschematów jest wykonywane dla wszystkich zapytań, stąd rozpoznanie zapytań nierozłącznych z modyfikacją wymaga bardzo szybkiego mechanizmu opartego na odpowiednio zorganizowanym, wspomagającym pracę menadżera pamięci podręcznej indeksie dopasowanym do specyfiki języka podschematów, czyli ich struktury. Zapytania niewyeliminowane z procesu synchronizacji danych są oznaczone w rejestrze jako wymagające aktualizacji (poprzez pełną reewaluację lub częściową), co odbywa się natychmiast po zakończonym procesie eliminacji lub jest odkładane do czasu konieczności ponownego wykorzystania wyników danego zapytania, zgodnie z procedurą opisaną w poprzednim rozdziale. Podczas oznaczania zapytania jako nieaktualnego zapisywane są w węźle danego zapytania informacje pomocnicze bazujące na podschemacie poczynionej modyfikacji (bądź grupy modyfikacji) ułatwiające późniejszą sprawną aktualizację wyników tego zapytania. Zapytania o częstych aktualizacjach wyników mogą być usuwane z pamięci podręcznej jako niewygodne - redukujące opłacalność metody optymalizacyjnej, zgodnie z mechanizmem optymalizacji wykorzystania zasobów bazującym na listach częstości użycia/aktualizacji Język podschematów dla języka SBQL Realizacja metody eliminacji wymaga zdefiniowania notacji podschematów (języka podschematów) umożliwiającej szybkie i wygodne tworzenie podschematów odpowiednich dla zapamiętanych zapytań i zapytań modyfikujących. Mamy tutaj do czynienia z dwoma trudnymi do pogodzenia kryteriami. Z jednej strony język podschematów powinien być na tyle precyzyjny, aby minimalizować opisywany fragment bazy danych, czyli zredukować do minimum liczbę zapamiętanych zapytań wymagających aktualizacji. Z drugiej strony język taki powinien być jak najprostszy, gdyż definiowane nim podschematy muszą być szybko wyszukiwane i porównywane. Bardzo precyzyjna notacja podschematów (lepiej aproksymującą wymaganą część schematu bazy danych), może więc spowodować problemy wydajnościowe. Stąd też całkowita wydajność metody eliminacji zależy w dużej mierze od doboru właściwego języka podschematów i w konsekwencji od zastosowanych do składowania, wyszukiwania i porównywania podschematów struktur danych i algorytmów. Warto zauważyć, że optymalizacja poprzez zapamiętywanie wyników zapytań i aktualizacja zapytań z nią związana są całkowicie wewnętrznymi mechanizmami, przezroczystymi dla końcowego użytkownika. Mamy więc szerokie pole swobody w doborze definicji podschematów. 106

107 5. Aktualizacja wyników zapamiętanych zapytań Dla podejścia stosowego do języków zapytań proponowanym językiem podschematów będzie język oparty na schemacie bazy danych zapisanym w metabazie w postaci grafu schematu. Każdy węzeł tego grafu opisujący obiekt w bazie danych posiada unikalny identyfikator. Identyfikatory te wykorzystamy jako atomowe elementy, z których będą budowane podschematy. Podczas statycznej analizy zapytania każda nazwa pojawiająca się w zapytaniu jest wiązana z odpowiednim węzłem grafu schematu opisującym obiekt kryjący się pod tą nazwą. Nawet jeżeli nazwy w schemacie bazy danych się powtarzają, np. dwa obiekty różnych klas posiadają podobiekty o tej samej nazwie, są one jednoznacznie kojarzone z odpowiednim węzłem schematu, a więc z jego unikalnym identyfikatorem i Nazwa. Na etapie optymalizacji są dostępne tego typu informacji i podczas dodawania zapytania do rejestru zapamiętanych zapytań odpowiedni podschemat może być budowany i równocześnie zapamiętywany. Użycie identyfikatorów jako elementów podschematów nie tylko ujednoznacznia je, ale również zapewnia stały rozmiar elementów podschematów oraz stosunkowo niewielki rozmiar samych podschematów, co znacząco wpływa na wydajność ich przetwarzania. Stąd mechanizm tworzenia podschematów ma postać dwóch funkcji generujących podschematy odpowiednio dla zapamiętywanego zapytania oraz instrukcji modyfikującej bazę danych. Funkcja podschematu zapytania (subq) Funkcja podschematu zapytania (subq) zwraca dla zapytania q zbiór unikalnych identyfikatorów węzłów grafu schematu bazy danych ustalonych dla wszystkich nazw obiektów N q wykrytych w drzewie syntaktycznym zapytania. Na pewno do zbioru tego należy przynajmniej jeden z identyfikatorów nazw obiektów korzeniowych (chyba, że zapytanie nie dotyczyło danych zapisanych w bazie, np * 3, ale zgodnie z opisanymi wcześniej regułami optymalizacji tego typu zapytania nie powinny podlegać zapamiętywaniu). W zbiorze tym znajdą się też identyfikatory nazw bezpośrednich podobiektów tych obiektów, ich podobiektów itd. (ale tylko tych, które zostały jawnie użyte w zapytaniu). Dotyczy to zarówno podobiektów natywnych jak i odziedziczonych z klas nadrzędnych. Zbiór zawiera również identyfikatory metod użytych w zapytaniu zdefiniowanych dla klas obiektów bazy danych. Formalnie, do zbioru N q wstawiany jest każdy identyfikator węzła grafu schematu bazy danych, który podczas statycznej ewaluacji zapytania q zostanie umieszczony na stosie S_QRES. Są to więc statyczne identyfikatory odpowiadające w drzewie syntaktycznym zapytania q węzłom zawierającym: wiązanie nazwy obiektu bazy danych (np. dla nazwy Emp będzie to i Emp, dla salary i salary, itp.); wiązanie nazwy metody pewnej klasy (np. dla nazwy age będzie to i age ); 107

108 5. Aktualizacja wyników zapamiętanych zapytań wiązanie nazwy procedury lub funkcji globalnej (z pominięciem funkcji cache'ującej $cache_fun). Zbiór tych identyfikatorów tworzy podschemat zapytania q. Funkcja podschematu modyfikacji (subu) Funkcja podschematu modyfikacji (subu) zwraca dla modyfikacji u zbiór unikalnych identyfikatorów węzłów grafu schematu bazy danych ustalonych dla wszystkich nazw obiektów N u zmienionych na skutek tej modyfikacji, czyli dodanych, zmodyfikowanych lub usuniętych. Jeżeli zmieniony został jakiś obiekt złożony w zbiorze tym pojawią się dodatkowo identyfikatory nazw wszystkich natywnych i odziedziczonych podobiektów tego obiektu, które również uczestniczyły w takiej modyfikacji. Jeżeli w zbiorze znajduje się identyfikator nazwy obiektu klasy pochodnej, powinny się w nim znaleźć również identyfikatory nazw wszystkich klas bazowych tej klasy. Sposób generowania zbioru N u zależy od rodzaju instrukcji imperatywnej u zmieniającej bazę danych. Oto wskazówki dotyczące generowania tego zbioru dla następujących instrukcji: create [permanent] q instrukcja tworząca nowe obiekty w bazie danych, gdzie q zwraca bag binderów definiujących nazwę tworzonych obiektów, które dalej zawierają dowolną liczbę dowolnie zagnieżdżonych binderów definiujących nazwy ich podobiektów (klauzula permanent powoduje tworzenie trwałych obiektów korzeniowych w składzie danych bez niej domyślnie tworzone są obiekty lokalne). Dla tej instrukcji do zbioru N u wstawiane są identyfikatory węzłów metabazy definiujących nazwy tych binderów, które uzyskuje się z wierzchołka stosu S_QRES po statycznej ewaluacji zapytania q. Wśród identyfikatorów mogą pojawić się identyfikatory nieistniejące przed realizacją takiej modyfikacji (gdy tworzony jest obiekt o nowej nazwie). Identyfikatory takie mogą być zignorowane nie mogły istnieć wcześniej zapytania, które odwoływałyby się do takich danych, gdyż ich po prostu nie było. insert q instrukcja tworząca i wstawiająca nowe podobiekty pewnego obiektu, gdzie q zwraca bag dwuelementowych struktur o elementach będących referencjami: obiektu, do którego wstawiamy i obiektu wstawianego utworzonego instrukcją create. Wstawienie nowego podobiektu (najczęściej wtedy, gdy jego liczebność może być większa od 1) może wpłynąć jedynie na zapytania odwołujące się do obiektów tego typu, więc w przypadku instrukcji insert zbiór N u jest tworzony jako zbiór właściwy dla jej podinstrukcji create (opisany wyżej). update q instrukcja zmiany wartości obiektów w bazie danych, gdzie q zwraca zwraca bag dwuelementowych struktur, w których pierwszy element jest 108

109 5. Aktualizacja wyników zapamiętanych zapytań referencją modyfikowanego obiektu, natomiast drugi nową wartość dla tego obiektu (powstałą w razie konieczności po zastosowaniu niejawnego operatora dereferencji). Modyfikacja danych odbywa się poprzez dereferencję obiektów zwracanych na pierwszych pozycjach wynikowych struktur zapytania q i zamianę ich wartości nowe zwracane na drugich pozycjach tych struktur. Dla tej instrukcji do zbioru N u wstawiane są identyfikatory węzłów metabazy definiujących nazwy binderów, które uzyskuje się z wierzchołka stosu S_QRES po statycznej ewaluacji zapytania q i dodatkowo identyfikatory uzyskane po statycznej dereferencji referencji stanowiących pierwsze element wynikowych struktur (zmienianych obiektów). Jeżeli referencje te są identyfikatorami obiektów atomowych, w zbiorze wynikowym pojawi się pojedynczy identyfikator węzła nazwy takich obiektów. W przeciwnym wypadku, najpierw dodany zostanie identyfikator węzła metabazy obiektu złożonego, którego referencję zwraca to zapytanie, a następnie po jej statycznej dereferencji na stosie S_QRES pojawi się struktura złożona z binderów wszystkich podobiektów tego obiektu. Do zbioru N u dodane zostaną identyfikatory węzłów metabazy definiujących nazwy tych binderów. delete q instrukcja usuwająca obiekty z bazy danych, gdzie q zwraca bag referencji obiektów do usunięcia. Podobnie jak w przypadku instrukcji przypisania nowej wartości, usuwany może być obiekt atomowy lub złożony. Analogicznie, do zbioru N u wstawiane są identyfikatory węzłów metabazy definiujących nazwy binderów, które uzyskuje się z wierzchołka stosu S_QRES po statycznej ewaluacji zapytania q i dodatkowo identyfikatory uzyskane po statycznej dereferencji tego wyniku (tym razem operacja dereferencji jest wymuszana). Jeżeli lewe zapytanie zwraca identyfikator obiektu atomowego, w zbiorze wynikowym pojawi się pojedynczy identyfikator węzła nazwy tego obiektu. W przeciwnym wypadku, najpierw dodany zostanie identyfikator węzła metabazy obiektu złożonego, którego referencję zwraca to zapytanie, a następnie po jej statycznej dereferencji na stosie S_QRES pojawi się struktura złożona z binderów wszystkich podobiektów tego obiektu. Do zbioru N u dodane zostaną identyfikatory węzłów metabazy definiujących nazwy tych binderów. Ostatecznie, niezależnie od rodzaju instrukcji zmieniającej bazę danych, dla każdego identyfikatora umieszczonego w zbiorze N u następuje: sprawdzenie, czy identyfikuje on węzeł metabazy powiązany więzami dziedziczenia z innym węzłem lub węzłami (są to węzły klas bazowych). Identyfikatory wszystkich takich węzłów są dodawane do zbioru wynikowego. Dla tych nowych identyfikatorów następuje rekurencyjnie analogiczne wykrycie identyfikatorów węzłów kolejnych klas bazowych, aż do końca linii dziedziczenia; 109

110 5. Aktualizacja wyników zapamiętanych zapytań sprawdzenie, czy jest on identyfikatorem węzła obiektu referencyjnego jeżeli tak, wstawiany jest do zbioru identyfikator węzła referencji zwrotnej. W ten sposób powstaje ostateczna zawartość podschematu właściwego dla danej modyfikacji u, która nastąpiła w bazie danych. Porównywanie podschematów Przy takiej definicji podschematów ich porównywanie polega na sprawdzeniu czy jako zbiory zawierają wspólne elementy. Jeżeli takich wspólnych elementów nie ma, czyli zbiory będące podschematem zapamiętanego zapytania i zaistniałej w bazie modyfikacji danych są rozłączne, oznacza to brak wpływu tej modyfikacji na dane zapytanie. W przeciwnym wypadku istnieje prawdopodobieństwo zmiany wyników zapytania na skutek tej modyfikacji, przy czym mogą zdarzyć się takie sytuacje, w których nierozłączność podschematów nie oznacza, że zapytanie po reewaluacji przy nowym stanie bazy da wynik inny niż przed. Wynika to z faktu nieuwzględnienia w definicji podschematów informacji filtrujących zbiór obiektów o danej nazwie, np. za pomocą operatora niealgebraicznego selekcji (where). W grafie schematu bazy danych brakuje informacji o wartościach obiektów znajdujących się w bazie w danej chwili, stąd uwzględnianie predykatów zawężających zbiór analizowanych obiektów nie jest możliwe na etapie analizy statycznej zapytania. Eliminacja oparta na tak zdefiniowanych podschematach uwzględnia więc zapytania, które na pewno mogą zostać pominięte podczas procesu synchronizacji stanu pamięci podręcznej wyników zapytań, pomijając jednocześnie zapytania, które potencjalnie mogły zostać zmienione. Metoda ta zapewnia więc utrzymanie rejestru zapytań w zgodności z aktualnym stanem bazy danych. Poniżej przedstawionych jest kilka przykładów mechanizmu eliminacji z użyciem podschematów zgodnych z zaproponowaną definicją. Dla uproszczenia i przejrzystości przykładów w miejsce identyfikatorów nazw wchodzących w skład grafu schematu używane są po prostu nazwy odpowiednich obiektów lub klas, a więc zamiast elementu i Nazwa w podschematach pojawi się element Nazwa. Przykłady dotyczą przykładowej bazy danych zaprezentowanej w rozdziale 3.6. Przykład 5.1 Rozważmy zapamiętane zapytanie q pobierające pracowników, którzy wcześniej pracowali w firmie o nazwie ABC i jednocześnie prowadzą więcej niż 5 kursów: Emp where (prev_job.company ABC ) and count(supervises) > 5 Podschemat tego zapytania wygenerowany funkcją subq ma postać: 110

111 5. Aktualizacja wyników zapamiętanych zapytań subq(q) = {Emp, prev_job, company, supervises} Następnie rozważmy dwie niezależne modyfikacje bazy danych u 1 i u 2 zrealizowane za pomocą następujących instrukcji imperatywnych: u 1 : u 2 : delete Emp.prev_job where company = ABC delete (Emp where count(prev_job) > 0).supervises.Training where count(received_by) < 8 Instrukcja u 1 usuwa z bazy danych informacje o wcześniejszym zatrudnieniu pracowników w firmie o nazwie ABC. Informacje te są przechowywane w obiektach o nazwie prev_job będących obiektami złożonymi, więc usuwane są również ich podobiekty o nazwach company i years (zakładamy, że usunięty został przynajmniej jeden obiekt prev_job). Podschemat tej modyfikacji składa się więc z identyfikatorów nazw tych trzech obiektów: subu(u 1 ) = {prev_job, company, years} Instrukcja u 2 usuwa natomiast z bazy danych kursy o liczbie kursantów mniejszej niż 8, prowadzone przez pracowników, którzy wcześniej pracowali gdziekolwiek. Są to obiekty o nazwie Training posiadające cztery zagnieżdżone podobiekty o nazwach subject, duration, received_by i supervised_by. Podczas usuwania obiektów klasy Training usuwane są też obiekty zwrotnych referencji receives i supervises będące nazwami podobiektów klas Student i Emp odpowiednio (receives jest referencją zwrotną dla referencji received_by, a supervises dla supervised_by). W związku z tym otrzymujemy następujący podschemat tej modyfikacji: subu(u 2 ) = {Training, subject, duration, received_by, supervised_by, receives, supervises} Podschemat odpowiedni dla zapytania q i podschemat modyfikacji u 1 mają część wspólną w sensie przecięcia zbiorów (identyfikatory nazw obiektów prev_job i company), więc te podschematy nie są rozłączne: subq(q) subu(u 1 ) = {prev_job, company} Modyfikacja u 1 może mieć zatem wpływ na wyniki zapytania q, więc zapytanie nie może być wyeliminowane z procesu synchronizacji po zakończeniu realizacji tej modyfikacji należy uznać wyniki zapamiętanego zapytania jako nieaktualne oznaczając je odpowiednio we właściwym dla niego węźle wraz z informacją o podschemacie 111

112 5. Aktualizacja wyników zapamiętanych zapytań stanowiącym część wspólną obu podschematów. Są to informacje o zakresie (nazwach) obiektów używanych podczas ewaluacji zapytania, a zmodyfikowanych (w przedstawionym przykładzie usuniętych) podczas realizacji zapytania modyfikującego bazę danych. Mogą być także rozszerzone o identyfikatory modyfikowanych lub dodawanych obiektów, a w przypadku obiektów usuwanych również o identyfikatory ich obiektów nadrzędnych (o ile nie były usuwane obiekty korzeniowe). Pozwolą one w sposób optymalny zaktualizować wyniki takiego zapytania natychmiast lub podczas aktualizacji zbiorczej (więcej na ten temat w kolejnych podrozdziałach). Podobny efekt, czyli konieczność aktualizacji wyników zapytania uzyskujemy po drugiej modyfikacji, gdyż: subq(q) subu(u 2 ) = {supervises} Jeżeli rozpatrzymy uproszczone zapytanie q postaci: Emp where (prev_job.company = ABC ) otrzymamy jego podschemat: subq(q ) = {Emp, prev_job, company} W tym przypadku podschemat zapytania q nadal posiada część wspólną z podschematem modyfikacji u 1. Inaczej jest jednak w kontekście modyfikacji u 2 : subq(q ) subu(u 2 ) = Oznacza to, że po wykonaniu modyfikacji u 2 wyniki zapamiętanego zapytania q pozostają niezmienione. Możliwa jest zatem eliminacja tego zapytania z procesu aktualizacji wyników. Przykład 5.2 Dla zapamiętanego zapytania q pobierającego z bazy danych informacje o nazwiskach pracowników o pensji przekraczającej 1200 i nazwach zatrudniających ich departamentów: (Emp where salary > 1200).(name, works_in.dept.dname) uzyskujemy jako jego podschemat zbiór identyfikatorów nazw w schemacie bazy danych: 112

113 5. Aktualizacja wyników zapamiętanych zapytań subq(q) = {Emp, salary, name, works_in, Dept, dname} Obiekt o nazwie name jest przykładem obiektu odziedziczonego obiekty klasy Emp dziedziczą wszystkie podobiekty i metody z klasy Person zgodnie z grafem schematu przedstawionym na Rys Oto przykładowe instrukcje modyfikujące bazę danych: u 1 : update (Emp where works_in.dept.dname = Trade ).(salary, 1300) u 2 : delete Emp where name = Brown u 3 : update (Student where name = Smith ).(grades as g where g = 3, 4) W wyniku modyfikacji u 1 zmieniane są wyłączne obiekty atomowe o nazwie salary (pensje pracowników zatrudnionych w departamencie Trade są ustalane na 1300), stąd: subu(u 1 ) = {salary} subq(q) subu(u 1 ) Podobnie jak w poprzednim przykładzie oznacza to konieczność korekty wyników zapytania q po modyfikacji u 1. Modyfikacja taka spowodowała zmianę wynagrodzenia pracowników wskazanego departamentu, co w konsekwencji zwiększa liczbę pracowników spełniających predykat zastosowany dla operatora where w zapytaniu q. Modyfikacja u 2 usuwa z bazy danych kompletne obiekty klasy Emp (wszystkich pracowników o nazwisku Brown ) razem z wszystkimi ich podobiektami łącznie z odziedziczonymi z klasy Person (dereferencja referencji obiektów klasy Emp zwracanych przez główne podzapytanie tej modyfikacji zawiera bindery tych wszystkich podobiektów), więc: subu(u 2 ) = {Emp, Person, name, birthday, job, salary, rating, works_in, prev_job, company, years, employs} Podczas konstruowania tego podschematu założono, że w wyniku działania modyfikacji u 2 usunięty został przynajmniej jeden obiekt Emp, który zawierał opcjonalne podobiekty prev_job, stąd obecność identyfikatora tej nazwy oraz identyfikatorów nazw company i years. Do zbioru tworzącego podschemat dołączony został również identyfikator nazwy klasy Person ze względu na zależność dziedziczenia istniejącą pomiędzy obiektami klas Emp i Person. Każdy pracownik jest osobą, zatem jego usunięcie może mieć wpływ również na zapytania jawnie odwołujące się do obiektów o nazwach Person. 113

114 5. Aktualizacja wyników zapamiętanych zapytań Ostatnią nazwą uwzględnioną w podschemacie tej modyfikacji jest nazwa employs, reprezentująca podobiekty obiektów klasy Dept, nie umieszczonej w zbiorze tworzącym podschemat. Jej obecność wynika z faktu, iż na skutek usunięcia podobiektu o nazwie works_in będącego obiektem referencyjnym nastąpiło usunięcie referencji zwrotnej o nazwie właśnie employs. Jednocześnie założono, że przed wystąpieniem modyfikacji u 2 żaden z usuniętych obiektów klasy Emp nie posiadał obiektów o nazwach supervises, ani manages, więc zostały one pominięte podobnie jak odpowiadające im obiekty referencji zwrotnych supervised_by i boss. W efekcie porównania mamy: subq(q) subu(u 2 ) = {Emp, salary, name, works_in} W związku z powyższym, podobnie jak po modyfikacji u 1, konsekwencją działania modyfikacji u 2 jest konieczność korekty wyników zapytania q. Modyfikacja u 3 podwyższa wszystkie oceny 3 na 4 wszystkim studentom o nazwisku Smith, czyli zmienia obiekty atomowe o nazwach grades, więc: subu(u 3 ) = {grades} subq(q) subu(u 3 ) = co w konsekwencji oznacza brak wpływu tej modyfikacji na wyniki zapamiętanego zapytania q jest ono eliminowane ze zbioru zapytań, które zostaną uwzględnione podczas operacji synchronizacji stanu pamięci podręcznej wyników zapytań z aktualnym stanem bazy danych po zmianie u Optymalizacja porównywania podschematów Zgodnie z wcześniejszymi wnioskami wydajność metody eliminacji zależy od doboru języka podschematów, a w konsekwencji od struktur danych i algorytmów zastosowanych dla składowania podschematów zapamiętanych zapytań, ich szybkiego wyszukiwania i porównywania z podschematami wygenerowanymi dla jednej bądź wielu modyfikacji bazy danych. Zaproponowany w poprzednim podrozdziale język podschematów opiera się na zbiorach identyfikatorów węzłów grafu schematu bazy danych zapisanego w metabazie, zatem porównywanie tych podschematów sprowadza się do sprawdzenia czy podschemat modyfikacji ma wspólne elementy z podschematami odpowiednimi dla zapamiętanych zapytań. W najprostszym rozwiązaniu, po wystąpieniu modyfikacji system generuje odpowiedni dla niej podschemat i zgodnie z założeniami metody eliminacji porównuje ten podschemat z podschematami wszystkich zapamiętanych zapytań w celu rozpoznania tych spośród nich, których modyfikacja nie dotyczyła. Pozostałe podlegają 114

115 5. Aktualizacja wyników zapamiętanych zapytań procesowi aktualizacji wyników opisanych w następnym podrozdziale. Jeżeli baza danych jest często modyfikowana, to przy założeniu, że zapamiętanych zapytań jest bardzo dużo, sekwencyjny proces sprawdzania rozłączności podschematów może być bardzo kosztowny. Stąd konieczne są bardziej wyrafinowane mechanizmy i struktury danych optymalizujące ten proces. Eliminacja zbiorcza Pierwszym z kroków optymalizacyjnym może być redukcja częstotliwości uruchamiania metody eliminacji poprzez grupowanie wielu kolejnych modyfikacji bazy danych i traktowanie ich jako jedną modyfikację. W tym celu po każdej modyfikacji konieczne jest zapamiętanie odpowiadającego jej podschematu w liście podschematów oczekujących na uwzględnienie wraz z dodatkowymi informacjami określającymi identyfikatory obiektów biorących udział w modyfikacji (zmienionych, dodanych lub usuniętych). Gdy w liście zostanie umieszczonych pewna liczba (konfigurowana przez administratora) podschematów lub zostanie przekroczony pewien czas (również konfigurowany) od momentu umieszczenia pierwszego elementu listy, następuje uruchomienie procesu synchronizacji. Oto jego etapy: 1. Generowany jest podschemat odpowiedni dla całej grupy oczekujących modyfikacji będący sumą mnogościową podschematów zgromadzonych w liście, czyli zawierający informację o wszystkich obiektach modyfikowanych w ostatnim czasie. 2. Następuje uruchomienie procesu eliminacji w kontekście tego połączonego podschematu i ostatecznie aktualizacja stanu pamięci podręcznej wyników niewyeliminowanych zapytań na podstawie dodatkowych informacji zapamiętanych w liście. Alternatywnie zapytania niewyeliminowane podczas tego procesu oznaczane są jako nieaktualne i zapamiętywane są w ich węzłach informacje pomocnicze z listy. Szczegóły procesu aktualizacji wyników przedstawione zostały w kolejnym podrozdziale. 3. Po zakończeniu przetwarzania listy modyfikacji zostaje ona wyczyszczona i jest gotowa do przyjęcia informacji o kolejnych modyfikacjach. Grupowanie modyfikacji może powodować przedłużanie trwania niezgodności wyników zapamiętanych zapytań ze stanem bazy danych, więc proces ten powinien być odpowiednio skonfigurowany. Oczywistym zastosowaniem tej metody jest sytuacja, w której szereg modyfikacji jest realizowany w pojedynczej transakcji. Szybki indeks podschematów Drugim, najistotniejszym krokiem optymalizacji przetwarzania podschematów jest dobór odpowiedniej struktury dla ich przechowywania (indeksu podschematów). Liczba zapamiętywanych zapytań, a więc również liczba podschematów jest znacząca, stąd struktura musi być tak dobrana, aby znalezienie zapytań, których podschematy 115

116 5. Aktualizacja wyników zapamiętanych zapytań zawierają elementy znajdujące się w wyszukiwanym zbiorze (podschemacie modyfikacji) było bardzo szybkie. Istotna jest też możliwość sprawnego rozbudowywania takiej struktury o nowe elementy oraz jej redukcji. Zaproponowane podschematy są zbiorami unikalnych identyfikatorów, dla których konieczne jest szybkie sprawdzenie pustości, bądź nie, części wspólnej tych zbiorów. Domena tych zbiorów, czyli liczba różnych możliwych elementów jest stosunkowo niewielka, co wynika z faktu, iż typowa baza danych może zawierać w schemacie kilkadziesiąt, a maksymalnie kilkaset definicji różnych typów obiektów (klas, ich niezmienników, np. metod i własności tych klas, czyli obiektów i ich podobiektów). Stąd też identyfikatorów węzłów grafu schematu bazy danych, będących elementami podschematów, jest niedużo w grafie z Rys.3.12 jest ich dla przykładu 28. Dodatkowo można założyć, że schemat bazy danych nie zmienia się lub zmienia się bardzo rzadko takie zmiany niosą ze sobą bardzo poważne konsekwencje dla aplikacji korzystających z takich danych, więc projektanci systemowi starają się nie doprowadzać do takich sytuacji. Domena wartości jest więc praktycznie stała i niewielka. Same podschematy też nie są licznymi zbiorami, najczęściej do kilkunastu elementów, maksymalnie kilkudziesięciu, co wynika ze specyfiki zapytań do bazy danych praktyki programistycznej rozdzielania większych zapytań na mniej skomplikowane, o łatwiejszym w utrzymaniu kodzie oraz cechy języka SBQL pozwalającej zapisywać nawet bardzo skomplikowane zapytania w prostej syntaktycznie formie. Cecha ta jest też wynikiem obiektowego podejścia do organizacji składu danych z użyciem referencji bezpośrednich do obiektów. Projektując strukturę danych odpowiednią dla wyszukiwania części wspólnej podschematów należy się więc kierować powyższymi założeniami. Poniżej wymienione zostały niektóre z dotychczas stworzonych i opisanych w literaturze technik indeksowania wartości będących zbiorami (ang. set-valued attributes), których analiza może być pomocna w doborze właściwego indeksu dla podschematów zapamiętanych zapytań: plik odwrócony (ang. inverted file) stworzony oryginalnie dla szybkiego przeszukiwania dokumentów tekstowych, zbudowany jako słownik wystąpień będący listą wszystkich elementów pojawiających się w indeksowanych zbiorach (np. słów w dokumentach) i skojarzoną z każdym elementem słownika listą identyfikatorów zbiorów zawierających ten element; dedykowany dla wyszukiwania podzbiorów lub części wspólnej dużej liczby zbiorów o niedużej liczności elementów i domenie wartości [ZMS93, SZ97, AGZ97]; plik sygnatur (ang. signature file) oparte na wektorach bitowych jednoznacznie reprezentujących elementy zbiorów (zwanych sygnaturami) i wykorzystujące operacje składania bitowego (bitowe OR) do tworzenia sygnatur zbiorów; rozróżnia się kilka sposobów przechowywania sygnatur zbiorów: sekwencyjne (ang. sequential signature file, [CF84, Fal92, IKO93]), 116

117 5. Aktualizacja wyników zapamiętanych zapytań hierarchiczne (ang. signature tree, bądź S-tree, [Dep86]) i rozdzielone (ang. extendible signature hashing, [ZRT91]); dedykowany dla wyszukiwania podzbiorów, nadzbiorów lub części wspólnej dużej liczby zbiorów o dużej liczności elementów i niedużej domenie wartości, przy czym zoptymalizowane pliki sygnatur o organizacji rozdzielonej ze względu na wykorzystanie haszowania odznaczają się dużą niejednoznacznością odwzorowania elementów zbiorów i ich sygnatur, co przy dużej liczbie zbiorów powoduje znaczny wzrost złych trafień (ang. false hits) i spadek wydajności; indeks przestrzenny (ang. spatial index) dedykowany do indeksowania danych wielowymiarowych, najczęściej geograficznych (R-tree, [Gut84]), dostosowany również do wyszukiwania zbiorów (RD-tree, ang. Russian Doll tree, [HP94]); oparty na drzewie z relacją tranzytywnego wzajemnego zawierania się kluczy umieszczanych w węzłach i ich podwęzłach indeksowane zbiory (ang. base sets) umieszczone są w liściach drzewa, a wewnętrzne węzły wyższych poziomów opisują zestaw (ang. bounding sets) węzłów kluczy lub zbiorów z niższych poziomów; w połączeniu z reprezentacją bitową kluczy wykorzystującą operacje bitowe indeks ten jest zbliżony do S-tree, umożliwia realizację wyszukiwania podzbiorów i części wspólnych, jednak jego wydajność jest niska przy bardzo dużej liczbie indeksowanych elementów (zbiorów); bitmapa haszowana (ang. hash group bitmap) zbliżony do pliku sygnatur, przy czym każdy element zbiorów jest haszowany do odpowiadającej mu pozycji w wektorze bitowym reprezentującym indeksowany zbiór [MZ98]; dedykowany dla niedużej domeny wartości (ze względu na możliwość haszowania do tej samej pozycji bitowej), dla operacji wyszukiwania podzbiorów, nadzbiorów i części wspólnych; duża liczba zbiorów w indeksie może zdegradować wydajność na skutek częstych złych trafień; bitmapa hierarchiczna (ang. hierarchical bitmap) kolejny indeks oparty na sygnaturach hierarchicznych (o organizacji drzewiastej, S-tree), z uwzględnieniem bardzo długich jednoznacznych kompresowanych kluczy o organizacji bitowej; dedykowany dla wyszukiwania dowolnych zależności (łącznie z podobieństwem zbiorów) pomiędzy zbiorami o elementach z bardzo szerokiej domeny wartości o dowolnej liczności zarówno zbiorów jak i ich elementów [MMN+03]. Biorąc pod uwagę wcześniej przedstawione założenia dotyczące zarówno gabarytów indeksowanych danych jak i odpowiednich operacji na zbiorach wymaganych przy porównywaniu podschematów oraz uwzględniając porównania wydajności w/w struktur indeksowych dostępne w literaturze (np. w [HM99]), do przechowywania podschematów zapamiętanych zapytań języka SBQL zastosowano odpowiednio dostosowany plik odwrócony. 117

118 5. Aktualizacja wyników zapamiętanych zapytań Struktura ta składa się ze słownika zawierającego wszystkie możliwe wartości przechowywane w indeksowanych zbiorach i dla każdej takiej wartości listy referencji do zbiorów, w których dana wartość się znajduje. W przypadku podschematów słownik zawiera identyfikatory wszystkich węzłów grafu schematu bazy danych, czyli wartości tworzące zbiory zdefiniowanego wcześniej języka podschematów. Aby dostęp do wygranego elementu słownika był bardzo szybki, nawet w przypadku grafu bardzo dużej bazy danych złożonego z kilkuset węzłów, słownik ten jest zorganizowany w formie B-drzewa. Każdy węzeł tego drzewa zawiera na początku adres pierwszego swojego wolnego bajtu. Węzły-liście przechowują wartości kluczowe (identyfikatory węzłów grafu) wraz z adresami związanych z nimi list wystąpień. Węzły wewnętrzne zawierają adresy węzłów-dzieci separowane odpowiednimi wartościami kluczowymi. Listy wystąpień zawierają identyfikatory węzłów w metabazie utworzonych dla zapamiętanych zapytań (umożliwiających jednoznaczny dostęp do wszystkich danych tych zapytań), których podschematy zawierają element słownika powiązany z daną listą. Identyfikator zapytania znajduje się więc w listach właściwych dla każdego elementu podschematu tego zapytania. W celu wyeliminowania z procesu aktualizacji wyników zapytań, których wyniki nie zmieniły się na skutek działania pewnej modyfikacji (lub grupy modyfikacji) bazy danych wykonywane są następujące kroki: 1. Generowany jest za pomocą funkcji subu lub pobierany z listy modyfikacji oczekujących podschemat odpowiedni dla tej/tych modyfikacji. 2. Dla każdego identyfikatora znajdującego się w zbiorze tworzącym ten podschemat następuje odnalezienie w indeksie listy wystąpień skojarzonej z tym identyfikatorem. 3. Listy wystąpień odnalezione dla wszystkich takich identyfikatorów są sumowane mnogościowo (eliminowane są powtórzenia), co w wyniku daje listę wszystkich zapamiętanych zapytań (ich identyfikatorów), które mogły zostać dotknięte rozpatrywaną modyfikacją. Dla usprawnienia procesu wyznaczania sumy mnogościowej identyfikatorów zapytań listy pobierane są w kolejności od najdłuższej do najkrótszej, co redukuje do minimum liczbę operacji porównania wykonywanych podczas odkrywania kolejnych elementów list wchodzących w skład sumy. W ten sposób tworzona jest lista identyfikatorów zapytań wymagających aktualizacji, a pozostałe zapytania, zgodnie z metodą eliminacji, są ignorowane podczas procesu synchronizacji. 4. Niewyeliminowane zapytania są odpowiednio oznaczane jako wymagające aktualizacji wyników w ich obiektach przechowywania umieszczane są również odpowiednie informacje o zmodyfikowanych obiektach uzyskane na podstawie przetwarzania rozpatrywanej modyfikacji. W momencie dodawania nowego zapytania do rejestru zapamiętanych zapytań wykonywane są następujące kroki: 118

119 5. Aktualizacja wyników zapamiętanych zapytań 1. Generowany jest jego podschemat za pomocą funkcji subq. 2. Identyfikator węzła tego zapytania umieszczany jest w listach wystąpień wszystkich elementów wygenerowanego podschematu. Analogicznie, mając zapisany wraz z wynikami podschemat danego zapytania, możliwe jest szybkie usunięcie referencji do tego zapytania w indeksie, poprzez usunięcie jej z listy wystąpień każdego z elementów tego podschematu. Usuwanie zapamiętanych zapytań jest skutkiem optymalnej utylizacji pamięci podręcznej opisanej w poprzednim rozdziale. Listy wystąpień mogą zawierać wiele elementów (identyfikatorów zapytań). Aby zatem zredukować pamięciożerność takich struktur i jednocześnie zapewnić szybkość operacji mnogościowego sumowania tych list oraz wstawiania i usuwania ich elementów stosuje się sortowanie ich zawartości na bieżąco oraz ich kompresję za pomocą lekkich technik umożliwiających szybką dekompresję, wydajną dla identyfikatorów węzłów zapytań będących liczbami całkowitymi. Jednym z rozwiązań jest kompresja poprzez usuwanie nieistotnych zer w reprezentacji bitowej elementów list. Skompresowana lista zawiera na początku listę długości bitowej poszczególnych elementów, na podstawie której wyliczane są przesunięcia, a następnie skompresowane wartości tych elementów. Alternatywnym rozwiązaniem dla posortowanych wartości jest zapamiętywanie informacji o przerwach wewnątrz posortowanego ciągu, czyli odległościach pomiędzy kolejnymi wartościami w liście. Tak skompresowana lista zawiera pierwszą najmniejszą wartość, liczbę wartości, informację o wielkości (wyrażonej w bitach) maksymalnej odległości i ostatecznie listę tych odległości, która może być dodatkowo skompresowana pierwszą metodą. W celu uniknięcia przerostu formy nad treścią dopuszczalne są również listy nieskompresowane, np. o liczności mniejszej niż 8 wartości. Każda lista wystąpień musi być na początku oznaczona odpowiednią flagą włączonej kompresji. 5.2 Korekta przyrostowa wyników Metoda eliminacji opisana w poprzednim podrozdziale dokonuje odfiltrowania ze zbioru zapamiętanych zapytań tych, których zmaterializowane wyniki nie zostały zmienione po zmianach w bazie danych, wyznaczając w ten sposób możliwie najmniejszą grupę zapytań wymagających aktualizacji wyników. Aktualizacja wyników jest konieczna, aby zastosowanie optymalizacji z użyciem zapamiętanych wyników nie wpływało na wyniki kolejnych zapytań, aby metoda optymalizacyjna była całkowicie przezroczysta dla kierującego zapytanie do bazy danych. Pełna ponowna reewaluacja zapamiętanego zapytania jest najprostszym rozwiązaniem, jednak jej zbyt częste występowanie może znacznie zwiększyć koszty optymalizacji i zmniejszyć wydajność 119

120 5. Aktualizacja wyników zapamiętanych zapytań systemu bazy danych. Zgodnie z wcześniejszymi wnioskami możliwe jest periodyczne aktualizowanie wyników zapytań po serii modyfikacji bazy danych, np. w ramach transakcji, czyli opóźniona synchronizacja pamięci podręcznej z możliwością natychmiastowego odświeżenia wyników zapytań potrzebnych w czasie tymczasowego utrzymywania jej w niezgodności ze stanem bazy. Zmiany w bazie danych najczęściej dotyczą względnie małego fragmentu danych, zatem znaczna cześć wyników zapytań pozostaje całkowicie lub częściowo poprawna w nowym stanie bazy danych. Można więc w miejsce całkowitej ich reewaluacji dokonać jedynie ich korekty. Przez korektę rozumie się usunięcie ze zmaterializowanych wyników tych, które były powiązane ze zmienianymi obiektami i uzupełnienie pozostałości odpowiednimi nowymi lub brakującymi wynikami. Taka korekta wyników nazywana jest korektą przyrostową (ang. incremental update). Dla realizacji tej metody konieczne jest wykonanie dwóch operacji w kontekście każdej modyfikacji bazy danych: Po pierwsze, określenia tych elementów wyników zapamiętanych zapytań, które są już nieaktualne i usunięcia ich. Następnie ewaluacji tych zapytań na możliwie najmniejszym fragmencie nowej bazy danych w celu uzyskania nowych elementów wchodzących w skład ich wyników. W celu oszacowania tych zbiorów wyników konieczne jest wprowadzenie kilku formalnych oznaczeń i definicji właściwych dla SBA. Każda instancja bazy danych zawiera w swoim składzie danych, zgodnie z Rys.3.10, zbiór identyfikatorów obiektów korzeniowych, które są jedynymi punktami startowymi zapytań odnoszących się do tego składu. W podejściu SBA, zgodnie z opisem przedstawionym w rozdziale 3, przy tworzeniu każdej sesji użytkownika bazy danych na stosie środowiskowym umieszczana jest sekcja zawierająca bindery dla wszystkich obiektów korzeniowych. Ewaluacja zapytań polega na przetwarzaniu danych obiektów dostępnych z poziomu obiektów korzeniowych: ich obiektów wewnętrznych, obiektów dostępnych poprzez obiekty referencyjne, ich obiektów wewnętrznych, itd. W pewnych sytuacjach duża część lub nawet wszystkie obiekty mogą być dostępne rozpoczynając zapytanie z jednego tylko obiektu korzeniowego dla przykładu z rozdziału 3.6 z obiektu klasy Dept z identyfikatorem i 21 mamy dostęp do wszystkich innych obiektów tej instancji bazy danych. Jest to efektem obecności w składzie tzw. referencji zwrotnych realizujących dwukierunkowe asocjacje pomiędzy dwoma obiektami korzeniowymi. Taka struktura danych umożliwiająca swobodną i szybką nawigację po danych umożliwia jednocześnie wygodne i sprawne określenie obiektów dostępnych z poziomu wybranego obiektu korzeniowego. Taki zbiór obiektów określamy mianem tranzytywnego domknięcia (ang. transitive closure) zbioru złożonego z tego elementu korzeniowego. Szczegółowe informacje dotyczące tego 120

121 5. Aktualizacja wyników zapamiętanych zapytań tematu znajdują się w [Sub04]. Oznaczmy zbiór wszystkich identyfikatorów obiektów wyznaczony jako tranzytywne domknięcie pewnego podzbioru R zbioru obiektów korzeniowych dla instancji bazy danych db poprzez A(R, db). Zbiór obiektów korzeniowych instancji bazy danych przed modyfikacją bazy danych będziemy oznaczać jako R old, a po modyfikacji jako R new. Odpowiednio instancje przed i po modyfikacji oznaczymy jako db old i db new, a zapamiętane wyniki pewnego zapytania q wykonanego dla tych instancji jako q(r old, db old ) i q(r new, db new ), odpowiednio. Jeżeli na skutek modyfikacji danych nastąpi usunięcie pewnych obiektów korzeniowych, ich identyfikatory będą zatem należały do zbioru R old \ R new. Jeżeli obiekty korzeniowe zostały dodane do składu danych, ich identyfikatory znajdują się w podzbiorze R new \ R old. Obiekty niekorzeniowe, które zostały zmodyfikowane, dodane lub usunięte mogą być dostępne również lub tylko i wyłącznie z niezmienionych obiektów korzeniowych, czyli zbioru R old R new. Maksymalny zbiór obiektów korzeniowych zawarty w zbiorze niezmienionych obiektów korzeniowych, z którego elementów jest dostęp zarówno w db old jak i db new do zmienionych obiektów, odgrywa istotną rolę w procesie korekty przyrostowej wyników zapytań. Oznaczmy go R u (R u R old R new ). Do wyznaczenia zbioru R u konieczne jest pozornie wykonanie niejako odwrotnego tranzytywnego domknięcia, jednak w SBA sprowadza się to do wyznaczenia tranzytywnego domknięcia zbioru zmodyfikowanych obiektów i wyłuskania z niego tylko identyfikatorów obiektów korzeniowy obecność referencji zwrotnych i powiązań dwukierunkowych w ramach zagnieżdżeń typu rodzic-dziecko wspiera silnie takie operacje. Wyniki zapamiętanego zapytania wyznaczone na bazie obiektów korzeniowych o identyfikatorach ze zbioru R u oraz bioru R old \ R new (usunięte obiekty korzeniowe) mogą po rozpatrywanej modyfikacji bazy danych stać się nieaktualne. Stąd, wyznaczając zbiór R old \ R new R u możemy wyznaczyć podzbiór wyników zapytania, które muszą zostać usunięte z pamięci podręcznej. Dotyczy to wszystkich wymagających aktualizacji zapamiętanych zapytań. Po tym zabiegu zapytania te muszą zostać zrewaluowane na najmniejszym fragmencie składu niezbędnym do wyznaczenia brakujących wyników w nowej bazie danych. Fragment ten jest wyznaczany przez drugi zbiór identyfikatorów obiektów korzeniowych: R new \ R old R u. Korzystając z definicji wyżej określonych zbiorów obiektów korzeniowych konieczne jest w ramach korekty przyrostowej wykonanie dwóch operacji: wyznaczenia nieaktualnych danych w wynikach zapamiętanych zapytań; realizacji redukcji i rozbudowania wyników zapytań. Nie wszystkie zapytania mogą być aktualizowane z użyciem tych zabiegów. Ich zbiór ogranicza się do rodziny zapytań spełniających następujące dwa warunki: wynik zapytania jest zbiorem identyfikatorów obiektów (ewentualnie oznaczonych nazwami pomocniczymi) lub dowolną kolekcją takich zbiorów (struktury, bagi i sekwencje identyfikatorów); 121

122 5. Aktualizacja wyników zapamiętanych zapytań wynik zapytania może być niezależnie obliczony dla pojedynczych elementów zbioru obiektów korzeniowych, tzn. można go uzyskać jako sumę mnogościową wyników uzyskanych przy ograniczeniu sekcji bazowej stosu środowiskowego do pojedynczego obiektu korzeniowego (zapytanie spełnia zasadę addytywności wyników). Zakładając, że zapamiętane zapytanie spełnia powyższe ograniczenia możemy uzyskać zbiór nieaktualnych elementów wyniku tego zapytania wyznaczając tranzytywne domknięcie zbioru R old \ R new R u na stanie bazy sprzed wykonania operacji modyfikacji (db old ) i usunąć z wyników zapytania identyfikatory otrzymanych obiektów za pomocą prostych operacji mnogościowych na zbiorach i kolekcjach. Tak zredukowany zbiór wyników zapytania może być następnie rozszerzony o identyfikatory obiektów uzyskanie poprzez ewaluację zapytania na nowej instancji bazy danych (db new ) z sekcją bazową zredukowaną do zbioru R new \ R old R u. Zgodnie z powyższym operacja przyrostowej korekty pewnego, spełniającego warunki tejże, zapamiętanego zapytania q po modyfikacji bazy danych u zmieniającej stan składu danych z db old w db new wyraża się następująco: W.5.1: q(r new, db new ) = q(r old, db old ) \ A(R old \ R new R u, db old ) q(r new \ R old R u, db new ) Ograniczenia założone na zapytania podlegające przyrostowej korekcie wyników powodują konieczność wyłączenia niektórych zapytań z takiej zoptymalizowanej korekty. Pierwsze ograniczenie eliminuje zapytania, których wyniki zawierają nietrwałe wartości atomowe, takie jak rezultaty działania operatorów algebraicznych (m.in. agregacje) oraz niektórych niealgebraicznych operatorów - kwantyfikatorów. W wielu przypadku zapytania będące argumentami tych operatorów spełniają to ograniczenie, więc proces dekompozycji zapytania przed zapamiętaniem w pamięci podręcznej (opisany w rozdziale 4.3.2) może uwzględniać to ograniczenie. Drugie ograniczenie nie jest natomiast tolerancyjne dla zagnieżdżonych zapytań oraz zapytań zawierających podzapytania niespełniające pierwszego ograniczenia. Z drugiej strony większość zapytań nawigacyjnych spełnia to ograniczenie. Zgodnie z opisanymi wcześniej regułami dekompozycji zapytania zagnieżdżone nie są zapamiętywane w swojej oryginalnej formie, lecz są rozkładane na wiele niezależnych podzapytań, które zazwyczaj spełniają oba ograniczenia. Zapytania nie satysfakcjonujące warunków korekty przyrostowej nie mogą być w ten sposób aktualizowane, więc dla ich utrzymania w zgodności ze zmiennym stanem bazy konieczna jest ponowna pełna reewaluacja lub (zgodnie z regułami optymalizacji zasobów przydzielonych dla pamięci podręcznej) usunięcie ich z pamięci podręcznej, gdyby taki proces był wykonywany zbyt często, a przez to zbyt kosztowny. 122

123 5. Aktualizacja wyników zapamiętanych zapytań Poza funkcjonalnością wyznaczania tranzytywnego domknięcia zbioru identyfikatorów obiektów konieczne jest zrealizowanie w systemie bazy danych mechanizmów pozwalających na: oszacowanie zawartości zbiorów identyfikatorów obiektów korzeniowych wchodzących w skład zbiorów R old \ R new i R new \ R old poprzez dostęp do stanu oryginalnej bazy danych db old tuż przed zatwierdzeniem transakcji modyfikującej bazę do stanu db new ; wyznaczenie (jako efekt uboczny wykonania modyfikacji) zawartości zbioru R u poprzez wykonanie tranzytywnego domknięcia zbioru zmodyfikowanych obiektów niekorzeniowych; wirtualne wykonanie wybranego zapytania lub sekwencyjnie wielu w kontekście kopii stosu środowisk z okrojoną sekcją bazową do odpowiedniego zbioru identyfikatorów obiektów korzeniowych. W przypadku konfiguracji systemu umożliwiającej opóźnioną aktualizację pamięci podręcznej po realizacji grupy modyfikacji w liście modyfikacji (przed zatwierdzeniem każdej z nich) muszą być zapamiętywane stany wyżej wymienionych zbiorów, zakładając, że nowy stan bazy po wykonaniu poprzedniej modyfikacji jest traktowany rekurencyjnie jako stary stan dla aktualnej modyfikacji. W ten sposób za pomocą szybkich operacji mnogościowych różnicy, przecięcia i sumy zbiorów zostaną utworzone wymagane zbiory właściwe dla grupy modyfikacji traktowanej w tej sytuacji jako jedna modyfikacja. Proces ten zadziała poprawnie jeżeli od momentu ostatniej aktualizacji w pamięci podręcznej nie pojawiły się żadne nowe zapamiętane zapytania ewaluowane na przejściowym stanie bazy danych (przy niepustej liście modyfikacji oczekujących na uwzględnienie w korekcie wyników zapytań). W przeciwnym wypadku, gdy takie zapytanie do zapamiętania się pojawi, aby zapamiętany mógł być dokładnie jeden stan bazy zgodny z aktualnie zapamiętanymi wynikami zapytań, konieczne jest przed zmaterializowaniem tego zapytania natychmiastowe przyspieszone wykonanie korekty pozostałych wymagających jej zapytań w kontekście aktualnej zawartości listy oczekujących modyfikacji. Przykład 5.3 Rozważmy przykład działania korekty przyrostowej oznaczając instancję bazy danych przedstawioną na Rys.3.10 jako db old wraz z jej zbiorem identyfikatorów obiektów korzeniowych R old = {i 1, i 9, i 21, i 25, i 30, i 35, i 40, i 48 }.Oto trzy przykładowe zapamiętane zapytania: q 1 : q 2 : q 3 : (Emp join works_in.dept).(name, dname) (Emp where salary < 2000).name avg(dept.count(employs)) 123

124 5. Aktualizacja wyników zapamiętanych zapytań Pierwsze zapytanie pobiera nazwiska pracowników wraz z nazwami departamentów, w których są zatrudnieni. Drugie zwraca nazwiska pracowników zarabiających mniej niż Trzecie zaś, średnią dla departamentów liczbę zatrudnionych w nich pracowników. Zapamiętane w pamięci podręcznej wyniki tych zapytań przedstawiają się następująco: q 1 (R old, db old ) = bag{struct{i 2, i 22 }, struct{i 10, i 22 }} q 2 (R old, db old ) = q 3 (R old, db old ) = 2 Załóżmy, że w bazie danych została uruchomiona modyfikacja u 1 rozumiana jako zbiorcze wywołanie dwóch następujących instrukcji imperatywnych tworzenia obiektu: create permanent struct( Programming as dname ) as Dept create permanent struct( ) as Emp Brown as name, as birthday, 1500 as salary, programmer as job, 8.5 as rating, (ref Dept where dname = Programming ) as works_in Modyfikacja ta zmieniła skład bazy danych poprzez wstawienie do niego dwóch nowych obiektów, po jednym klasy Dept i Emp (zakładamy, iż w momencie tworzenia w obiekcie pracownika podobiektu referencyjnego works_in został automatycznie utworzony w obiekcie departamentu obiekt referencji zwrotnej employs): <i 67, Dept, {<i 68, dname, Programming >, <i 69, employs, i 60 >}> <i 60, Emp, {<i 61, name, Brown >, <i 62, birthday, >, <i 63, salary, 1500>, <i 64, job, programmer >, <i 65, rating, 8.5>, <i 66, works_in, i 67 >}> W ten sposób powstała nowa instancja bazy danych db new z jej nowym zbiorem identyfikatorów obiektów korzeniowych R new = {i 1, i 9, i 21, i 25, i 30, i 35, i 40, i 48, i 60, i 67 }. Pojawiły się zatem dwa nowe obiekty korzeniowe, nie zmieniono żadnych poprzednio 124

125 5. Aktualizacja wyników zapamiętanych zapytań występujących w bazie danych i nowe dane nie są powiązane z dotychczasowymi obiektami korzeniowymi, więc zbiory identyfikatorów wyznaczające biorącą udział w modyfikacji część bazy danych wyglądają następująco: R old \ R new = R new \ R old = {i 60, i 67 } R u = W konsekwencji otrzymujemy: R old \ R new R u = R new \ R old R u = {i 60, i 67 } Modyfikacja u 1 dodała do bazy danych jeden obiekt klasy Emp i jeden klasy Dept. Zgodnie z regułą metody eliminacji opisanej w poprzednim rozdziale podschematy wszystkich trzech zapytań q 1, q 2 i q 3 mają część wspólną z podschematem tej modyfikacji, więc ich wyniki wymagają korekty. Zapytanie q 3 nie spełnia obu warunków postawionych dla korekty przyrostowej opisanej wzorem W.5.1, więc musi zostać wykonane ponownie lub oznaczone jako nieaktualne to opóźnionego wykonania gdy będzie ponownie potrzebne. Zapytania q 1 i q 2 spełniają te ograniczenia, zatem ich zapamiętane wyniki mogą zostać zaktualizowane metodą korekty przyrostowej. Zgodnie ze wzorem W.5.1 dla obu zapytań otrzymujemy nowe wyniki poprzez korektę starych wyników z użyciem wyników otrzymanych z ewaluacji tych zapytań na możliwie najmniejszym zmienionym fragmencie bazy danych: q 1 (R new, db new ) = = bag{struct{i 2, i 22 }, struct{i 10, i 22 }} \ A(, db old ) q 1 ({i 60, i 67 }, db new ) = = bag{struct{i 2, i 22 }, struct{i 10, i 22 }} \ bag{struct{i 61, i 68 }} = = bag{struct{i 2, i 22 }, struct{i 10, i 22 }, struct{i 61, i 68 }} q 2 (R new, db new ) = \ A(, db old ) q 2 ({i 60, i 67 }, db new ) = \ bag{i 61 } = bag{i 61 } Pełna reewaluacja zapytania q 3 da wynik: q 3 (R new, db new ) = 1.5 Oznaczmy teraz stan bazy danych po zakończeniu modyfikacji u 1 jako db old oraz aktualny stan zbioru identyfikatorów obiektów korzeniowych R old = {i 1, i 9, i 21, i 25, i 30, i 35, i 40, i 48, i 60, i 67 } i wykonajmy kolejną modyfikację u 2 bazy danych zmieniającą 125

126 5. Aktualizacja wyników zapamiętanych zapytań (zmniejszającą o 1000) wynagrodzenie pracownikom o nazwisku White (w aktualnym składzie jest jeden taki pracownik z wynagrodzeniem wynoszącym 2900): u 2 : update (Emp where name = White ).(salary, salary 1000) Powyższa modyfikacja nie zmienia zbioru obiektów korzeniowych, więc R old = R new i dla korekty przyrostowej istotny jest jedynie zbiór R u, który w tym przypadku nie jest pusty, gdyż zmodyfikowany obiekt o nazwie salary (o identyfikatorze i 12 ) jest osiągalny bezpośrednio lub pośrednio z wielu obiektów korzeniowych: R old \ R new = R new \ R old = R u = {i 1, i 9, i 21, i 25, i 30, i 35, i 40, i 48 } R old \ R new R u = {i 1, i 9, i 21, i 25, i 30, i 35, i 40, i 48 } R new \ R old R u = {i 1, i 9, i 21, i 25, i 30, i 35, i 40, i 48 } Dodatkowo tranzytywne domknięcie zbioru R u dla instancji db old zawiera wszystkie obiekty o identyfikatorach pomiędzy i 1 i i 52. Zapytania q 1 i q 3 są eliminowane z procesu korekty ze względy na rozłączność ich podschematów z podschematem modyfikacji, więc konieczna jest tylko aktualizacja wyników zapytania q 2 : q 2 (R new, db new ) = bag{i 61 } \ A({i 1, i 9, i 21, i 25, i 30, i 35, i 40, i 48 }, db old ) q 2 ({i 1, i 9, i 21, i 25, i 30, i 35, i 40, i 48 }, db new ) = = bag{i 61 } \ bag{i 1,..., i 52 } bag{i 10 } = bag{i 10, i 61 } W efekcie wśród wyników zapytania q 2 znalazł się dodatkowo identyfikator pracownika o nazwisku White. Taki sam wynik zostałby uzyskany przy ponownej ewaluacji tego zapytania wynagrodzenie tego pracownika wynosi po modyfikacji 1900, więc obiekt ten spełnia już warunek operatora where użytego w zapytaniu. W rozbudowanych bazach danych z wieloma powiązaniami pomiędzy obiektami bardzo prawdopodobne jest, że zbiór R u będzie bardzo duży będzie zawierał identyfikatory wszystkich lub prawie wszystkich obiektów korzeniowych. W tej sytuacji tranzytywne domknięcie takiego zbioru może zawierać znaczą część obiektów znajdujących się w składzie danych i jego wyznaczenie może zająć stosunkowo dużo czasu. Dla takiej bazy danych możliwe jest alternatywne rozwiązanie dotyczące zmiany formy przechowywania wyników zapamiętanych zapytań. Alternatywa ta może być konfigurowana lub wybierana na podstawie jednorazowego wyznaczenia podczas uruchamiania systemu stosunku średniej liczby obiektów osiągalnych z jednego obiektu korzeniowego i liczby wszystkich obiektów 126

127 5. Aktualizacja wyników zapamiętanych zapytań (jeżeli stosunek ten przekracza pewną wartość, np. 0.5). Rozwiązanie to przyspiesza korektę przyrostową i polega na wzbogaceniu właściwych wyników zapytania dodatkowymi informacjami dotyczącymi źródła każdego z częściowych wyników tego zapytania. Dla każdego zapytania q spełniającego warunki korekty przyrostowej, dla każdego pojedynczego obiektu korzeniowego ze zbioru R obiektów korzeniowych instancji db bazy danych wyznaczany jest podzbiór wszystkich wyników zapytania otrzymanych poprzez ewaluację tego zapytania z sekcją bazową stosu środowiskowego ograniczoną do tego jednego obiektu korzeniowego. Taki zbiór wyników częściowych, będący zbiorem par <identyfikator obiektu korzeniowego, zbiór wyników częściowych>, może być wyznaczany jako efekt uboczny normalnej ewaluacji zapytania i oznaczamy go P(q(R, db)). Jeżeli zapytanie q spełnia powyższe warunki i jest wyposażone w zbiór wyników częściowych, możliwe jest wyeliminowanie tranzytywnego domknięcia ze wzoru W.4.1 i zastąpienie go następującą regułą: W.5.2: P(q(R new, db new )) = P(q(R old, db old )) \ P(q(R old \ R new R u, db old )) P(q(R new \ R old R u, db new )) W tym przypadku po zmianach w bazie danych nie jest konieczne wyznaczanie tranzytywnego domknięcia zbioru R u i odejmowanie otrzymanych identyfikatorów ze zbioru wyników zapytania. Korekta przyrostowa przeprowadzana jest teraz z wykorzystaniem prostszych zbiorów: 1. W pierwszej kolejności od częściowych wyników zapamiętanego zapytania wyliczonych w kontekście poprzedniej wersji bazy danych odejmowany jest mnogościowo zbiór wszystkich wyników częściowych z pierwszym elementem pary będącym identyfikatorem należącym do zbioru R old \ R new R u. 2. Następnie konieczne jest dodanie do otrzymanego zbioru nowych wyników częściowych wyliczonych dla zapytania w nowej instancji bazy danych ze zbiorem identyfikatorów obiektów korzeniowych ograniczonym do R new \ R old R u. 3. Ostateczny wynik zapytania jest wyznaczany jako suma drugich elementów par wyników częściowych z finalnego zbioru. Optymalizacja korekty przyrostowej zapamiętanych zapytań jest więc realizowana niewielkim dodatkowym kosztem czasu utworzenia wyników częściowych, zasobów pamięci podręcznej niezbędnych do ich przechowania obok wyników właściwych oraz czasu potrzebnego na skomponowanie finalnych wyników z wyników częściowych. 127

128 5. Aktualizacja wyników zapamiętanych zapytań Przykład 5.4 Załóżmy, że aktualny stan bazy danych db old, to stan zaraz po wykonaniu modyfikacji u 1 z Przykładu 5.3. Zbiór wyników częściowych zapytania q 2 wygląda następująco: P(q 2 (R old, db old )) = {<i 1, >, <i 9, >, <i 21, >, <i 25, >, <i 30, >, <i 35, >, <i 40, >, <i 48, >, <i 60, bag{i 61 }>, <i 67, >} Zgodnie z regułą W.5.2, w ramach korekty tego zapytania wymuszonej przez modyfikację u 2, pozbywamy się w pierwszej kolejności wszystkich par z pierwszym elementem należącym do zbioru R old \ R new R u, co w tym przypadku odpowiada zbiorowi R u = {i 1, i 9, i 21, i 25, i 30, i 35, i 40, i 48 }, otrzymując: P(q 2 (R old, db old )) \ P(q 2 (R old \ R new R u, db old )) = {<i 60, bag{i 61 }>, <i 67, >} Następnie wykonujemy zapytanie w nowej instancji bazy danych dla zbioru identyfikatorów obiektów korzeniowych R new \ R old R u (w rozpatrywanym przypadku jest to znów zbiór R u ) uzyskując zbiór nowych brakujących wyników częściowych: P(q 2 (R new \ R old R u, db new )) = {<i 1, >, <i 9, bag{i 10 }>, <i 21, >, <i 25, >, <i 30, >, <i 35, >, <i 40, >, <i 48, >} Suma tych zbiorów daje ostatecznie: P(q 2 (R new, db new )) = {<i 1, >, <i 9, bag{i 10 }>, <i 21, >, <i 25, >, <i 30, >, <i 35, >, <i 40, >, <i 48, >, <i 60, bag{i 61 }>, <i 67, >} Konstruując finalny wynik zapytania po korekcie jako zbiór drugich elementów otrzymanego zbioru par otrzymujemy wynik zgodny z otrzymanym wcześniejszą metodą: q 2 (R new, db new ) = bag{i 10, i 61 } 128

129 6. Prototyp 6. Prototyp Do implementacji mechanizmów związanych z optymalizacją zapytań poprzez zapamiętywanie ich wyników wykorzystano prototyp obiektowej bazy danych o nazwie ODRA (ang. Object Database for Rapid Application development) implementującej większość aspektów ustalonych w ramach podejścia SBA. Prototyp ten wykonano w języku programowania Java v1.5 [Sun15]. Celem projektu ODRA jest stworzenie narzędzia nowej generacji dla przyszłych programistów aplikacji bazodanowych. Jest to narzędzie czysto obiektowe z punktu widzenia programisty (wyeliminowano problem niezgodności impedancji cechujący rozwiązania, w których obiektowy język programowania, taki jak Java, C++, czy C#, służy do oprogramowania relacyjnych baz danych poprzez zanurzenie w nim języka zapytań SQL). Jest ono oparte o język zapytań SBQL, nowy obiektowy język programowania wysokiego poziomu. W ramach projektu powstał system bazy danych i środowisko uruchomieniowe dla języka SBQL zawierające: magazyn danych (Data Store) przechowuje obiekty zarówno trwałe jak i nietrwałe; dostęp do obiektów odbywa się przez referencje; przechowuje również kod procedur, perspektyw, klas i modułów; stos środowisk (Environment Stack; ENVS); stos rezultatów (QRES) zawiera pośrednie i ostateczne rezultaty zapytań; interpreter wykonujący zapytania, instrukcje imperatywne, procedury, metody klas i aktualizowalnych perspektyw; optymalizator wykorzystujący metody niezależnych podzapytań, martwych podzapytań, indeksy; importer i eksporter danych w formacie XML; osłony (ang. wrappers) dla relacyjnych baz danych oraz usług Web Services; narzędzia dla rozproszenia i komunikacji z interfejsami Java,.NET i Web Services system autoryzacji dostępu, zarządzania zasobami i uprawnieniami; interfejsy IDE oraz CLI. Więcej informacji na temat projektu ODRA można znaleźć w [LS07, ADH+08, ODRA1]. 129

130 6. Prototyp Celem powstania prototypu było udowodnienie, iż rozwiązania zaproponowane w poprzednich rozdziałach wprowadzają istotną optymalizację czasu wykonywania zapytań wyrażonych w języku SBQL kierowanych do obiektowej bazy danych. Dzięki tej implementacji potwierdzona została teza niniejszej rozprawy. Ze względu na ograniczenia czasowe nie zaimplementowano wszystkich elementów wchodzących w skład omawianego mechanizmu. Dotyczy to przede wszystkim kwestii związanych z aktualizacją wyników zapamiętanych zapytań po zmianach w bazie opisanych w rozdziale 5, tzn. metody eliminacji opartej na definicjach podschematów oraz korekty przyrostowej wyników. Realizacja tych elementów może być istotna w systemach, w których często dochodzi do zmian w bazie danych. Prototyp składa się z trzech grup modułów: 1. Moduły optymalizatora zaimplementowano metody normalizacji zapytań polegające na zamianie kolejności operandów i operatorów, dekompozycji z wydzielaniem niezależnych podzapytań, analizy zapytania prowadzącej do wyszukania go w rejestrze zapamiętanych zapytań oraz dodania do tego rejestru i aktualizacji typologicznej drzewa syntaktycznego po optymalizacji. 2. Moduły odpowiedzialne za interpretację i aktualizację wyników zaimplementowano interpretację wyników pobieranych z rejestru zapamiętanych zapytań, aktualizację statystyk wykorzystania zapamiętanych wyników i pełną reewaluację wyników zapytań. 3. Moduły realizujące działanie menadżera zapamiętanych zapytań (nazywanego w prototypie menadżerem pamięci podręcznej). Ta grupa modułów zawiera implementację narzędzi wykorzystywanych przez klasy wchodzące w skład dwóch pierwszych modułów. Zaimplementowano moduł menadżera podzielonego na podmoduły do zarządzania danymi i metadanymi zapamiętanych zapytań. Powstały struktury danych do szybkiego wyszukiwania zapamiętanych zapytań (indeks zapytań będący liniowo haszowaną tablicą ze znormalizowaną tekstową postacią zapytania jako kluczem) oraz utrzymywania statystyk użycia tych zapytań opartych na priorytetowych listach MRU (do tego celu stworzono specjalny kontroler optymalizujący użycie zasobów pamięciowych przydzielonych dla rejestru zapytań). Wykorzystano trwały i ulotny skład danych do przechowywania wyników zapytań. Wprowadzono szereg komend CLI (ang. Command Level Interface) pozwalających administratorowi bazy danych na konfigurowanie działania mechanizmu zapamiętywania wyników. Powstał kontroler konfiguracji gromadzący i udostępniający innym modułom informacje dotyczące ustawień optymalizacji przez cache'owanie. Implementacja prototypu została wykonana zgodnie z architekturą systemu przetwarzania zapytań zaprezentowaną na Rys.4.1, zgodnie z którą dane wymieniane są pomiędzy trzema głównymi elementami systemu: 130

131 6. Prototyp optymalizatorem cache'ującym znajdującym się w klienckiej części systemu, korzystającym z informacji zdobywanych podczas statycznej ewaluacji drzewa syntaktycznego oraz z danych zgromadzonych w metabazie (m.in. z grafu schematu bazy danych oraz meta-informacji związanych z zapamiętanymi zapytaniami); interpreterem, również znajdującym się w klienckiej części systemu, ewaluującym zoptymalizowany i skompilowany plan wykonania zapytania, pobierającym wyniki zapytań z rejestru i ewentualnie przekazującym je do zapamiętania lub zaktualizowania; menadżerem pamięci podręcznej zarządzającym po stronie serwera bazy danych rejestrem zapamiętanych zapytań i optymalizującym wykorzystanie zasobów. Szczegółowy opis klas wchodzących w skład powstałego prototypu, wraz z podziałem na funkcjonalność ich pól i metod oraz wzajemnie zależności i odwołania, został przedstawiony w kolejnych podrozdziałach. 6.1 Optymalizator Implementacja optymalizatora wykorzystującego wyniki zapytań i proponującego zapamiętanie nowych wykorzystuje istniejące w projekcie ODRA konstrukcje programistyczne (interfejs ISBQLOptimizer oraz klasy SBQLIndependentQueryOptimizer i ASTNode) oraz wprowadza trzy nowe klasy CacheOptimizer, QueryFinder i IndependentQuery ISBQLOptimizer ISBQLOptimizer (Rys.6.1) jest interfejsem bazowym, który implementują wszystkie optymalizatory zaimplementowane w systemie ODRA. Rys.6.1. Schemat interfejsu ISBQLOptimizer Metody: optimize Służy do optymalizacji zapytania przesłanego jako parametr w postaci drzewa syntaktycznego (ASTNode). 131

132 6. Prototyp reset Operacja wykonywana podczas resetowania optymalizatora. setstaticeval Służy do ustawienia obiektu klasy ASTAdapter, który sprawdza poprawność typologiczną zapytania ASTNode Zapytanie przesłane do optymalizacji jest zamieniane z postaci tekstowej do postaci drzewa zaimplementowanego za pomocą klasy ASTNode. Każdy element danego drzewa zawiera jedno bądź dwa odgałęzienia. Węzłami drzewa są wyrażenia będące operatorami algebraicznymi (np. or, =), niealgebraicznymi (np. where), koercje (np. niejawne konwersje zamieniające identyfikator obiektu na jego wartość w celu zastosowania jej w ramach innego operatora, np. =). Zapytaniu: Prac where Zar = 1400 or Zar = 1500 and Zar = 1100 odpowiada drzewo przedstawione na Rys.6.2: Rys.6.2. Drzewo ASTNode zapytania 132

133 6. Prototyp CacheOptimizer Jeśli w konfiguracji ustawione jest użycie pamięci podręcznej, podczas optymalizacji zapytanie zostanie przesłane do optymalizatora wykorzystującego wyniki zapamiętanych zapytań klasy CacheOptimizer (Rys.6.3) implementującej interfejs ISBQLOptimizer. Rys.6.3. Optymalizator pamięci podręcznej wyników Pola: qf Obiekt klasy QueryFinder do niego zostaje przesłane zapytanie w celu sprawdzenia i ewentualnego dodania zapytania do pamięci podręcznej. staticeval Obiekt klasy ASTAdapter, odpowiedzialny za sprawdzenie poprawności typologicznej zapytania. Metody: optimize Funkcja ta przyjmuje zapytanie i przeprowadza je przez kolejne funkcje: refactorquery, independentoptymize, oraz dodaje całe zapytanie do zapamiętania w pamięci podręcznej za pomocą obiektu qf klasy QueryFinder. refactorquery Dokonuje normalizacji zapytania. Normalizacja zapytania jest realizowana na zasadzie przechodzenia poziomami drzewa (kolejnymi zagnieżdżonymi operatorami binarnymi). Po napotkaniu na operatory: 133

134 6. Prototyp >, >= : dokonuje zamiany prawej i lewej gałęzi zapytania oraz odpowiednio zmienia operator na <, <= ; =, <> : zamienia lewą i prawą część zapytania na wartość tekstową i porównuje je ze sobą. Wartość zapytania, która w porównywaniu tekstowym jest mniejsza, wstawiana jest po lewej stronie; or, and, +,, * : po napotkaniu jednego z tych operatorów dodaje wartość prawej gałęzi do kolejki priorytetowej. Kolejka ta jest odpowiedzialna za sortowanie wejściowych zapytań. Zapytanie jest zmieniane na wartość tekstową i wstawiane w odpowiedniej kolejności. Następnie sprawdza czy lewa gałąź też nie jest operatorem tego samego typu. Jeśli nie jest, wówczas dokonuje dalszej normalizacji lewego zapytania. Natomiast jeśli operator jest tego samego typu, analogicznie dodaje prawą część lewego zapytania do kolejki i analizuje jego lewą część. Po zakończeniu analizy lewej części dodaje całą wartość lewego zapytania do kolejki, ale pod warunkiem, że aktualnie analizowany operator nie jest operatorem odejmowania. Jeżeli nie jest to odejmowanie pobiera pierwsze zapytanie z kolejki i wstawia je jako lewe podzapytanie. Ostatecznie, niezależnie już od operatora, rozpoczynając od aktualnego operatora i cofając się poziomami do góry do operatorów nadrzędnych, zamienia wartości prawych zapytań dla tych poziomów na odpowiednio posortowane pobierając kolejne zapytania z kolejki. reset Funkcja ta jest uruchamiana podczas wykonywania operacji resetowania optymalizatora. W tym optymalizatorze nie wykonuje żadnych operacji. setstaticeval Służy do ustawienia obiektu staticeval klasy ASTAdapter, który sprawdza poprawność typologiczną zapytania QueryFinder Obiekt tej klasy (Rys.6.3) jest głównym obiektem, przez który odbywa się wyszukiwanie i dodawanie zapytań do pamięci podręcznej. Jako jedyny moduł wewnątrz optymalizatora podczas optymalizacji ma dostęp do menadżera pamięci podręcznej (CacheManager). Pola: cachemanager Obiekt klasy CacheManager odpowiedzialny za wszystkie operacje na pamięci podręcznej potrzebny do wyszukiwania i dodawania nowych zapytań. 134

135 6. Prototyp Metody: findcache Sprawdza najpierw złożoność zapytania (checkcomlexquery), a następnie przesyła całość do menadżera pamięci podręcznej, który sprawdza czy podane zapytanie już zostało zapamiętane. Jeśli taki wpis znajduje się już w bazie wtedy funkcja zwraca jako rezultat procedurę $cache_proc z odpowiednimi parametrami dla danego zapytania, w przeciwnym wypadku generuje wyjątek. addcache Sprawdza najpierw złożoność zapytania (checkcomlexquery), a następnie przesyła całość do menadżera pamięci podręcznej, który sprawdza czy podane zapytanie już zostało zapamiętane. Jeśli nie, wówczas zostaje automatycznie dodane do pamięci. Jako rezultat zwracana jest procedura $cache_proc z odpowiednimi parametrami dla danego zapytania. checkcomlexquery Sprawdza złożoność zapytania. Proces ten, podobnie jak normalizacja, polega na przeanalizowaniu drzewa zapytania, w tym wypadku sprawdza czy zapytanie zawiera odwołane do obiektu, który znajduje się w bazie oraz dodatkowo czy zawiera co najmniej jeden z operatorów: nawigacji (.), złączenia (join) lub selekcji (where) IndependentQuery Rys.6.4. Optymalizator niezależnych podzapytań W projekcie ODRA jest już zaimplementowany optymalizator wyszukiwania niezależnych podzapytań (SBQLIndependentQueryOptimizer), dlatego klasa IndependentQuery (Rys.6.4) jest klasą pochodną z tej klasy. Jedyną zmianą, jaka została wprowadzona w tej klasie specjalizowanej jest modyfikacja funkcji factoring, która 135

136 6. Prototyp zostaje wywołana, jeśli znaleziono niezależne podzapytanie. Modyfikacją jest sprawdzenie czy dane niezależne podzapytanie znajduje się już w pamięci podręcznej i ewentualne dodanie tego zapytania do bazy pamiętanych wyników, co realizowane jest poprzez przesłanie go do obiektu queryfinder klasy QueryFinder. 6.2 Interpreter Interpreter (obiekt istniejącej klasy SBQLInterpreter) zajmuje się analizą i wykonaniem otrzymanego kodu. Jeśli natrafi na kod użycia wyników zapamiętanych w pamięci podręcznej (procedura $cache_proc umieszczona w planie ewaluacji przez optymalizator), przenosi sterowanie do nowej swojej metody - funkcji callcache. Funkcja ta odwołuje się do menadżera pamięci podręcznej (metody getdata klasy CacheManager), który zwraca rezultat oryginalnego zapamiętanego zapytania i ewentualnie dokonuje innych operacji utrzymania pamięci podręcznej (optymalizacja zasobów, aktualizacja wyników zapytań wymagających zmiany). 6.3 Menadżer pamięci podręcznej Menadżer pamięci podręcznej jest głównym modułem odpowiedzialnym za wszystkie operacje na pamięci podręcznej. W jego skład wchodzą następujące klasy pomocnicze: menadżer metabazy (CacheMetaManager) odwołuje się do danych w metabazie, zawiera sygnatury zapytań; menadżer danych (CacheDataManager) odwołuje się do danych zapisanych w bazie, podzielony na następujące klasy kontroli danych: menadżer statystyk (CacheMRUController) zawiera statystyki pamięci podręcznej; menadżer konfiguracji (ConfigController) zawiera konfigurację określającą sposób działania pamięci podręcznej; menadżer zapytań (CacheDataResultController) zawiera zapytania oryginalne wraz z rezultatami; rejestr zapytań (indeks), zawiera wpisy zapytań zapisanych w bazie CacheManager Główna klasa odpowiedzialna za pracę składu zapamiętanych zapytań, czyli pamięci podręcznej (Rys.6.5). 136

137 6. Prototyp Pola: admod Dostęp do modułu administratora. W związku z tym, że pewne operacje można realizować tylko z uprawnieniami administracyjnymi, wymagany jest dostęp do takiego modułu. oid Numer węzła, w którym znajduje się obiekt CacheManager. nkidx Rejestr indeksowy, w którym znajdują się nazwy zapamiętanych zapytań wraz z indeksem wewnętrznym (OID) obiektu metabazy danego zapytania. Obiekt ten zawiera sygnatury i indeks obiektu w bazie danych. Rejestr jest wspomagany funkcjami haszującymi, dzięki czemu czas wyszukania, dodawania, usuwania wpisów jest bardzo szybki. Podczas dodawania zapytania do rejestru jest ono haszowane przez odpowiednie funkcje, następnie w miejscu określonym przez funkcje haszującą zapisywana jest para (zapytanie, OID). Wyszukiwanie danego zapytania zwraca OID obiektu metabazy, podanego podczas dodawania zapytania, bądź wartość pustą (NULL), jeśli takiego wpisu nie znaleziono. Usuwanie polega na wyszukaniu węzła odpowiadającego zapytaniu i usunięcie go z rejestru. Do implementacji zastosowano metodę liniowego haszowania zrealizowaną w przypadku systemu ODRA dla tradycyjnych indeksów [KWK+08]. generator Konwertuje kod z postaci tekstowej do postaci rozumianej przez interpreter (ByteCode). Metody: addquery Tworzy nowe węzły dla nowego zapytania w bazie i metabazie. W bazie zapisuje wartość oryginalnego zapytania, natomiast w metabazie zapisuje indeks węzła w bazie oraz zapisuje sygnatury (w tym przypadku jest to typ rezultatu zwracanego przez zapytanie). clearoldcache W momencie przepełnienia pamięci podręcznej dokonuje usuwania najrzadziej używanych zapytań. 137

138 6. Prototyp Rys.6.5. Menadżer pamięci podręcznej checkcacheexists Sprawdza czy pamięć podręczna została już zainicjalizowana. Realizowane jest to przez sprawdzenie czy nazwa obiektu klasy CacheManager została dodana do listy dostępnych obiektów. clearcache Usuwa wszystkie obiekty z bazy, metabazy, rejestru zapytań oraz zeruje statystyki wykorzystania pamięci podręcznej. 138

Wykład XII. optymalizacja w relacyjnych bazach danych

Wykład XII. optymalizacja w relacyjnych bazach danych Optymalizacja wyznaczenie spośród dopuszczalnych rozwiązań danego problemu, rozwiązania najlepszego ze względu na przyjęte kryterium jakości ( np. koszt, zysk, niezawodność ) optymalizacja w relacyjnych

Bardziej szczegółowo

Temat : SBQL 1 obiektowy język zapytań.

Temat : SBQL 1 obiektowy język zapytań. Laboratorium Języki i środowiska przetwarzania danych rozproszonych Temat : SBQL 1 obiektowy język zapytań. Historia zmian Data Wersja Autor Opis zmian 23.4.2012 1.0 Tomasz Kowalski Utworzenie dokumentu

Bardziej szczegółowo

Modelowanie hierarchicznych struktur w relacyjnych bazach danych

Modelowanie hierarchicznych struktur w relacyjnych bazach danych Modelowanie hierarchicznych struktur w relacyjnych bazach danych Wiktor Warmus (wiktorwarmus@gmail.com) Kamil Witecki (kamil@witecki.net.pl) 5 maja 2010 Motywacje Teoria relacyjnych baz danych Do czego

Bardziej szczegółowo

Spis treści. Przedmowa

Spis treści. Przedmowa Spis treści Przedmowa V 1 SQL - podstawowe konstrukcje 1 Streszczenie 1 1.1 Bazy danych 1 1.2 Relacyjny model danych 2 1.3 Historia języka SQL 5 1.4 Definiowanie danych 7 1.5 Wprowadzanie zmian w tabelach

Bardziej szczegółowo

Indeksy w bazach danych. Motywacje. Techniki indeksowania w eksploracji danych. Plan prezentacji. Dotychczasowe prace badawcze skupiały się na

Indeksy w bazach danych. Motywacje. Techniki indeksowania w eksploracji danych. Plan prezentacji. Dotychczasowe prace badawcze skupiały się na Techniki indeksowania w eksploracji danych Maciej Zakrzewicz Instytut Informatyki Politechnika Poznańska Plan prezentacji Zastosowania indeksów w systemach baz danych Wprowadzenie do metod eksploracji

Bardziej szczegółowo

Systemy GIS Tworzenie zapytań w bazach danych

Systemy GIS Tworzenie zapytań w bazach danych Systemy GIS Tworzenie zapytań w bazach danych Wykład nr 6 Analizy danych w systemach GIS Jak pytać bazę danych, żeby otrzymać sensowną odpowiedź......czyli podstawy języka SQL INSERT, SELECT, DROP, UPDATE

Bardziej szczegółowo

Oracle11g: Wprowadzenie do SQL

Oracle11g: Wprowadzenie do SQL Oracle11g: Wprowadzenie do SQL OPIS: Kurs ten oferuje uczestnikom wprowadzenie do technologii bazy Oracle11g, koncepcji bazy relacyjnej i efektywnego języka programowania o nazwie SQL. Kurs dostarczy twórcom

Bardziej szczegółowo

PHP: bazy danych, SQL, AJAX i JSON

PHP: bazy danych, SQL, AJAX i JSON 1 PHP: bazy danych, SQL, AJAX i JSON SYSTEMY SIECIOWE Michał Simiński 2 Bazy danych Co to jest MySQL? Jak się połączyć z bazą danych MySQL? Podstawowe operacje na bazie danych Kilka dodatkowych operacji

Bardziej szczegółowo

Teoretyczne podstawy informatyki

Teoretyczne podstawy informatyki Teoretyczne podstawy informatyki Wykład 8b: Algebra relacyjna http://hibiscus.if.uj.edu.pl/~erichter/dydaktyka2009/tpi-2009 Prof. dr hab. Elżbieta Richter-Wąs 1 Algebra relacyjna Algebra relacyjna (ang.

Bardziej szczegółowo

Przestrzenne bazy danych Podstawy języka SQL

Przestrzenne bazy danych Podstawy języka SQL Przestrzenne bazy danych Podstawy języka SQL Stanisława Porzycka-Strzelczyk porzycka@agh.edu.pl home.agh.edu.pl/~porzycka Konsultacje: wtorek godzina 16-17, p. 350 A (budynek A0) 1 SQL Język SQL (ang.structured

Bardziej szczegółowo

Optymalizacja w relacyjnych bazach danych - wybór wydajnej strategii obliczania wyrażenia relacyjnego.

Optymalizacja w relacyjnych bazach danych - wybór wydajnej strategii obliczania wyrażenia relacyjnego. Plan wykładu Spis treści 1 Optymalizacja 1 1.1 Etapy optymalizacji............................... 3 1.2 Transformacja zapytania............................ 3 1.3 Przepisywanie zapytań.............................

Bardziej szczegółowo

Bazy danych wykład dwunasty. dwunasty Wykonywanie i optymalizacja zapytań SQL 1 / 36

Bazy danych wykład dwunasty. dwunasty Wykonywanie i optymalizacja zapytań SQL 1 / 36 Bazy danych wykład dwunasty Wykonywanie i optymalizacja zapytań SQL Konrad Zdanowski Uniwersytet Kardynała Stefana Wyszyńskiego, Warszawa dwunasty Wykonywanie i optymalizacja zapytań SQL 1 / 36 Model kosztów

Bardziej szczegółowo

Temat : SBQL 1 obiektowy język zapytań.

Temat : SBQL 1 obiektowy język zapytań. Laboratorium Języki i środowiska programowania systemów rozproszonych Temat : SBQL 1 obiektowy język zapytań. Historia zmian Data Wersja Autor Opis zmian 23.4.2012 1.0 Tomasz Kowalski Utworzenie dokumentu

Bardziej szczegółowo

SQL w 24 godziny / Ryan Stephens, Arie D. Jones, Ron Plew. Warszawa, cop Spis treści

SQL w 24 godziny / Ryan Stephens, Arie D. Jones, Ron Plew. Warszawa, cop Spis treści SQL w 24 godziny / Ryan Stephens, Arie D. Jones, Ron Plew. Warszawa, cop. 2016 Spis treści O autorach 11 Podziękowania 12 Część I Wprowadzenie do języka SQL 13 Godzina 1. Witamy w świecie języka SQL 15

Bardziej szczegółowo

Hurtownie danych. Przetwarzanie zapytań. http://zajecia.jakubw.pl/hur ZAPYTANIA NA ZAPLECZU

Hurtownie danych. Przetwarzanie zapytań. http://zajecia.jakubw.pl/hur ZAPYTANIA NA ZAPLECZU Hurtownie danych Przetwarzanie zapytań. Jakub Wróblewski jakubw@pjwstk.edu.pl http://zajecia.jakubw.pl/hur ZAPYTANIA NA ZAPLECZU Magazyny danych operacyjnych, źródła Centralna hurtownia danych Hurtownie

Bardziej szczegółowo

Wykład I. Wprowadzenie do baz danych

Wykład I. Wprowadzenie do baz danych Wykład I Wprowadzenie do baz danych Trochę historii Pierwsze znane użycie terminu baza danych miało miejsce w listopadzie w 1963 roku. W latach sześcdziesątych XX wieku został opracowany przez Charles

Bardziej szczegółowo

6. Bezpieczeństwo przy współpracy z bazami danych

6. Bezpieczeństwo przy współpracy z bazami danych 6. Bezpieczeństwo przy współpracy z bazami danych 6.1. Idea ataku SQL injection Atak znany jako SQL injection jest możliwy wtedy, gdy użytkownik ma bezpośredni wpływ na postać zapytania wysyłanego do bazy

Bardziej szczegółowo

XQTav - reprezentacja diagramów przepływu prac w formacie SCUFL przy pomocy XQuery

XQTav - reprezentacja diagramów przepływu prac w formacie SCUFL przy pomocy XQuery http://xqtav.sourceforge.net XQTav - reprezentacja diagramów przepływu prac w formacie SCUFL przy pomocy XQuery dr hab. Jerzy Tyszkiewicz dr Andrzej Kierzek mgr Jacek Sroka Grzegorz Kaczor praca mgr pod

Bardziej szczegółowo

Programowanie obiektowe

Programowanie obiektowe Programowanie obiektowe Wykład 13 Marcin Młotkowski 27 maja 2015 Plan wykładu Trwałość obiektów 1 Trwałość obiektów 2 Marcin Młotkowski Programowanie obiektowe 2 / 29 Trwałość (persistence) Definicja Cecha

Bardziej szczegółowo

QUERY język zapytań do tworzenia raportów w AS/400

QUERY język zapytań do tworzenia raportów w AS/400 QUERY język zapytań do tworzenia raportów w AS/400 Dariusz Bober Katedra Informatyki Politechniki Lubelskiej Streszczenie: W artykule przedstawiony został język QUERY, standardowe narzędzie pracy administratora

Bardziej szczegółowo

Porównanie systemów zarządzania relacyjnymi bazami danych

Porównanie systemów zarządzania relacyjnymi bazami danych Jarosław Gołębiowski 12615 08-07-2013 Porównanie systemów zarządzania relacyjnymi bazami danych Podstawowa terminologia związana z tematem systemów zarządzania bazami danych Baza danych jest to zbiór danych

Bardziej szczegółowo

PRZESTRZENNE BAZY DANYCH WYKŁAD 2

PRZESTRZENNE BAZY DANYCH WYKŁAD 2 PRZESTRZENNE BAZY DANYCH WYKŁAD 2 Baza danych to zbiór plików, które fizycznie przechowują dane oraz system, który nimi zarządza (DBMS, ang. Database Management System). Zadaniem DBMS jest prawidłowe przechowywanie

Bardziej szczegółowo

Integralność danych Wersje języka SQL Klauzula SELECT i JOIN

Integralność danych Wersje języka SQL Klauzula SELECT i JOIN Integralność danych Wersje języka SQL Klauzula SELECT i JOIN Robert A. Kłopotek r.klopotek@uksw.edu.pl Wydział Matematyczno-Przyrodniczy. Szkoła Nauk Ścisłych, UKSW Integralność danych Aspekty integralności

Bardziej szczegółowo

Optimizing Programs with Intended Semantics

Optimizing Programs with Intended Semantics Interaktywna optymalizacja programów 26 kwietnia 2010 Spis treści Spis treści Wstęp Omówienie zaproponowanego algorytmu na przykładzie Wewnętrzna reprezentacja reguł dotyczących optymalizacji Wybrane szczegóły

Bardziej szczegółowo

Baza danych. Baza danych to:

Baza danych. Baza danych to: Baza danych Baza danych to: zbiór danych o określonej strukturze, zapisany na zewnętrznym nośniku (najczęściej dysku twardym komputera), mogący zaspokoić potrzeby wielu użytkowników korzystających z niego

Bardziej szczegółowo

Model semistrukturalny

Model semistrukturalny Model semistrukturalny standaryzacja danych z różnych źródeł realizacja złożonej struktury zależności, wielokrotne zagnieżdżania zobrazowane przez grafy skierowane model samoopisujący się wielkości i typy

Bardziej szczegółowo

Indeksowanie w bazach danych

Indeksowanie w bazach danych w bazach Katedra Informatyki Stosowanej AGH 5grudnia2013 Outline 1 2 3 4 Czym jest indeks? Indeks to struktura, która ma przyspieszyć wyszukiwanie. Indeks definiowany jest dla atrybutów, które nazywamy

Bardziej szczegółowo

Informatyka I. Standard JDBC Programowanie aplikacji bazodanowych w języku Java

Informatyka I. Standard JDBC Programowanie aplikacji bazodanowych w języku Java Informatyka I Standard JDBC Programowanie aplikacji bazodanowych w języku Java dr inż. Andrzej Czerepicki Politechnika Warszawska Wydział Transportu 2017 Standard JDBC Java DataBase Connectivity uniwersalny

Bardziej szczegółowo

KOR. Konstrukcja systemów obiektowych i rozproszonych. PJWSTK, 2010 Emil Wcisło.

KOR. Konstrukcja systemów obiektowych i rozproszonych. PJWSTK, 2010 Emil Wcisło. KOR Konstrukcja systemów obiektowych i rozproszonych 2010 PJWSTK, 2010 Emil Wcisło Ćwiczenia Ćwiczenia z KOR w roku 2010/11 w semestrze letnim odbywać się będą: Dla studiów niestacjonarnych: Dla grupy

Bardziej szczegółowo

Alicja Marszałek Różne rodzaje baz danych

Alicja Marszałek Różne rodzaje baz danych Alicja Marszałek Różne rodzaje baz danych Rodzaje baz danych Bazy danych można podzielić wg struktur organizacji danych, których używają. Można podzielić je na: Bazy proste Bazy złożone Bazy proste Bazy

Bardziej szczegółowo

Język SQL. instrukcja laboratoryjna. Politechnika Śląska Instytut Informatyki. laboratorium Bazy Danych

Język SQL. instrukcja laboratoryjna. Politechnika Śląska Instytut Informatyki. laboratorium Bazy Danych Politechnika Śląska Instytut Informatyki instrukcja laboratoryjna laboratorium Bazy Danych przygotowali: mgr inż. Paweł Kasprowski (Kasprowski@zti.iinf.polsl.gliwice.pl) mgr inż. Bożena Małysiak (bozena@ivp.iinf.polsl.gliwice.pl)

Bardziej szczegółowo

Administracja i programowanie pod Microsoft SQL Server 2000

Administracja i programowanie pod Microsoft SQL Server 2000 Administracja i programowanie pod Paweł Rajba pawel@ii.uni.wroc.pl http://www.kursy24.eu/ Zawartość modułu 9 Optymalizacja zapytań Pobieranie planu wykonania Indeksy i wydajność - 1 - Zadania optymalizatora

Bardziej szczegółowo

Autor: Joanna Karwowska

Autor: Joanna Karwowska Autor: Joanna Karwowska Jeśli pobieramy dane z więcej niż jednej tabeli, w rzeczywistości wykonujemy tak zwane złączenie. W SQL istnieją instrukcje pozwalające na formalne wykonanie złączenia tabel - istnieje

Bardziej szczegółowo

Dariusz Brzeziński. Politechnika Poznańska, Instytut Informatyki

Dariusz Brzeziński. Politechnika Poznańska, Instytut Informatyki Dariusz Brzeziński Politechnika Poznańska, Instytut Informatyki Język programowania prosty bezpieczny zorientowany obiektowo wielowątkowy rozproszony przenaszalny interpretowany dynamiczny wydajny Platforma

Bardziej szczegółowo

Struktura drzewa w MySQL. Michał Tyszczenko

Struktura drzewa w MySQL. Michał Tyszczenko Struktura drzewa w MySQL Michał Tyszczenko W informatyce drzewa są strukturami danych reprezentującymi drzewa matematyczne. W naturalny sposób reprezentują hierarchię danych toteż głównie do tego celu

Bardziej szczegółowo

77. Modelowanie bazy danych rodzaje połączeń relacyjnych, pojęcie klucza obcego.

77. Modelowanie bazy danych rodzaje połączeń relacyjnych, pojęcie klucza obcego. 77. Modelowanie bazy danych rodzaje połączeń relacyjnych, pojęcie klucza obcego. Przy modelowaniu bazy danych możemy wyróżnić następujące typy połączeń relacyjnych: jeden do wielu, jeden do jednego, wiele

Bardziej szczegółowo

SQL (ang. Structured Query Language)

SQL (ang. Structured Query Language) SQL (ang. Structured Query Language) SELECT pobranie danych z bazy, INSERT umieszczenie danych w bazie, UPDATE zmiana danych, DELETE usunięcie danych z bazy. Rozkaz INSERT Rozkaz insert dodaje nowe wiersze

Bardziej szczegółowo

Plan. Formularz i jego typy. Tworzenie formularza. Co to jest formularz? Typy formularzy Tworzenie prostego formularza Budowa prostego formularza

Plan. Formularz i jego typy. Tworzenie formularza. Co to jest formularz? Typy formularzy Tworzenie prostego formularza Budowa prostego formularza 4 Budowa prostych formularzy, stany sesji, tworzenie przycisków Plan Co to jest formularz? Typy formularzy Tworzenie prostego formularza Budowa prostego formularza 2 Formularz i jego typy Tworzenie formularza

Bardziej szczegółowo

STROJENIE BAZ DANYCH: INDEKSY. Cezary Ołtuszyk coltuszyk.wordpress.com

STROJENIE BAZ DANYCH: INDEKSY. Cezary Ołtuszyk coltuszyk.wordpress.com STROJENIE BAZ DANYCH: INDEKSY Cezary Ołtuszyk coltuszyk.wordpress.com Plan spotkania I. Wprowadzenie do strojenia baz danych II. III. IV. Mierzenie wydajności Jak SQL Server przechowuje i czyta dane? Budowa

Bardziej szczegółowo

Konstruowanie Baz Danych SQL UNION, INTERSECT, EXCEPT

Konstruowanie Baz Danych SQL UNION, INTERSECT, EXCEPT Studia podyplomowe Inżynieria oprogramowania współfinansowane przez Unię Europejska w ramach Europejskiego Funduszu Społecznego Projekt Studia podyplomowe z zakresu wytwarzania oprogramowania oraz zarządzania

Bardziej szczegółowo

SBQL. język zapytań dla obiektowych baz danych. Kamil Adamczyk. Uniwersytet Warszawski 20.IV.2009

SBQL. język zapytań dla obiektowych baz danych. Kamil Adamczyk. Uniwersytet Warszawski 20.IV.2009 SBQL język zapytań dla obiektowych baz danych Kamil Adamczyk Uniwersytet Warszawski 20.IV.2009 Spis treści 1. Wstęp 2. Obiektowe bazy danych Model danych Języki zapytań Dostępne produkty 3. Sbql Główne

Bardziej szczegółowo

Maciej Piotr Jankowski

Maciej Piotr Jankowski Reduced Adder Graph Implementacja algorytmu RAG Maciej Piotr Jankowski 2005.12.22 Maciej Piotr Jankowski 1 Plan prezentacji 1. Wstęp 2. Implementacja 3. Usprawnienia optymalizacyjne 3.1. Tablica ekspansji

Bardziej szczegółowo

Plan. Wprowadzenie. Co to jest APEX? Wprowadzenie. Administracja obszarem roboczym

Plan. Wprowadzenie. Co to jest APEX? Wprowadzenie. Administracja obszarem roboczym 1 Wprowadzenie do środowiska Oracle APEX, obszary robocze, użytkownicy Wprowadzenie Plan Administracja obszarem roboczym 2 Wprowadzenie Co to jest APEX? Co to jest APEX? Architektura Środowisko Oracle

Bardziej szczegółowo

Optymalizacja poleceń SQL Wprowadzenie

Optymalizacja poleceń SQL Wprowadzenie Optymalizacja poleceń SQL Wprowadzenie 1 Fazy przetwarzania polecenia SQL 2 Faza parsingu (1) Krok 1. Test składniowy weryfikacja poprawności składniowej polecenia SQL. Krok 2. Test semantyczny m.in. weryfikacja

Bardziej szczegółowo

SQL - Structured Query Language -strukturalny język zapytań SQL SQL SQL SQL

SQL - Structured Query Language -strukturalny język zapytań SQL SQL SQL SQL Wprowadzenie do SQL SQL - Structured Query Language -strukturalny język zapytań Światowy standard przeznaczony do definiowania, operowania i sterowania danymi w relacyjnych bazach danych Powstał w firmie

Bardziej szczegółowo

SQL Server i T-SQL w mgnieniu oka : opanuj język zapytań w 10 minut dziennie / Ben Forta. Gliwice, Spis treści

SQL Server i T-SQL w mgnieniu oka : opanuj język zapytań w 10 minut dziennie / Ben Forta. Gliwice, Spis treści SQL Server i T-SQL w mgnieniu oka : opanuj język zapytań w 10 minut dziennie / Ben Forta. Gliwice, 2017 Spis treści O autorze 9 Wprowadzenie 11 Lekcja 1. Zrozumieć SQL 15 Podstawy baz danych 15 Język SQL

Bardziej szczegółowo

Technologie baz danych

Technologie baz danych Plan wykładu Technologie baz danych Wykład 6: Algebra relacji. SQL - cd Algebra relacji operacje teoriomnogościowe rzutowanie selekcja przemianowanie Małgorzata Krętowska Wydział Informatyki Politechnika

Bardziej szczegółowo

ZASADY PROGRAMOWANIA KOMPUTERÓW

ZASADY PROGRAMOWANIA KOMPUTERÓW POLITECHNIKA WARSZAWSKA Instytut Automatyki i i Robotyki ZASADY PROGRAMOWANIA KOMPUTERÓW Język Język programowania: C/C++ Środowisko programistyczne: C++Builder 6 Wykład 9.. Wskaźniki i i zmienne dynamiczne.

Bardziej szczegółowo

Przykłady najlepiej wykonywać od razu na bazie i eksperymentować z nimi.

Przykłady najlepiej wykonywać od razu na bazie i eksperymentować z nimi. Marek Robak Wprowadzenie do języka SQL na przykładzie baz SQLite Przykłady najlepiej wykonywać od razu na bazie i eksperymentować z nimi. Tworzenie tabeli Pierwsza tabela W relacyjnych bazach danych jedna

Bardziej szczegółowo

BAZY DANYCH LABORATORIUM. Studia niestacjonarne I stopnia

BAZY DANYCH LABORATORIUM. Studia niestacjonarne I stopnia BAZY DANYCH LABORATORIUM Studia niestacjonarne I stopnia Gdańsk, 2011 1. Cel zajęć Celem zajęć laboratoryjnych jest wyrobienie praktycznej umiejętności tworzenia modelu logicznego danych a nastepnie implementacji

Bardziej szczegółowo

Relacyjne bazy danych a XML

Relacyjne bazy danych a XML Relacyjne bazy danych a XML Anna Pankowska aniap@amu.edu.pl Internet, SQLiXMLwbiznesie Internet nieoceniony sposób komunikacji z klientami, pracownikami i partnerami handlowymi przyspiesza transakcje finansowe

Bardziej szczegółowo

Model relacyjny. Wykład II

Model relacyjny. Wykład II Model relacyjny został zaproponowany do strukturyzacji danych przez brytyjskiego matematyka Edgarda Franka Codda w 1970 r. Baza danych według definicji Codda to zbiór zmieniających się w czasie relacji

Bardziej szczegółowo

Instrukcja podwaja zarobki osób, których imiona zaczynają się P i dalsze litery alfabetu zakładamy, że takich osbób jest kilkanaście.

Instrukcja podwaja zarobki osób, których imiona zaczynają się P i dalsze litery alfabetu zakładamy, że takich osbób jest kilkanaście. Rodzaje triggerów Triggery DML na tabelach INSERT, UPDATE, DELETE Triggery na widokach INSTEAD OF Triggery DDL CREATE, ALTER, DROP Triggery na bazie danych SERVERERROR, LOGON, LOGOFF, STARTUP, SHUTDOWN

Bardziej szczegółowo

Informatyka I. Programowanie aplikacji bazodanowych w języku Java. Standard JDBC.

Informatyka I. Programowanie aplikacji bazodanowych w języku Java. Standard JDBC. Informatyka I Programowanie aplikacji bazodanowych w języku Java. Standard JDBC. dr hab. inż. Andrzej Czerepicki Politechnika Warszawska Wydział Transportu 2019 Standard JDBC Java DataBase Connectivity

Bardziej szczegółowo

Bazy danych. Wykład IV SQL - wprowadzenie. Copyrights by Arkadiusz Rzucidło 1

Bazy danych. Wykład IV SQL - wprowadzenie. Copyrights by Arkadiusz Rzucidło 1 Bazy danych Wykład IV SQL - wprowadzenie Copyrights by Arkadiusz Rzucidło 1 Czym jest SQL Język zapytań deklaratywny dostęp do danych Składnia łatwa i naturalna Standardowe narzędzie dostępu do wielu różnych

Bardziej szczegółowo

Temat: Ułatwienia wynikające z zastosowania Frameworku CakePHP podczas budowania stron internetowych

Temat: Ułatwienia wynikające z zastosowania Frameworku CakePHP podczas budowania stron internetowych PAŃSTWOWA WYŻSZA SZKOŁA ZAWODOWA W ELBLĄGU INSTYTUT INFORMATYKI STOSOWANEJ Sprawozdanie z Seminarium Dyplomowego Temat: Ułatwienia wynikające z zastosowania Frameworku CakePHP podczas budowania stron internetowych

Bardziej szczegółowo

REFERAT PRACY DYPLOMOWEJ

REFERAT PRACY DYPLOMOWEJ REFERAT PRACY DYPLOMOWEJ Temat pracy: Projekt i implementacja środowiska do automatyzacji przeprowadzania testów aplikacji internetowych w oparciu o metodykę Behavior Driven Development. Autor: Stepowany

Bardziej szczegółowo

Wstęp 5 Rozdział 1. Podstawy relacyjnych baz danych 9

Wstęp 5 Rozdział 1. Podstawy relacyjnych baz danych 9 Wstęp 5 Rozdział 1. Podstawy relacyjnych baz danych 9 Tabele 9 Klucze 10 Relacje 11 Podstawowe zasady projektowania tabel 16 Rozdział 2. Praca z tabelami 25 Typy danych 25 Tworzenie tabel 29 Atrybuty kolumn

Bardziej szczegółowo

Część I Tworzenie baz danych SQL Server na potrzeby przechowywania danych

Część I Tworzenie baz danych SQL Server na potrzeby przechowywania danych Spis treści Wprowadzenie... ix Organizacja ksiąŝki... ix Od czego zacząć?... x Konwencje przyjęte w ksiąŝce... x Wymagania systemowe... xi Przykłady kodu... xii Konfiguracja SQL Server 2005 Express Edition...

Bardziej szczegółowo

Bazy danych. Plan wykładu. Diagramy ER. Podstawy modeli relacyjnych. Podstawy modeli relacyjnych. Podstawy modeli relacyjnych

Bazy danych. Plan wykładu. Diagramy ER. Podstawy modeli relacyjnych. Podstawy modeli relacyjnych. Podstawy modeli relacyjnych Plan wykładu Bazy danych Wykład 9: Przechodzenie od diagramów E/R do modelu relacyjnego. Definiowanie perspektyw. Diagramy E/R - powtórzenie Relacyjne bazy danych Od diagramów E/R do relacji SQL - perspektywy

Bardziej szczegółowo

Cel przedmiotu. Wymagania wstępne w zakresie wiedzy, umiejętności i innych kompetencji 1 Język angielski 2 Inżynieria oprogramowania

Cel przedmiotu. Wymagania wstępne w zakresie wiedzy, umiejętności i innych kompetencji 1 Język angielski 2 Inżynieria oprogramowania Przedmiot: Bazy danych Rok: III Semestr: V Rodzaj zajęć i liczba godzin: Studia stacjonarne Studia niestacjonarne Wykład 30 21 Ćwiczenia Laboratorium 30 21 Projekt Liczba punktów ECTS: 4 C1 C2 C3 Cel przedmiotu

Bardziej szczegółowo

Monitoring procesów z wykorzystaniem systemu ADONIS. Krok po kroku

Monitoring procesów z wykorzystaniem systemu ADONIS. Krok po kroku z wykorzystaniem systemu ADONIS Krok po kroku BOC Information Technologies Consulting Sp. z o.o. e-mail: boc@boc-pl.com Tel.: (+48 22) 628 00 15, 696 69 26 Fax: (+48 22) 621 66 88 BOC Management Office

Bardziej szczegółowo

Problemy optymalizacji, rozbudowy i integracji systemu Edu wspomagającego e-nauczanie i e-uczenie się w PJWSTK

Problemy optymalizacji, rozbudowy i integracji systemu Edu wspomagającego e-nauczanie i e-uczenie się w PJWSTK Problemy optymalizacji, rozbudowy i integracji systemu Edu wspomagającego e-nauczanie i e-uczenie się w PJWSTK Paweł Lenkiewicz Polsko Japońska Wyższa Szkoła Technik Komputerowych Plan prezentacji PJWSTK

Bardziej szczegółowo

Technologia informacyjna

Technologia informacyjna Technologia informacyjna Bazy danych Dr inż. Andrzej Czerepicki Politechnika Warszawska Wydział Transportu 2016 Plan wykładu Wstęp do baz danych Modele baz danych Relacyjne bazy danych Język SQL Rodzaje

Bardziej szczegółowo

Usługi analityczne budowa kostki analitycznej Część pierwsza.

Usługi analityczne budowa kostki analitycznej Część pierwsza. Usługi analityczne budowa kostki analitycznej Część pierwsza. Wprowadzenie W wielu dziedzinach działalności człowieka analiza zebranych danych jest jednym z najważniejszych mechanizmów podejmowania decyzji.

Bardziej szczegółowo

Języki i Środowiska Programowania Baz Danych 2010/2011 PJWSTK, 2010

Języki i Środowiska Programowania Baz Danych 2010/2011 PJWSTK, 2010 JPS Języki i Środowiska Programowania Baz Danych 2010/2011 PJWSTK, 2010 Emil Wcisło Wykład i ćwiczenia z JPS są poświęcone obiektowym bazom danych. Studenci poznają podstawowe założenia podejścia stosowego

Bardziej szczegółowo

Język SQL, zajęcia nr 1

Język SQL, zajęcia nr 1 Język SQL, zajęcia nr 1 SQL - Structured Query Language Strukturalny język zapytań Login: student Hasło: stmeil14 Baza danych: st https://194.29.155.15/phpmyadmin/index.php Andrzej Grzebielec Najpopularniejsze

Bardziej szczegółowo

Blaski i cienie wyzwalaczy w relacyjnych bazach danych. Mgr inż. Andrzej Ptasznik

Blaski i cienie wyzwalaczy w relacyjnych bazach danych. Mgr inż. Andrzej Ptasznik Blaski i cienie wyzwalaczy w relacyjnych bazach danych. Mgr inż. Andrzej Ptasznik Technologia Przykłady praktycznych zastosowań wyzwalaczy będą omawiane na bazie systemu MS SQL Server 2005 Wprowadzenie

Bardziej szczegółowo

Baza danych. Modele danych

Baza danych. Modele danych Rola baz danych Systemy informatyczne stosowane w obsłudze działalności gospodarczej pełnią funkcję polegającą na gromadzeniu i przetwarzaniu danych. Typowe operacje wykonywane na danych w systemach ewidencyjno-sprawozdawczych

Bardziej szczegółowo

Języki i środowiska przetwarzania danych rozproszonych

Języki i środowiska przetwarzania danych rozproszonych Języki i środowiska przetwarzania danych rozproszonych Wprowadzenie do przetwarzania kolekcji w językach programowania Wykładowca: Tomasz Kowalski Wykład przygotowany na podstawie materiałów prof. Kazimierza

Bardziej szczegółowo

Definicja bazy danych TECHNOLOGIE BAZ DANYCH. System zarządzania bazą danych (SZBD) Oczekiwania wobec SZBD. Oczekiwania wobec SZBD c.d.

Definicja bazy danych TECHNOLOGIE BAZ DANYCH. System zarządzania bazą danych (SZBD) Oczekiwania wobec SZBD. Oczekiwania wobec SZBD c.d. TECHNOLOGIE BAZ DANYCH WYKŁAD 1 Wprowadzenie do baz danych. Normalizacja. (Wybrane materiały) Dr inż. E. Busłowska Definicja bazy danych Uporządkowany zbiór informacji, posiadający własną strukturę i wartość.

Bardziej szczegółowo

Relacyjne bazy danych. Podstawy SQL

Relacyjne bazy danych. Podstawy SQL Relacyjne bazy danych Podstawy SQL Język SQL SQL (Structured Query Language) język umożliwiający dostęp i przetwarzanie danych w bazie danych na poziomie obiektów modelu relacyjnego tj. tabel i perspektyw.

Bardziej szczegółowo

INFORMATYKA Pytania ogólne na egzamin dyplomowy

INFORMATYKA Pytania ogólne na egzamin dyplomowy INFORMATYKA Pytania ogólne na egzamin dyplomowy 1. Wyjaśnić pojęcia problem, algorytm. 2. Podać definicję złożoności czasowej. 3. Podać definicję złożoności pamięciowej. 4. Typy danych w języku C. 5. Instrukcja

Bardziej szczegółowo

Optymalizacja zapytań. Proces przetwarzania i obliczania wyniku zapytania (wyrażenia algebry relacji) w SZBD

Optymalizacja zapytań. Proces przetwarzania i obliczania wyniku zapytania (wyrażenia algebry relacji) w SZBD Optymalizacja zapytań Proces przetwarzania i obliczania wyniku zapytania (wyrażenia algebry relacji) w SZBD Elementy optymalizacji Analiza zapytania i przekształcenie go do lepszej postaci. Oszacowanie

Bardziej szczegółowo

Projektowanie architektury systemu rozproszonego. Jarosław Kuchta Projektowanie Aplikacji Internetowych

Projektowanie architektury systemu rozproszonego. Jarosław Kuchta Projektowanie Aplikacji Internetowych Projektowanie architektury systemu rozproszonego Jarosław Kuchta Zagadnienia Typy architektury systemu Rozproszone przetwarzanie obiektowe Problemy globalizacji Problemy ochrony Projektowanie architektury

Bardziej szczegółowo

Podstawowe pakiety komputerowe wykorzystywane w zarządzaniu przedsiębiorstwem. dr Jakub Boratyński. pok. A38

Podstawowe pakiety komputerowe wykorzystywane w zarządzaniu przedsiębiorstwem. dr Jakub Boratyński. pok. A38 Podstawowe pakiety komputerowe wykorzystywane w zarządzaniu przedsiębiorstwem zajęcia 1 dr Jakub Boratyński pok. A38 Program zajęć Bazy danych jako podstawowy element systemów informatycznych wykorzystywanych

Bardziej szczegółowo

Opisy efektów kształcenia dla modułu

Opisy efektów kształcenia dla modułu Karta modułu - Bazy Danych II 1 / 5 Nazwa modułu: Bazy Danych II Rocznik: 2012/2013 Kod: BIT-2-105-s Punkty ECTS: 4 Wydział: Geologii, Geofizyki i Ochrony Środowiska Poziom studiów: Studia II stopnia Specjalność:

Bardziej szczegółowo

Czym jest Java? Rozumiana jako środowisko do uruchamiania programów Platforma software owa

Czym jest Java? Rozumiana jako środowisko do uruchamiania programów Platforma software owa 1 Java Wprowadzenie 2 Czym jest Java? Język programowania prosty zorientowany obiektowo rozproszony interpretowany wydajny Platforma bezpieczny wielowątkowy przenaszalny dynamiczny Rozumiana jako środowisko

Bardziej szczegółowo

Baza danych sql. 1. Wprowadzenie. 2. Repozytaria generyczne

Baza danych sql. 1. Wprowadzenie. 2. Repozytaria generyczne Baza danych sql 1. Wprowadzenie Do tej pory operowaliście na listach. W tej instrukcji pokazane zostanie jak stworzyć bazę danych. W zadaniu skorzystamy z możliwości utworzenia struktury bazy danych z

Bardziej szczegółowo

Bazy danych. Plan wykładu. Rozproszona baza danych. Fragmetaryzacja. Cechy bazy rozproszonej. Replikacje (zalety) Wykład 15: Rozproszone bazy danych

Bazy danych. Plan wykładu. Rozproszona baza danych. Fragmetaryzacja. Cechy bazy rozproszonej. Replikacje (zalety) Wykład 15: Rozproszone bazy danych Plan wykładu Bazy danych Cechy rozproszonej bazy danych Implementacja rozproszonej bazy Wykład 15: Rozproszone bazy danych Małgorzata Krętowska, Agnieszka Oniśko Wydział Informatyki PB Bazy danych (studia

Bardziej szczegółowo

K1A_W11, K1A_W18. Egzamin. wykonanie ćwiczenia lab., sprawdzian po zakończeniu ćwiczeń, egzamin, K1A_W11, K1A_W18 KARTA PRZEDMIOTU

K1A_W11, K1A_W18. Egzamin. wykonanie ćwiczenia lab., sprawdzian po zakończeniu ćwiczeń, egzamin, K1A_W11, K1A_W18 KARTA PRZEDMIOTU (pieczęć wydziału) KARTA PRZEDMIOTU 1. Nazwa przedmiotu: BAZY DANYCH 2. Kod przedmiotu: 3. Karta przedmiotu ważna od roku akademickiego: 2014/2015 4. Forma kształcenia: studia pierwszego stopnia 5. Forma

Bardziej szczegółowo

Specjalizacja magisterska Bazy danych

Specjalizacja magisterska Bazy danych Specjalizacja magisterska Bazy danych Strona Katedry http://bd.pjwstk.edu.pl/katedra/ Prezentacja dostępna pod adresem: http://www.bd.pjwstk.edu.pl/bazydanych.pdf Wymagania wstępne Znajomość podstaw języka

Bardziej szczegółowo

Pytania SO Oprogramowanie Biurowe. Pytania: Egzamin Zawodowy

Pytania SO Oprogramowanie Biurowe. Pytania: Egzamin Zawodowy Pytania SO Oprogramowanie Biurowe Pytania: Egzamin Zawodowy Pytania SO Oprogramowanie Biurowe (1) Gdzie w edytorze tekstu wprowadza się informację lub ciąg znaków, który ma pojawić się na wszystkich stronach

Bardziej szczegółowo

Optymalizacja poleceń SQL Metody dostępu do danych

Optymalizacja poleceń SQL Metody dostępu do danych Optymalizacja poleceń SQL Metody dostępu do danych 1 Metody dostępu do danych Określają, w jaki sposób dane polecenia SQL są odczytywane z miejsca ich fizycznej lokalizacji. Dostęp do tabeli: pełne przeglądnięcie,

Bardziej szczegółowo

Słowem wstępu. Część rodziny języków XSL. Standard: W3C XSLT razem XPath 1.0 XSLT Trwają prace nad XSLT 3.0

Słowem wstępu. Część rodziny języków XSL. Standard: W3C XSLT razem XPath 1.0 XSLT Trwają prace nad XSLT 3.0 Słowem wstępu Część rodziny języków XSL Standard: W3C XSLT 1.0-1999 razem XPath 1.0 XSLT 2.0-2007 Trwają prace nad XSLT 3.0 Problem Zakładane przez XML usunięcie danych dotyczących prezentacji pociąga

Bardziej szczegółowo

Oracle PL/SQL. Paweł Rajba.

Oracle PL/SQL. Paweł Rajba. Paweł Rajba pawel@ii.uni.wroc.pl http://www.kursy24.eu/ Zawartość modułu 2 Kusory Wprowadzenie Kursory użytkownika Kursory domyślne Zmienne kursora Wyrażenia kursora - 2 - Wprowadzenie Co to jest kursor?

Bardziej szczegółowo

Hurtownie danych i business intelligence - wykład II. Zagadnienia do omówienia. Miejsce i rola HD w firmie

Hurtownie danych i business intelligence - wykład II. Zagadnienia do omówienia. Miejsce i rola HD w firmie Hurtownie danych i business intelligence - wykład II Paweł Skrobanek, C-3 pok. 321 pawel.skrobanek@pwr.wroc.pl oprac. Wrocław 2005-2008 Zagadnienia do omówienia 1. 2. Przegląd architektury HD 3. Warsztaty

Bardziej szczegółowo

ZMODYFIKOWANY Szczegółowy opis przedmiotu zamówienia

ZMODYFIKOWANY Szczegółowy opis przedmiotu zamówienia ZP/ITS/11/2012 Załącznik nr 1a do SIWZ ZMODYFIKOWANY Szczegółowy opis przedmiotu zamówienia Przedmiotem zamówienia jest: Przygotowanie zajęć dydaktycznych w postaci kursów e-learningowych przeznaczonych

Bardziej szczegółowo

Wykład 6. SQL praca z tabelami 3

Wykład 6. SQL praca z tabelami 3 Wykład 6 SQL praca z tabelami 3 Łączenie wyników zapytań Język SQL zawiera mechanizmy pozwalające na łączenie wyników kilku pytań. Pozwalają na to instrukcje UNION, INTERSECT, EXCEPT o postaci: zapytanie1

Bardziej szczegółowo

Projektowanie i implementacja wysokowydajnych aplikacji w języku

Projektowanie i implementacja wysokowydajnych aplikacji w języku Program szkolenia: Projektowanie i implementacja wysokowydajnych aplikacji w języku PHP Informacje: Nazwa: Kod: Kategoria: Grupa docelowa: Czas trwania: Forma: Projektowanie i implementacja wysokowydajnych

Bardziej szczegółowo

Tworzenie aplikacji bazodanowych w delphi dla dużych baz danych FRAMEWORK IMPET

Tworzenie aplikacji bazodanowych w delphi dla dużych baz danych FRAMEWORK IMPET Tworzenie aplikacji bazodanowych w delphi dla dużych baz danych FRAMEWORK IMPET Maciej Szymczak, maj 2001 soft@home.pl Ostatnia aktualizacja: 2012-03-31 1 Plan widoczny podczas całego wykładu Numer slajdu

Bardziej szczegółowo

T-SQL dla każdego / Alison Balter. Gliwice, cop Spis treści. O autorce 11. Dedykacja 12. Podziękowania 12. Wstęp 15

T-SQL dla każdego / Alison Balter. Gliwice, cop Spis treści. O autorce 11. Dedykacja 12. Podziękowania 12. Wstęp 15 T-SQL dla każdego / Alison Balter. Gliwice, cop. 2016 Spis treści O autorce 11 Dedykacja 12 Podziękowania 12 Wstęp 15 Godzina 1. Bazy danych podstawowe informacje 17 Czym jest baza danych? 17 Czym jest

Bardziej szczegółowo

Fizyczna struktura bazy danych w SQL Serwerze

Fizyczna struktura bazy danych w SQL Serwerze Sposób przechowywania danych na dysku twardym komputera ma zasadnicze znaczenie dla wydajności całej bazy i jest powodem tworzenia między innymi indeksów. Fizyczna struktura bazy danych w SQL Serwerze

Bardziej szczegółowo

Oracle PL/SQL. Paweł Rajba. pawel@ii.uni.wroc.pl http://www.kursy24.eu/

Oracle PL/SQL. Paweł Rajba. pawel@ii.uni.wroc.pl http://www.kursy24.eu/ Paweł Rajba pawel@ii.uni.wroc.pl http://www.kursy24.eu/ Zawartość modułu 6 Wprowadzenie Definiowanie wyzwalaczy DML Metadane wyzwalaczy Inne zagadnienia, tabele mutujące Wyzwalacze INSTEAD OF Wyzwalacze

Bardziej szczegółowo

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1 Podstawy programowania. Wykład Funkcje Krzysztof Banaś Podstawy programowania 1 Programowanie proceduralne Pojęcie procedury (funkcji) programowanie proceduralne realizacja określonego zadania specyfikacja

Bardziej szczegółowo

Zdalne monitorowanie i zarządzanie urządzeniami sieciowymi

Zdalne monitorowanie i zarządzanie urządzeniami sieciowymi Uniwersytet Mikołaja Kopernika w Toruniu Wydział Matematyki i Informatyki Wydział Fizyki, Astronomii i Infomatyki Stosowanej Piotr Benetkiewicz Nr albumu: 168455 Praca magisterska na kierunku Informatyka

Bardziej szczegółowo

Informacje wstępne Autor Zofia Kruczkiewicz Wzorce oprogramowania 4

Informacje wstępne Autor Zofia Kruczkiewicz Wzorce oprogramowania 4 Utrwalanie danych zastosowanie obiektowego modelu danych warstwy biznesowej do generowania schematu relacyjnej bazy danych Informacje wstępne Autor Zofia Kruczkiewicz Wzorce oprogramowania 4 1. Relacyjne

Bardziej szczegółowo

Bazy danych. Plan wykładu. Przetwarzanie zapytań. Etapy przetwarzania zapytania. Translacja zapytań języka SQL do postaci wyrażeń algebry relacji

Bazy danych. Plan wykładu. Przetwarzanie zapytań. Etapy przetwarzania zapytania. Translacja zapytań języka SQL do postaci wyrażeń algebry relacji Plan wykładu Bazy danych Wykład 12: Optymalizacja zapytań. Język DDL, DML (cd) Etapy przetwarzania zapytania Implementacja wyrażeń algebry relacji Reguły heurystyczne optymalizacji zapytań Kosztowa optymalizacja

Bardziej szczegółowo

Fazy przetwarzania zapytania zapytanie SQL. Optymalizacja zapytań. Klasyfikacja technik optymalizacji zapytań. Proces optymalizacji zapytań.

Fazy przetwarzania zapytania zapytanie SQL. Optymalizacja zapytań. Klasyfikacja technik optymalizacji zapytań. Proces optymalizacji zapytań. 1 Fazy przetwarzania zapytanie SQL 2 Optymalizacja zapytań część I dekompozycja optymalizacja generacja kodu wyraŝenie algebry relacji plan wykonania kod katalog systemowy statystyki bazy danych wykonanie

Bardziej szczegółowo