Αντικειμενοστρεφείς βάσεις δεδομένων

Οι αντικειμενοστρεφείς βάσεις δεδομένων είναι συστήματα βάσεων δεδομένων που κρατούν τα δεδομένα τους σαν αντικείμενα, κατά τις αρχές του αντικειμενοστρεφούς προγραμματισμού.

ODMG-93

Η επιτυχία των σχεσιακών συστημάτων διαχείρισης βάσεων δεδομένων βασίστηκε αφενός στην απλότητα του σχεσιακού μοντέλου δεδομένων και στην ανεξαρτησία των δεδομένων από τις εφαρμογές, και αφετέρου στην τυποποίηση. Η αποδοχή του SQL standard, το οποίο βασίστηκε στο μοντέλο δεδομένων και τη γλώσσα που πρότεινε η ΙΒΜ (η μεγαλύτερη εταιρεία που αναμίχθηκε από νωρίς στην ανάπτυξη τέτοιων συστημάτων), πρόσφερε μεταφερσιμότητα των δεδομένων και των εφαρμογών, γεγονός που έκανε το σχεσιακό μοντέλο δεδομένων και την SQL τόσο δημοφιλή.
Στην περίπτωση των αντικειμενοστρεφών συστημάτων δεδομένων, η κατάσταση είναι διαφορετική, καθώς η "παιδική τους ηλικία" συνοδεύτηκε από το βασικό πρόβλημα της ανυπαρξίας ενός καλά ορισμένου μοντέλου δεδομένων. Κάθε σύστημα προσέφερε το δικό του μοντέλο δεδομένων. Τα μοντέλα δεδομένων αυτά έμοιαζαν σημαντικά μεταξύ τους, όμως η ομοιότητα δεν είναι αρκετή: στο επίπεδο του μοντέλου δεδομένων χρειάζεται ταύτιση, και όχι απλή ομοιότητα στα βασικά στοιχεία. Το ίδιο συμβαίνει και στο επίπεδο των γλωσσών προγραμματισμού και των ερωτήσεων. Κάθε σύστημα, βασιζόμενο στο δικό του μοντέλο δεδομένων, προσέφερε και τη δική του γλώσσα. Αν και οι γλώσσες προγραμματισμού της βάσης δεδομένων βασίζονταν στις ίδιες κλασικές γλώσσες τρίτης γενιάς (C++, Smalltalk, κλπ.) δεν υπήρχε ταύτιση στα επιπλέον στοιχεία που χρειάζονται για τη διαχείριση των αντικειμένων μιας βάσης δεδομένων.
Με βάση τα παραπάνω, ήταν φανερή η ανάγκη για την ύπαρξη τουλάχιστον ενός κοινού παρονομαστή, που να επιτρέπει τη μεταφερσιμότητα εφαρμογών και δεδομένων. Το Object Database Management Group (ODMG) είναι μια επιτροπή που, στις αρχές της δεκαετίας του 1990, πρότεινε ένα κοινό interface (ODMG-93) στους διάφορους κατασκευαστές αντικειμενοστρεφών συστημάτων βάσεων δεδομένων, με βάση το οποίο να συγγράφονται μεταφέρσιμες εφαρμογές από το ένα σύστημα στο άλλο. Η μεταφερσιμότητα (portability) των εφαρμογών συνίσταται στη μεταφερσιμότητα του σχήματος της βάσης, του binding της γλώσσας προγραμματισμού, της γλώσσας διαχείρισης δεδομένων και της γλώσσας ερωτήσεων.
Το ODMG δεν είναι μια επίσημη επιτροπή τυποποίησης. Συμμετέχουν όμως, σ’ αυτήν, εταιρείες που κατέχουν το 80% της αγοράς των αντικειμενοστρεφών συστημάτων βάσεων δεδομένων. Επιπλέον, οι εταιρείες αυτές έχουν δεσμευτεί να παρέχουν το κοινό αυτό interface από τις αρχές του 1995. Κομμάτια του standard αποτελούν στοιχεία ήδη υλοποιημένα σε κάποια από τα υπάρχοντα αντικειμενοστρεφή συστήματα βάσεων δεδομένων (π.χ. η γλώσσα ερωτήσεων βασίζεται στη γλώσσα ερωτήσεων του Ο2).
Το 1993, τα επτά μέλη του ODMG με δικαίωμα ψήφου ήταν οι εταιρείες Object Design (ObjectStore), Objectivity, ONTOS, O2 Technology, POET Software, Servio Corporation (GemStone), και Versant Technology. Επιπλέον όλες οι εταιρείες που συμμετέχουν ως Reviewer Members έχουν δεσμευτεί να υλοποιήσουν το ODMG-93. Σήμερα, στα 1997, οι εταιρείες αυτές έχουν παραμείνει σχεδόν οι ίδιες (αν και πολλές έχουν αλλάξει τα ονόματά τους) και είναι οι εξής: GemStone Systems, IBEX Computing, O2 Technology, Object Design, Objectivity, POET Software, UniSQL, Versant Object Technology. Πρακτικά, αποχώρησε η Ontos και προσχώρησε η IBEX Computing (που διακινεί το Itasca) και η UniSQL. Στην παρουσίαση του ODMG-93 standard θα βασιστούμε κυρίως στο [Ca95] και κατά δεύτερο λόγο στα [Ki94], [ODMG94]. Όλα τα σχήματα και τα παραδείγματα είναι από το [Ca94] και το [Ca95].

