We współczesnych bazach danych są rozpowszechnione dwa podstawowe modele danych:
Dominuje relacyjny model danych, w którym organizacja danych opiera się na pojęciu zbioru.
Ostatnio dużą popularność zdobywa obiektowy model danych, będący w pewnym sensie mocno rozszerzoną wersją dawnego sieciowego modelu danych. Swoją popularność zawdzięcza dobremu dopasowaniu do obiektowych języków programowania.
Na czym polega w uproszczeniu programowanie obiektowe? Podstawowym pojęciem jest obiekt, traktowany jako kontener zawierający pewien zbiór wartości. Z obiektem związuje się zbiór specyficznych operacji do obserwacji i zmiany stanu obiektu (czyli wartości w nim zawartych).
Musi istnieć możliwość odwołania się do obiektu, aby wykonać którąś jego operację, dlatego obiekty są nazwane (niekoniecznie bezpośrednio).
Obiekty mogą się do siebie odwoływać.
obiekty elementarne: odwołują się tylko do swoich klas
obiekty złożone: odwołują się także do innych obiektów (bezpośrednio zależą od nich)
Obiekt może jednak przestać istnieć, może się więc zdarzyć niepowodzenie podczas odwoływania się do niego.
Często zachodzi potrzeba dłuższego przechowania niektórych obiektów (np. między sesjami)
Takie obiekty nazywamy trwałymi (ang. persistent)
Obiekty trwałe można zachować na dysku w repozytorium obiektów
Program korzysta z repozytorium za pośrednictwem pamięci buforowej (cache, obiekty ładuje się na żądanie.
Jeśli dany obiekt ma być trwały, to wszystkie obiekty do których się odwołuje też muszą być trwałe.
Jako nazwy takiego obiektu (niekoniecznie jedynej) używa się PID (persistent object identifier)
Repozytoria obiektów trwałych nie są dzielone, jeśli dodamy współbieżny dostęp otrzymamy prawdziwą obiektową bazę danych.
Podstawowe składowe:
relacje odpowiadające zarówno typom encji (entity), jak i typom związków (relationship);
wiersze (krotki) reprezentujące egzemplarze (wystąpienia) encji i związków;
kolumny reprezentujące atrybuty;
zbiór wartości, które można umieszczać w danej kolumnie, nazywa się jej dziedziną. Relacje są związane tylko wtedy, gdy mają kolumny o wspólnej dziedzinie.
Operacje na danych opisuje się:
algebrą relacji;
rachunkiem relacji (specjalizacja rachunku predykatów pierwszego rzędu).
W praktyce używa się języka SQL łączącego obie te metody.
Zorientowany na rekordy (dziedzictwo implementacyjne).
Konieczność wcześniejszego określenia ,,schematu” bazy danych, dopiero potem można coś do bazy wstawiać.
Nie można wstawiać danych nie pasujących do schematu, oficjalnie nie wolno dodawać nowych atrybutów.
Za słaby do reprezentacji wiedzy: tylko jedna konstrukcja (tabela), atomowe atrybuty.
Dopuszcza atrybuty o wartościach zbiorowych.
Wartości zbiorowe reprezentuje się osobnymi, wewnętrznymi tabelami.
Jedno podstawowe pojęcie do modelowania świata — obiekt.
Z obiektem związany jest jego stan i zachowanie.
Stan definiuje się wartościami własności (atrybutów) obiektu, mogą one być
wartościami elementarnymi (liczby, napisy) lub
obiektami, zawierającymi z kolei inne własności.
Tak więc obiekty można definiować rekurencyjnie.
Na rynku ok. 25 produktów, np. GemStone, ONTOS, ObjectStore, ENCORE.
Nie są to jednak jeszcze pełne i uniwersalne bazy danych.
Dlatego wiele relacyjnych DBMS uzupełniono o możliwości obiektowe, np. Oracle.
Zachowanie obiektu opisuje się zbiorem metod — procedur operujących na stanie obiektu.
Każdy obiekt jest jednoznacznie identyfikowany systemowym identyfikatorem (OID).
Proste definiowanie obiektów złożonych.
Obiekty o takich samych własnościach i zachowaniu grupuje się w klasy. Obiekt może być egzemplarzem tylko jednej klasy lub wielu.
Klasy łączy się w hierarchie dziedziczenia, w których podklasa dziedziczy własności i metody nadklasy, dokładając swoje specyficzne.
Niektóre modele pozwalają na przesłanianie (overriding) — zmianę odziedziczonej własności lub metody.
Atrybuty mogą być wielowartościowe.
Eliminuje to potrzebę używania większości złączeń równościowych.
Możliwość definiowania związków odwrotnych.
Nie trzeba definiować kluczy, ich rolę pełnią OIDy.
Eliminuje to modyfikowanie wartości klucza.
Dwa rodzaje równości: identyczność i równość wartości.
Złączenia używane gdy warunki w zapytaniu dotyczą porównywania wartości atrybutów. W przypadku ,,kluczy zewnętrznych” bezpośrednia nawigacja z użyciem OID.
Przy ładowaniu powiązanych obiektów z bazy danych do pamięci (buforowanie) OIDy zastępuje się wskaźnikami (pointer swizzling), co wielokrotnie przyśpiesza nawigację.
Wersjonowanie (długie transakcje) — tylko w niektórych OBD.
Dwa tryby dostępu:
Nawigacyjny: mamy OID obiektu i zaczynając od niego przechodzimy po kolejnych referencjach. Zwykle używany w programach.
Język zapytań (często podobny do SQL): zapytanie opisuje zbiór obiektów. Deklaratywność.
Przykład: Znaleźć wszystkie projekty z budżetem ponad 100000 złp i mające sponsora z Warszawy
Relacyjnie
{p | (f)(a) Projekt(p) AND Firma(f) AND Adres(a) AND p.budżet > 100000 AND p.sponsor = f AND f.adres = a AND a.miasto = 'Warszawa'}
Z użyciem wyrażeń ścieżkowych (co pozwala unikać zmiennych ,,roboczych”)
{p | Projekt(p) AND p.budżet > 100000 AND p.sponsor.adres.miasto = 'Warszawa'}
Potrzebne np. w systemach CAD do eksploracji wariantów.
Każda wersja ma własny OID, ale zachowujemy związki wyprowadzenia między wersjami, najczęściej tworzące hierarchię.
Hierarchia wersji zwykle trzymana w specjalnym obiekcie generycznym.
Dwa rodzaje odwołań do wersji
specyficzna (albo statyczna): konkretna wersja
generyczna (albo dynamiczna): do domyślnej wersji (zwykle ostatniej).
Brak optymalizacji zapytań. Główny problem to wyrażenia ścieżkowe, trudno dla nich budować indeksy.
Brak dobrej formalizacji, ale są już algebry podobne do algebry relacji.
Pięć typowych operacji: suma (union, różnica (difference), selekcja (select), generowanie (generate), odwzorowanie (map).
Jednak brak powiązania tych operacji z niskopoziomowymi operacjami fizycznymi (takiego jak w algebrze relacji RBD), stąd ich arbitralność.
Brak uniwersalnych języków zapytań. Zwykle brak zagnieżdżonych podzapytań, funkcji agregujących, grupowania. Brak automatycznego wsparcia dla obsługi ekstensji klasy — zbioru jej egzemplarzy; użytkownik musi sam zdefiniować kolekcję (collection) i pilnować jej aktualizacji przy dodawaniu i usuwaniu obiektów.
Kompozycyjność
W modelu relacyjnym wynik zapytania jest (anonimową) relacją.
Można go więc obrobić kolejnym zapytaniem, co umożliwia składanie zapytań.
W modelu obiektowym jest gorzej, obiekty ze zbioru wynikowego mogą nie należeć do żadnej klasy.
Brak wsparcia dla redefiniowania klas (odpowiednik ALTER z SQL), np. dodawania lub usuwania atrybutów.
Brak perspektyw (ale może są zbędne).
Brak sensownego mechanizmu definiowania uprawnień, nie wiadomo jaka ziarnistość: obiekt, zbiór, klasa, fragment hierarchii?
Bardzo ograniczone więzy spójności, konieczność realizacji metodami.
Kłopoty z współbieżnością, ręczne operowanie blokadami.
Problem długich transakcji (unika się ich w systemach OLTP dla relacyjnych baz danych).
Propozycja: wspólna publiczna baza danych + prywatne bazy danych użytkowników.
Definiowanie klasy wygląda w O następująco:
class Instytut type tuple : (obszar-badań : string, nazwa-instytutu : string, adres : Adres, grupa-badawcza : set(Zespół)) public read nazwa-instytutu, write obszar-badań method init(string, string, Adres, set(Zespół)) : Instytut is public;
Zamiast tuple można użyć list lub set, a nawet zagnieżdżać je w sobie.
Deklaracja metody występująca powyżej podaje jedynie jej sygnaturę.
Kolekcje muszą być tworzone ręcznie, mamy dwie możliwości.
Możemy utworzyć kolekcję jako klasę
class Instytuty type set(Instytut);
albo też jako nazwaną wartość złożoną
name instytuty: set(Instytut);
Standardowym językiem do pisania programów jest CO
execute co2 { o2 Instytut tmp; tmp = new(Instytut); instytuty = set(tmp); }
Zapytania można zadawać używając języka zapytań OQL, stanowiącego
rozszerzenie SQL:
select x from x in instytuty
where x.obszar-badań = 'Bazy danych';
Można w nim używać wyrażeń ścieżkowych:
select x from x in instytuty
where x.obszar-badań = 'Bazy danych'
and x.adres.kraj = 'Włochy';
make-class 'Instytut' superclasses: nil attributes: '((obszar-badań: domain string) (nazwa-instytutu: domain string) (adres: domain Adres) (grupa-badawcza: domain (set-of Zespół)))
Uwagi:
Można określać wartości domyślne dla atrybutów
Wielodziedziczenie
Brak złączeń, rzutowanie tylko na jeden atrybut lub wszystkie.
Powód: dzięki temu wynik zawsze należy do jakiejś klasy.
(Instytut select :I (:I obszar-badań = 'Bazy danych')) (Instytut select (:I ((:I obszar-badań = 'Bazy danych') and (:I adres kraj = 'Włochy')))
interface Instytut (extent Instytuty key nazwa) {attribute string nazwa; attribute string obszar-badań; attribute Adres adres; relationship Zespół grupa-badawcza inverse Zespół::afiliacja};
Dozwolone wyrażenia ścieżkowe
selectdistinct x from Instytuty x
where x.obszar-badań = 'Bazy danych'
and x.adres.kraj = 'Włochy';
Rodzaje OID:
logiczne: bez związku z adresem w pamięci zewnętrznej
fizyczne: na podstawie położenia
Orion: <id-klasy,id-egzemplarza>. Definicje atrybutów i metod trzymane w obiekcie reprezentującym klasę, więc potrzebny szybki dostęp do niego. Migracja obiektu z klasy do klasy trudnsa, bo zmienia OID.
GemStone: informacja o klasie w samym obiekcie, a nie w OID, wymaga wczesnego pobrania obiektu.
O używa Wiss (Wisconsin Storage Subsystem), obiekt przechowywany jako rekord Wiss, OID jest identyfikatorem rekordem (RID).
Przy zmianie strony forward reference.
Wymagane tymczasowe OID dla obiektów utworzonych w pamięci, otrzymują trwały OID dopiero przy zatwierdzaniu (commit).
Orion: w wersji rozproszonej OID dodatkowo zawierał identyfikator położenia, nie zmieniający się nawet przy migracji.
Czy klasy są obiektami? Inaczej mówiąc, czy schemat trzymany jest osobno?
Jakie są dozwolone związki między klasami?
Czy i jak jest wspierana nawigacja?
Jakie są operacje na obiektach?
Jak identyfikuje się obiekty?
Jak wybiera się obiekty?
Jaką rolę pełni klasyfikacja?
Czy klasy obiektów mogą ewoluować?
Czy w rozproszonym systemie sieć powinna być widoczna?
Czy jest specjalna obsługa obiektów aktywnych?
Czy ,,świat” obiektów jest zamknięty (closed) czy otwarty?
Sikha Bagui Achievements and Weaknesses of Object-Oriented Databases, Journal of Object Technology, vol. 2, no. 4, July-August 2003, str. 29–41, http://www.jot.fm/issues/issue\_2003\_07/column2.
Dziedziczenie to jedno z podstawowych pojęć obiektowych baz danych. PostgreSQL umożliwia nakładanie dziedziczenia na tabele.
Zobaczmy to na przykładzie. Utworzymy dwie tabele: tabelę miast i tabelę stolic. Ponieważ stolice także są miastami, chcemy uniknąc dublowania informacji. Jeden ze sposobów to
CREATE TABLE Stolice ( nazwa text, populacja real, temperatura real kraj char(3) ); CREATE TABLE Inne_miasta ( nazwa text, populacja real, temperatura real );gdzie listę wszystkich miast otrzymamy z:
CREATE VIEW miasta AS SELECT nazwa, populacja, temperatura FROM Stolice UNION SELECT nazwa, populacja, temperatura FROM Inne_miasta;
W PostgreSQL lepszym rozwiązaniem jest:
CREATE TABLE Miasta ( nazwa text, populacja real, temperatura real ); CREATE TABLE Stolice ( kraj char(3) ) INHERITS (Miasta);
Tabela Stolice dziedziczy wszystkie kolumny (nazwa, populacja, temperatura) z macierzystej tabeli Miasta. W tabeli tej występuje jednak dodatkowa kolumna kraj.
Poniższe zapytanie podaje nazwy wszystkich miast (także stolic), w których średnia temperatura roczna jest wyższa niż 10C:
SELECT nazwa, temperatura FROM Miasta WHERE temperatura > 500;natomiast innym zapytaniem możemy otrzymać listę obejmującą jedynie te z nich, które nie są stolicami:
SELECT nazwa, temperatura FROM ONLY Miasta WHERE temperatura > 500;Fraza ONLY powoduje, że zapytanie obejmujące jedynie wiersze znajdujące się bezpośrednio w tabeli Miasta, nie zaś w tabelach dziedziczących z niej. Frazy ONLY można używać w poleceniach SELECT, UPDATE i DELETE.
W PostgreSQL tabela może dziedziczyć również z kilku tabel. Struktura dziedziczenia nie może jednak zawierać cykli.
W PostgreSQL można definiować typy w sposób zbliżony do SQL3. Definicja ma postać:
CREATE TYPE typ AS OBJECT (
lista atrybutów i metod
);
Używając jej możemy zdefiniować typ dla punktów:
CREATE TYPE punkt_t AS OBJECT ( x NUMERIC, y NUMERIC );
Po zdefiniowaniu typu obiektowego używa się podobnie jak typów standardowych, np. możemy teraz zdefiniować typ dla odcinków:
CREATE TYPE odcinek_t AS OBJECT ( początek punkt_t, koniec punkt_t );
Możemy teraz utworzyć tabelę zawierającą odcinki wraz z ich identyfikatorami (ID):
CREATE TABLE Odcinki ( ID INT, odcinek odcinek_t );
Typy usuwamy poleceniem DROP:
DROP TYPE odcinek_t;Przed usunięciem typu trzeba jednak oczywiście usunąć wszystkie tabele oraz inne typy, które używają danego typu a type, więc powyższe polecenie zakończy się niepowodzeniem. Trzeba najpierw usunąć tabelę Odcinki.
Typów zdefiniowanych można również używać bezpośrednio de definiowania tabel (odpowiednik ,,rowtype” z SQL3). W poleceniu CREATE TABLE można zastąpić listę kolumn słowem kluczowym OF i nazwą typu, np.
CREATE TABLE Odcinki OF odcinek_t;
PostgreSQL dla każdego definiowanego typu tworzy automatycznie funkcję konstruktora o tej samej nazwie. Obiekt typu punkt_t tworzy się wywołując funkcję odcinek_t od (podanej w nawiasach) listy wartości atrybutów, np.
INSERT INTO Odcinki VALUES(27, odcinek_t(punkt_t(0.0, 0.0), punkt_t(3.0, 4.0)));
Dostęp do składowych obiektu uzyskuje się używając notacji kropkowej. Jeśli O jest pewnym obiektem typu T, którego jedną ze składowych (atrybutów lub metod) jest A, to O.A odnosi się do tej składowej w obiekcie O.
Możemy wyszukać współrzędne początków owszystkich odcinków
SELECT o.odcinek.początek.x, o.odcinek.początek.y FROM Odcinki o;Użycie aliasu jest w takich sytuacjach obowiązkowe. Zapytanie
SELECT o.odcinek.koniec FROM Odcinki o;wyszuka końce wszystkich odcinków (wypisując je ewentualnie jako wywołania konstruktora.
Dla typu t wyrażenie REF t oznacza jego typ referencyjny (,,ID obiektu”). Można go używać w definicjach kolumn:
CREATE TABLE Odcinki ( początek REF punkt_t, koniec REF punkt_t );
Używając takiego typu trzeba pamiętać o pewnych ograniczeniach. Konkretne referencje muszą odnosić się do wierszy tabeli podanego typu. Na przykład dla tabeli
CREATE TABLE Punkty OF punkt_t;możemy umieścić w tabeli Odcinki referencje do wierszy z tej tabeli:
INSERT INTO Odcinki SELECT REF(pp), REF(qq) FROM Points pp, Points qq WHERE pp.x < qq.x;
Referencje nie mogą natomiast odnosić się do obiektów występujących w kolumnach innych tabel, ani nie mogą być tworzone ad hoc (np. umieszczając w poleceniu INSERT klauzulę VALUES(REF(punkt_t(1,2)), REF(punkt_t(3,4)))).
Przechodzenie po referencji zapisuje się używając notacji kropkowej, np. poniższe zapytanie podaje współrzędne x początków i końców wszystkich odcinków w tabeli Odcinki.
SELECT ll.początek.x, ll.koniec.x FROM Lines2 ll;
W Postgresie jako typów kolumn można używać typów tablicowych. Wartością atrybutu może być wtedy cała tablica, tak jak dla kolumny b poniżej.
a | b | ||||||
---|---|---|---|---|---|---|---|
- |
|
||||||
- |
|
||||||
- |
|
Aby było to możliwe, należy najpierw zdefiniować relację jako typ, używając frazy AS TABLE OF, np.
CREATE TYPE wielobok_t AS TABLE OF punkt_t;definiuje typ wielobok_t jako relację, której krotki są typu punkt_t, tzn. mają dwa atrybuty x i y.
Teraz można zdefiniować relację, w której jedna z kolumn będzie reprezentowała wieloboki, tzn. zbiory punktów, np.
CREATE TABLE Wieloboki ( nazwa VARCHAR2(20), punkty wielobok_t) NESTED TABLE punkty STORE AS TabPunkty;
Relacje odpowiadające poszczególnym wielobokom nie są zapisywane bezpośrednio jako wartości atrybutu punkty, lecz trzyma się je w pojedynczej tabeli, której nazwę należy zadeklarować. Do tabeli tej nie można się odwoływać bezpośrednio.
Przy wstawianiu wierszy do relacji Wieloboki używa się konstruktora typu dla zagnieżdżonej relacji (wielobok_t). Wartość zagnieżdżonej relacji podaje się w nim jako rozdzielaną przecinkami listę wartości odpowiedniego typu (punkt_t), najczęściej także używając konstruktora.
INSERT INTO Wieloboki VALUES('kwadrat', wielobok_t(punkt_t(0.0, 0.0), punkt_t(0.0, 1.0), punkt_t(1.0, 0.0), punkt_t(1.0, 1.0)));
Wierzchołki tego kwadratu podaje poniższe zapytanie:
SELECT punkty FROM Wieloboki WHERE nazwa = 'kwadrat';
Treść automatycznie generowana z plików źródłowych LaTeXa za pomocą oprogramowania wykorzystującego LaTeXML.
strona główna | webmaster | o portalu | pomoc
© Wydział Matematyki, Informatyki i Mechaniki UW, 2009-2010. Niniejsze materiały są udostępnione bezpłatnie na licencji Creative Commons Uznanie autorstwa-Użycie niekomercyjne-Bez utworów zależnych 3.0 Polska.
Projekt współfinansowany przez Unię Europejską w ramach Europejskiego Funduszu Społecznego.
Projekt współfinansowany przez Ministerstwo Nauki i Szkolnictwa Wyższego i przez Uniwersytet Warszawski.