Γλώσσα Ορισμού Αντικειμένων (Object Definition Language - ODL)

Η ODL είναι μια γλώσσα που χρησιμοποιείται για να δηλώσει διαπροσωπείες (interfaces) σε τύπους αντικειμένων που ακολουθούν το μοντέλο αντικειμένων του ODMG-93 και έχει σκοπό να παρέχει μεταφερσιμότητα των σχημάτων των βάσεων δεδομένων. Η ODL αποτελεί την DDL για τύπους αντικειμένων. Καθορίζει τα χαρακτηριστικά των τύπων και τις υπογραφές των μεθόδων. Για τη διαχείριση των στιγμιότυπων των τύπων, και την υλοποίηση των μεθόδων, υπάρχουν οι γλώσσες διαχείρισης αντικειμένων (Object Manipulation Languages - OMLs). Το ODMG-93 δεν καθορίζει κάποια συγκεκριμένη OML. Περιγράφει, όμως, δύο interfaces για τη σύνδεση των αντικειμενοστρεφών συστημάτων βάσεων δεδομένων με C++ και Smalltalk.
Δεδομένου ότι δεν είναι σαφές που θα υλοποιηθεί το σχήμα που θα καθοριστεί στην ODL, η ODL πρέπει να είναι ανεξάρτητη από την οποιαδήποτε γλώσσα προγραμματισμού. Επιπλέον, πρέπει να μπορεί να ενσωματωθεί με ομαλό τρόπο σε γλώσσες όπως η Smalltalk και η C++, πράγμα που καθορίζεται στα αντίστοιχα bindings. Ακολουθεί το παράδειγμα ενός υποτυπώδους σχήματος σε ODL (με έντονα γράμματα φαίνονται οι δεσμευμένες λέξεις της ODL):
 interface Person
 (   extent People)
 {
   attribute String name;
   attribute Struct Address { Unsigned Short number, String
       Street, String city_name} address;
   relationship Person spouse inverse Person::spouse;
   relationship Set<Person> children inverse Person::parents
       {order by birth date }
   relationship List<Person> parents inverse Person::children;
   void birth (in String name);
   Boolean marriage (in String person_name) raises
       (no_such_person);
   Unsigned Short ancestors (out Set<Person> all_ancestors)
       raises (no_such_person);
   void move (in String new_address);
 };
 interface Employee: Person
 (    extent employees
   key(name, id))
 {
   attribute Short id;
   attribute Unsigned Short annual_salary;
 };
 interface City
 (    extent cities
   key city_code)
 {
   attribute Unsigned Short city_code;
   attribute String name;
   attribute Set<Person> population;
 };
Στο παραπάνω παράδειγμα, ορίσαμε την κλάση Person που μοντελοποιεί ένα άνθρωπο. Τα χαρακτηριστικά της εν λόγω κλάσης είναι το όνομα (name), η διεύθυνση (Address) που αποτελεί μια σύνθετη τιμή, η/ο σύζυγος (spouse) που αποτελεί ένας αντίστροφος δείκτης (inverse) και οι αντίστροφοι δείκτες children και parents, που μοντελοποιούν τα παιδιά και τους γονείς αντίστοιχα. Ακόμα υπάρχουν οι συναρτήσεις birth (ημερομηνία γεννήσεως), marriage (αν κάποιος είναι παντρεμένος ή όχι), ancestors (που επιστρέφει όλους τους προγόνους κάποιου) και move (που αλλάζει τη διεύθυνση κάποιου. Η κλάση Employee κληρονομεί την κλάση Person και την εξειδικεύει προσθέτοντας ένα κωδικό (id) και ένα ετήσιο εισόδημα (annual_salary). Τέλος, η κλάση City έχει τα χαρακτηριστικά city_code (κωδικός), name (όνομα) και population, που είναι ο πληθυσμός της και είναι ένα σύνολο από αντικείμενα της κλάσης Person.

C++ Binding

Στη συνέχεια, θα παρουσιάσουμε ένα από τα χρησιμοποιούμενα bindings, το C++ binding, το οποίο είναι και το πλέον χρησιμοποιούμενο. Όταν αναφερόμαστε στο C++ binding για ODL/OML πρέπει να έχουμε υπόψη μας δύο πράγματα: το C++ binding για ODL είναι μια βιβλιοθήκη κλάσεων και μια επέκταση στη γραμματική της C++. Η βιβλιοθήκη των κλάσεων παρέχει κλάσεις για να υλοποιηθεί το μοντέλο αντικειμένων του ODMG-93. Η επέκταση του συντακτικού της γλώσσας έχει να κάνει με την υποστήριξη αναφορών (relationships) μεταξύ των κλάσεων. Η OML χρησιμοποιείται για την ανάκτηση και την επεξεργασία δεδομένων από τη βάση, και ακολουθεί το συντακτικό της C++. Οι ODL/OML δεν ασχολούνται με τη φυσική αποθήκευση των αντικειμένων, αλλά μόνο με τα λογικά χαρακτηριστικά τους. Ένα πρόσθετο σύνολο δομών, με όνομα physical pragmas, έχει καθοριστεί για να αναλάβει να δώσει στον προγραμματιστή κάποιο έλεγχο πάνω σε αυτά τα ζητήματα.
Τα bindings για τις διάφορες γλώσσες προγραμματισμού καθορίστηκαν με βάση την αρχή ότι ο προγραμματιστής πρέπει να έχει να κάνει με μόνο μία γλώσσα. Αποτελέσματα της αρχής αυτής είναι, ότι υπάρχει μόνο ένα σύστημα τύπων και για τη γλώσσα και για τη βάση δεδομένων, και ότι η ενσωμάτωση των χαρακτηριστικών που οφείλονται στο μοντέλο αντικειμένων της βάσης δεδομένων γίνεται ομαλά.
Για να δηλωθούν τα διαρκή (persistent) δεδομένα στη C++, μια κλάση πρέπει να είναι υποκλάση της κλάσης Persistent_Object. Αυτό σημαίνει, ότι η κλάση αυτή μπορεί να έχει και διαρκή (persistent) και μεταβατικά (transient) -μέσω κάποιων συναρτήσεων- αντικείμενα. Στο παρακάτω παράδειγμα, ακολουθεί η δήλωση ενός στιγμιότυπου (profP) μιας κλάσεως (Professor), το οποίο είναι διαρκές:
Ref<Professor> profP;
Στην παραπάνω εικόνα φαίνεται ο τρόπος με τον οποίο παράγεται μια εφαρμογή, χρησιμοποιώντας το C++ binding για ODL/OML. Ένας διαχειριστής της βάσης γράφει δηλώσεις για το σχήμα της, οι οποίες συγγράφονται στην C++ ODL, η οποία είναι μια επέκταση της C++. Ο προγραμματιστής εφαρμογών γράφει και ένα πρόγραμμα που υλοποιεί την εφαρμογή σε C++, η οποία επεκτείνεται για να αποτελέσει την γλώσσα προγραμματισμού μιας βάσης δεδομένων. Οι δηλώσεις περνούν από κάποιο προεπεξεργαστή (preprocessor), ο οποίος αφενός παράγει το σχήμα της βάσης και αφετέρου φτιάχνει μια περιγραφή του σχήματος στη C++. Ο κώδικας της εφαρμογής και η περιγραφή αυτή μεταφράζονται, δημιουργείται ένα εκτελέσιμο αρχείο της εφαρμογής, το οποίο αφού συνδυαστεί με τη μηχανή του αντικειμενοστρεφούς συστήματος βάσεων δεδομένων (μέσα από ένα πρόγραμμα σύνδεσης -linker) δημιουργεί την τελική εφαρμογή, η οποία και επεξεργάζεται τα δεδομένα της βάσης δεδομένων.
Όπως προαναφέρθηκε, η ODL καθορίζει το σχήμα μιας βάσης, ενώ η OML καθορίζει τη διαχείριση των αντικειμένων, των σχέσεων μεταξύ των κλάσεων, καθώς και των συλλογών (set, bag, list, array). Σε ότι αφορά τις δοσοληψίες ή συναλλαγές (transactions), είναι υποχρεωτικό, κάθε λειτουργία διαχείρισης αντικειμένων να γίνεται μέσα από μια δοσοληψία. Οι δοσοληψίες αποτελούν στιγμιότυπα της ομώνυμης κλάσης και διαθέτουν λειτουργίες commit, abort και checkpoint. Δεν υποστηρίζονται μακρές συναλλαγές, υποστηρίζονται όμως, εμφωλευμένες. Επιπλέον, η OML είναι υπεύθυνη και για τις λειτουργίες σύνδεσης και αποσύνδεσης με μία βάση.
Υπάρχει και μια απεικόνιση της OQL για C++. Οι ερωτήσεις μπορούν να δηλωθούν μέσα σε ένα πρόγραμμα μέσω μιας συναρτήσεως για ερωτήσεις, η οποία παίρνει συμβολοσειρές για ορίσματα. Τα ορίσματα αυτά πρέπει να είναι έγκυρες ερωτήσεις, οι οποίες μεταφράζονται και εκτελούνται στο run-time. Το αποτέλεσμα επιστρέφεται στην παράμετρο "result".

Διακριτοί Τύποι

Οι διακριτοί τύποι (distinct types) αποτελούν την πιο βασική έκφραση των ορισμένων από τον χρήστη τύπων. Ουσιαστικά, αποτελούν μετονομασία ενός βασικού τύπου πηγή (π.χ. DECIMAL, REAL, INTEGER). Στην συνέχεια, θα αναφερόμαστε στον τύπο βάσει του οποίου ορίζεται ένας διακριτός τύπος, ως πηγαίο τύπο (source type) ή βασικό τύπο αναφοράς του διακριτού τύπου. Ο διακριτός τύπος με τον πηγαίο τύπο έχουν την ίδια εσωτερική δομή αλλά παρουσιάζουν εν γένει διαφορετική συμπεριφορά.
Για παράδειγμα, μπορούμε να δηλώσουμε δύο διακριτούς τύπους, παράγωγα του DECIMAL, που θα αναπαριστούν το αμερικάνικο (US_DOLLAR) και το καναδικό δολάριο (CND_DOLLAR).
CREATE DISTINCT TYPE US_DOLLAR AS DECIMAL (9.2)
CREATE DISTINCT TYPE CDN_DOLLAR AS DECIMAL (9.2)
Οι διακριτοί τύποι είναι εφοδιασμένοι από το σύστημα με τελεστές σύγκρισης. Ο ορισμός των τελεστών βασίζεται στους αντιστοίχους ορισμούς του πηγαίου τύπου.
Παρόλο που οι διακριτοί τύποι παρουσιάζουν την ίδια εσωτερική δομή με τον πηγαίο τύπο, δεν είναι άμεσα συγκρίσιμοι μαζί του (strongly typed). Για να μπορέσουμε να συγκρίνουμε ένα διακριτό τύπο με τον πηγαίο του, καθώς και διακριτούς τύπους μεταξύ τους (που έχουν βέβαια κοινή πηγή) χρησιμοποιούμε συναρτήσεις μετατροπής (casting).
Με χρήση των διακριτών τύπων μπορούμε να ορίζουμε σχέσεις. Για παράδειγμα:
CREATE TABLE SALES
  (ID INTEGER, US US_DOLLAR, CDN CDN_DOLLAR)
Οι μεταβλητές US, CND βασίζονται στον ίδιο πηγαίο τύπο αλλά ανήκουν σε διαφορετικούς διακριτούς τύπους. Ως εκ τούτου η ακόλουθη έκφραση είναι λανθασμένη.
SELECT * FROM SALES WHERE CDN > US
Για να πετύχουμε την σύγκριση χρησιμοποιούμε την συνάρτηση μετατροπής CND_DOLLAR η οποία μετατρέπει το όρισμα της σε καναδικά δολάρια.
SELECT * FROM SALES WHERE CDN > CDN_DOLLAR(US)
Αντίστοιχα, θα μπορούσαμε να χρησιμοποιήσουμε την συνάρτηση μετατροπής US_DOLLAR.
Οι διακριτοί τύποι δεν υποστηρίζουν κληρονομικότητα ούτε είναι δυνατό να ορίσουμε ένα διακριτό τύπο με πηγαίο έναν άλλο διακριτό τύπο.

Αφηρημένοι τύποι δεδομένων

Έκτος από τους διακριτούς τύπους δεδομένων μπορούμε να ορίσουμε και αφηρημένους τύπους δεδομένων (Abstract Data Types, ADTs). Οι αφηρημένοι τύποι δεδομένων αποτελούν οντότητες με συμπεριφορά και ενθυλακωμένη εσωτερική δομή. Ο ορισμός της δομής ενός ATD γίνεται σε SQL. Για παράδειγμα ορίζουμε τον ακόλουθο τύπο:
CREATE TYPE address
  (street char (30),
  city char (20),
  state char (2),
  zip integer);

Ενθυλάκωση (Encapsulation)

Όπως έχουμε ήδη αναφέρει, η εσωτερική δομή των αφηρημένων τύπων είναι ενθυλακωμένη. Η πρόσβαση στα πεδία του τύπου περιορίζεται σε συναρτήσεις που ορίζονται στο ορατό μέρος του κάθε αφηρημένου τύπου. Παράλληλα, δεν υπάρχει διάκριση μεταξύ συναρτήσεων, αποθηκευμένων συναρτήσεων (stored functions) και ιδεατών πεδίων (virtual attributes).
Οι συναρτήσεις με τις οποίες μας επιστρέφεται η τιμή ενός πεδίου (observer functions) ή με τις οποίες αναθέτουμε μια τιμή σε κάποιο πεδίο (mutator functions) δημιουργούνται αυτόματα. Για παράδειγμα:
street(address) -> char(30)
city(address) -> char(20)
state(address) -> char(2)
zip(address) -> integer
street(address, char(30)) -> address
city(address, char(20)) -> address
state(address, char(2)) -> address
zip(address, integer) -> address
Η κλήση της street(address) θα επιστρέψει ένα πεδίο char(30) που αντιστοιχεί στο πεδίο street του τύπου address. Αντίστοιχα, η κλήση της συνάρτησης street(address, char(30)) θα δημιουργήσει ένα στιγμιότυπο του τύπου address με τιμή πεδίου address την τιμή του char(30).

Δημιουργία Στιγμιότυπων

Για να δημιουργήσουμε ένα νέο στιγμιότυπο χρησιμοποιούμε την κατασκευαστική συνάρτηση (constructor function). Κάθε φόρα που ορίζουμε ένα ADT δημιουργείται αυτόματα και μια κατασκευαστική συνάρτηση.
address() -> address
Με την κλήση της παραπάνω συνάρτησης κατασκευάζουμε ένα νέο στιγμιότυπο του τύπου address με κάθε πεδίο του αρχικοποιήμενο στην εξ’ ορισμού τιμή.

Κληρονομικότητα

Σε αντίθεση με τους διακριτούς τύπους, οι αφηρημένοι τύποι μπορούν να οριστούν με βάση έναν ή περισσότερους διαφορετικούς αφηρημένους τύπους (subtyping). Έτσι ο νέος τύπος (υποτύπος) κληρονομεί τα αντίστοιχα πεδία, την συμπεριφορά και τις συναρτήσεις των τύπων ορισμού του (υπερτύποι). Τονίζουμε ότι υποστηρίζεται και πολλαπλή κληρονομικότητα.
CREATE TYPE shape …
CREATE TYPE point UNDER shape …
CREATE TYPE line UNDER shape …
CREATE TYPE polygon UNDER shape …
Στο παράδειγμα μας ο υπερτύπος είναι ο τύπος shape και οι υποτύποι είναι οι τύποι point, line και polygon.

Χρήση

Οι αφηρημένοι τύποι δεδομένων μπορούν να χρησιμοποιηθούν όπως και οι βασικοί τύποι της SQL. Έστω ότι ορίζουμε ένα ADT με όνομα shape ως εξής:
CREATE TYPE shape
  (refercing_system INTEGER,
  tolerance DECIMAL(8.2),
  geometry BLOB(1M));
Με βάση τον παραπάνω τύπο μπορούμε να ορίσουμε και την σχέση:
CREATE TABLE real_estate_info
  (address address,
  price money,
  owner char(40),
  property shape);
Η διαχείριση των στιγμιότυπων γίνεται με την κλήση των αντιστοίχων συναρτήσεων ορισμού. Χρησιμοποιούμε σαν διαχωριστή πεδίου τις δύο τελείες (dot notation). Για παράδειγμα:
INSERT real_estate_info
VALUES (US_DOLLAR(30000),‘Joe Doe’,spatial(10,12,15,20))
SELECT D_mark(price), owner
FROM real_estate_info
WHERE overlaps(property..geometry, square (5, 5, 25, 25))

Δυνατότητα Αντικατάστασης (Substitutability)

Για να κατανοήσουμε την έννοια της δυνατότητας αντικατάστασης θα χρησιμοποιήσουμε ένα παράδειγμα. Ας υποθέσουμε την ύπαρξη της ακόλουθης σχέσης:
CREATE TABLE real_estate_info
  (price money,
  owner CHAR (40),
  property shape);
Ακόμα, ας υποθέσουμε ότι κάνουμε τις ακόλουθες εισαγωγές:
INSERT INTO real_estate_info VALUES 
(US_DOLLAR(100000),‘Mr. S. White’,point(4, 4))
INSERT INTO real_estate_info VALUES
(CDN_DOLLAR(400000),‘Mr. W. Green’,
poly(point(4, 4), point(10, 10), point(12, 14)))
INSERT INTO SALES VALUES
(S_FRANK(150000),‘Mrs. D. Black’,line(5, 5, 7, 8))
Τα δεδομένα φυλάσσονται ως εξής:
Παρατηρούμε ότι στην στήλη price εκτός από τις τιμές φυλάσσουμε και πληροφορία σχετική με την μονάδα μέτρησης. Ακόμα, αν έχουμε δηλώσει ένα πεδίο σαν τύπο Α, η τιμή του πεδίου αυτού μπορεί να είναι οποιαδήποτε στιγμιότυπο του τύπου Α ή κάποιου από τους υποτύπους του. Έτσι στο πεδίο property του τύπου shape μπορούμε να αναθέσουμε στιγμιότυπα των υποτύπων point, poly και line. Τα στοιχεία του υποτύπου βρίσκονται κατά τον χρόνο εκτέλεσης, γεγονός που απαιτεί καθυστέρηση στην αποτίμηση (late binding).
Το βασικό χαρακτηριστικό της καθυστέρησης στην αποτίμηση είναι η αυτόματη κλήση των συναρτήσεων μετατροπής κατά την εκτέλεση μιας ερώτησης.
Για παράδειγμα, κατά την εκτέλεση της ερώτησης
SELECT owner, dollar_amount(price)
FROM real_estate_info
WHERE dollar_amount(price)<US_DOLLAR(500000);
θα κληθούν αυτόματα οι συναρτήσεις μετατροπής US_DOLLAR, CND_DOLLAR και
SWISS_FRANK ανάλογα με τον τύπο των δεδομένων της στήλης price.

Επώνυμοι Τύποι Γραμμής (Named Row Types)

Οι τύποι που έχουμε μέχρι τώρα εξετάσει αναφέρονται στον ορισμό νέων πεδίων. Υπάρχουν όμως και τύποι για την αναπαράσταση μιας ολόκληρης πλειάδας μια σχέσης -ενός συνόλου δηλαδή πεδίων. Οι τύποι αυτοί ονομάζονται επώνυμοι τύποι γραμμής (named row types). Γενικά, οι επώνυμοι τύποι γραμμής αποτελούν τύπους ορισμένους από τον χρήστη χωρίς ενθυλακωμένη εσωτερική δομή. Για παράδειγμα:
CREATE ROW TYPE account_t
  (acctno INTEGER,
  cust REF(customer_t),
  type CHAR (1),
  opened DATE,
  rate DOUBLE PRECISION,
  balance DOUBLE PRECISION);
Χρησιμοποιώντας τους επώνυμους τύπους γραμμής μπορούμε να ορίσουμε σχέσεις. Για παράδειγμα:
CREATE TABLE account OF account_t
  (PRIMARY KEY acctno);

Τύποι Αναφοράς (Reference types)

Οι επώνυμοι τύποι γραμμής μπορούν να συνδυαστούν με αναφορές, όπως στο πεδίο cust του τύπου γραμμής account_t. Οι τύποι αναφοράς παραπέμπουν σε δηλώσεις τύπων (στην συγκεκριμένη περίπτωση του τύπου customer_t) και χρησιμοποιούνται κυρίως για να μοντελοποιήσουν σχέσεις μεταξύ επώνυμων τύπων γραμμών.
Οι αναφορές μπορούν να έχουν εμβέλεια (scope).
CREATE TABLE account OF account_t
  (PRIMARY KEY acctno,
  SCOPE FOR cust IS customer);
Σε μια σχέση μπορούμε να ορίσουμε αναφορές μόνο σε μεταβλητές που βρίσκονται στο πρώτο επίπεδο (π.χ. cust). Επιπλέον μπορούμε να χρησιμοποιούμε τους τύπους αναφοράς για να εκφράσουμε ερωτήσεις:
SELECT a.acctno, a.cust->name
FROM account a
WHERE a.cust->address..city = “Hollywood”
AND a.balance > 1000000;

Υποτύποι και κληρονομικότητα

Ένας επώνυμος τύπος γραμμής μπορεί να δηλωθεί σαν υπερτύπος από έναν ή περισσότερους επώνυμους τύπους γραμμής. Οι υποτύποι κληρονομούν τις μεταβλητές και τις συναρτήσεις από τους υπερτύπους τους.
Έστω ότι ορίζουμε ένα τύπο γραμμής για να αναπαραστήσουμε εργαζόμενους.
CREATE TYPE empoloyee
  (name CHAR (20), salary DECIMAL (10,2))
Βασισμένοι στον τύπο αυτό ορίζουμε δυο νέους για τους προϊστάμενους και τους φοιτητές που εργάζονται το καλοκαίρι.
CREATE TYPE manager UNDER employee
  (bonus DECIMAL (10,2));
CREATE TYPE summer_stundent UNDER employee
  (school VARCHAR (30));
Χρησιμοποιώντας τύπους και υποτύπους μπορούμε να ορίσουμε ιεραρχίες σε σχέσεις.
Έστω:
CREATE TABLE employees OF employee;
CREATE TABLE managers OF manager UNDER employees;
CREATE TABLE summer_students OF summer_student
  UNDER employees;
Πρακτικά, ιεραρχία στις σχέσεις σημαίνει ότι αν εκφράσουμε ερώτηση που αφορά μια δεδομένη σχέση, θα αναζητηθούν απαντήσεις και σε όλες τις σχέσεις που κληρονομούν από αυτήν. Έτσι, απευθύνοντας ερώτηση στην σχέση employees αυτόματα εξετάζονται και δεδομένα από τις σχέσεις managers και summer_students.

Τύποι Κατηγορημάτων (Type Predicate)

Ας ξαναδούμε την σχέση real_estate_info.
CREATE TABLE real_estate_info
  (price money,
  owner CHAR(40),
  property real_estate);
Είναι δυνατό μια ερώτηση, κατά τον χρόνο εκτέλεσης, να ελέγχει το είδος του τύπου που έχει ορίσει ο χρήστης:
SELECT price, owner, property
FROM real_estate_info
WHERE TYPE(price) IN US_DOLLAR;
Επίσης είναι δυνατό μια ερώτηση να μετατρέψει ένα τύπο σε ένα άλλο υποτύπο του (subtype specification):
SELECT TREAT (price AS US_DOLLAR), owner, property
FROM real_estate_info
WHERE TYPE (price) IN (US_DOLLAR);

Συναρτήσεις και Μέθοδοι ορισμένοι από τον χρήστη (User Defined Functions-Methods)

Ο χρήστης είναι δυνατό να ορίζει συναρτήσεις, μεθόδους και αποθηκευμένες διαδικασίες (stored procedures) βασισμένες σε κάποιο τύπο (ADTs, row types, κλπ.). Οι δηλώσεις τους γίνονται είτε σε SQL/PSM, είτε σε γλώσσες τρίτης ή τέταρτης γενιάς (3GLs ή 4GLs).
Διαχωρίζουμε τις συναρτήσεις που μπορεί ο χρήστης να ορίσει σε εξωτερικές (external) και σε εσωτερικές (internal). Οι εξωτερικές συναρτήσεις γράφονται και δηλώνονται σε μια από τις καθιερωμένες γλώσσες προγραμματισμού (ADA, C κλπ.). Οι συναρτήσεις αυτές προφανώς υπόκεινται στους περιορισμούς αλλά και εκμεταλλεύονται τις βιβλιοθήκες και την λειτουργικότητα της γλώσσας στην οποία ορίζονται. Παράλληλα, μπορούν να περιέχουν και ενσωματωμένη SQL. Αντίθετα, οι εσωτερικές συναρτήσεις είναι δηλωμένες εξ ολοκλήρου σε SQL, εφοδιασμένη με συναρτήσεις ροής (loop, case, if, for, repeat, return κτλ.) και ανάθεσης (set).

Συγκεντρωτικοί Τύποι (Collection Types)

Οι συγκεντρωτικοί τύποι αποτελούν δομές όπως σύνολα (sets), λίστες (lists), πίνακες (arrays) και πολυσύνολα (bags).
CREATE ROW TYPE employee
  (id INTEGER,
  name VARCHAR(30),
  address address,
  manager REF (employee),
  projects SET (REF (project)),
  children LIST (REF (PERSON)),
  hobbies SET (VARCHAR (20)))
Στις ερωτήσεις τα στιγμιότυπα των συγκεντρωτικών τύπων συμπεριφέρονται σαν σχέσεις.
SELECT e.name
FROM employees e
WHERE ‘travel’ IN (SELECT *
FROM TABLE (e.hobbies) c)
AND 5 > (SELECT count(*) FROM TABLE (e.children) d)
Επιπρόσθετα οι πίνακες και οι λίστες υποστηρίζουν και δεικτοδότηση για πιο γρήγορη πρόσβαση.
Ας εξετάσουμε μερικά παραδείγματα. Ας υποθέσουμε ότι έχουμε ορίσει δυο σχέσεις που αφορούν εργαζόμενους και τμήματα (employees και departments αντίστοιχα) ως εξής:
CREATE TABLE employees
  (id INTEGER PRIMARY KEY,
  name VARCHAR (30),
  address ROW (street VARCHAR (40),
  city CHAR(20),
  state CHAR (2)
  zip INTEGER,
  country VARCHAR(30),
  salary DECIMAL(10,2),
  deptno INTEGER REFERENCES departments,
  manager INTEGER RFEFERENCES employees,
  projects SET (INTEGERS),
  children LIST (person),
  hobbies SET (VARCHAR(20)));
CREATE TABLE departments
  (deptno INTEGER PRIMARY KEY,
  manager INTEGER REFERENCES employees,
  projects TABLE (projno INTEGER),
  projname CHAR (30),
  budget DECIMAL (10,2));

Μετασχηματισμός συγκεντρωτικών τύπων σε πίνακες

Ερώτηση: Βρείτε όλους τους εργαζόμενους που δουλεύουν σε περισσότερα από δύο έργα.
SELECT name
FROM employees e
WHERE 2 < (SELECT count(*) FROM TABLE (e.projects) d)
Στο παραπάνω παράδειγμα, παρατηρούμε ότι μπορούμε να χρησιμοποιούμε σαν πίνακα, οποιαδήποτε attributes τύπου SET (εδώ συγκεκριμένα χρησιμοποιούμε το φορμαλισμό TABLE(e.projects)).

Μετασχηματισμός πινάκων σε συγκεντρωτικούς τύπους

Ερώτηση: Βρείτε όλους τους εργαζόμενους που δεν είναι προϊστάμενοι.
SET (TABLE employees
EXCEPT
(SELECT * FROM employees e WHERE e.id IN
(SELECT ee.manager FROM employees ee)))
Στο παραπάνω παράδειγμα, παρατηρούμε ότι μπορούμε να κάνουμε και την αντίστροφη διαδικασία: να μετατρέπουμε, δηλαδή, αποτελέσματα τύπου πίνακα σε αποτελέσματα τύπου SET. Αυτό γίνεται μέσω της δεσμευμένης λέξης SET.

Unnesting

Ερώτηση: Βρείτε τον κωδικό, τον προϋπολογισμό και το μέσο όρο μισθού για κάθε τμήμα.
SELECT d.deptno, d.budget, av.avg_sal
FROM departments d, (SELECT avg (salary) AS avg_sal
FROM e.deptno = d.deptno) AS av
ή όμοια
SELECT d.deptno, d.budget, AVG (e.salary) AS avg_sal
FROM departments d, employees e
WHERE d.deptno = e.deptno
GROUP BY d.deptno, d.budget
Στο παραπάνω παράδειγμα, παρατηρούμε ότι μπορούμε να εισάγουμε στο from clause μιας έκφρασης και ένα ολόκληρο query.

Flattening των αποτελεσμάτων

Ερώτηση: Βρείτε το σύνολο των εξωεργασιακών (sic!) δραστηριοτήτων του εργαζόμενου με κωδικό 12345.
THE (SELECT e.hobbies
FROM employees e
WHERE e.id = 12345)
Στο παραπάνω παράδειγμα, το αποτέλεσμα δεν είναι τύπου ROW(SET(VARCHAR)), αλλά (SET(VARCHAR)), λόγω της χρήσης του τελεστή ΤΗΕ.

Γενικά Παραδείγματα

Ερώτηση: Βρείτε τους εργαζόμενους που ο προϊστάμενος τους διαμένει σε μια συγκεκριμένη διεύθυνση.
SELECT e.id
FROM employees as e
WHERE row (row (123, University Avenue, Palo Alto, CA, 94301))
= (SELECT mgr.address
FROM employees as mgr
WHERE e.manager = mgr.id)
Στο παραπάνω παράδειγμα μπορούμε να δούμε πώς συγκρίνουμε ένα αποτέλεσμα της μορφής ROW με ένα ολόκληρο πίνακα (θυμηθείτε ότι παλαιότερα, θα έπρεπε να
χρησιμοποιήσουμε τελεστές όπως IN, EXISTS, κλπ) Ερώτηση: Βρείτε τους προϊσταμένους και τα έργα με προϋπολογισμό μεγαλύτερο από 100000.
SELECT d.manager, multiset (SELECT *
FROM table (d.projects) as p
WHERE p.budget >100000)
FROM departments as d
Στο εν λόγω παράδειγμα, βλέπουμε πώς μπορούμε να έχουμε αποτέλεσμα τύπου MULTISET, αντί για τύπου SET.
Ερώτηση: Βρείτε ποια από τις διευθύνσεις addr.1_v, addr.2_v, addr.3_v είναι πιο κοντά στη διεύθυνση 123, University Avenue, Palo Alto, CA, 94301 (δεδομένης μιας Boolean συνάρτησης κοντά, (NEAR)).
SELECT row addr
FROM (values (addr1_v, addr2_v, addr3_v)) as addr
WHERE near
(row addr, row(123 University Avenue, Palo Alto, CA, 94301))
ή όμοια
SELECT row addr
FROM (multiset ( addr1_v, addr2_v, addr3_v)) as addr
WHERE near
(row addr, row(123 University Avenue, Palo Alto, CA, 94301))
Στο συγκεκριμένο παράδειγμα, μπορούμε να δούμε αφενός τη χρήση συναρτήσεων που έχει φτιάξει ο χρήστης και αφετέρου ένα φορμαλισμό για την κατασκευή συνόλων από συγκεκριμένες τιμές (μέσω της έκφρασης (values addr1_v, addr2_v, addr3_v) as addr).