Οι σχέσεις IS-A στις Σχεσιακές Βάσεις Δεδομένων
Στο σχεδιασμό Βάσεων Δεδομένων, οι σχέσεις IS-A είναι ίσως η τεχνική που έχει αγνοηθεί περισσότερο από όλες. Ακόμα και όταν η κατηγοριοποίηση των οντοτήτων είναι προφανής, πολλοί επιλέγουν τη δημιουργία ενός πίνακα με πολλά πεδία, που καλύπτουν όλες τις περιπτώσεις, ο οποίος τελικά καταλήγει να είναι “αραιός”. Άλλοι πάλι, προτιμούν να υλοποιήσουν τόσους πίνακες, όσες και οι τελικές κατηγορίες οντοτήτων. Αλλά οι πίνακες αυτοί είναι εντελώς ανεξάρτητοι μεταξύ τους, και τελικά το πρόβλημα της εννοιολογικής συσχέτισης τους μεταφέρεται σε ψηλότερα επίπεδα στο μοντέλο της εφαρμογής.
Ακόμα χειρότερα, η συζήτηση για τη βέλτιστη υλοποίηση συχνά καταλήγει στο δίλημμα: “αραιός πίνακας ή πολλοί ανεξάρτητοι πίνακες;” αφήνοντας εντελώς απέξω την προοπτική της σχέσης IS-A. Ειδικά όταν το έργο είναι “μικρό”, και οι επιπτώσεις στην απόδοση αλλά και τη συντήρηση δεν είναι μεγάλες, οι δύο αυτές επιλογές φαντάζουν αυτόματες, σε αντίθεση με την “πολυπλοκότητα” της υλοποίησης ενός πιο δομημένου σχήματος.
Για να μπορέσουμε να κατανοήσουμε καλύτερα την υλοποίηση των σχέσεων IS-A, και να τις συγκρίνουμε με άλλες τεχνικές, ας φανταστούμε τη ΒΔ ενός ηλεκτρονικού καταστήματος. Μεταξύ άλλων, διατηρεί πληροφορίες για διάφορους τύπους ατόμων: υπαλλήλους, πελάτες, προμηθευτές και άλλες επαφές. Είναι προφανές ότι όλα αυτά τα άτομα έχουν ένα ονοματεπώνυμο (fullname) και πιθανότατα ένα τηλέφωνο (phone). Οι υπάλληλοι έχουν μισθό (salary) και ανήκουν σε τμήμα (department), οι προμηθευτές σχετίζονται με κάποια επιχείρηση (company) και οι πελάτες έχουν ένα ποσό πίστωσης (credit). Επιπλέον, οι υπάλληλοι καθώς και οι πελάτες έχουν στοιχεία αυθεντικοποίησης (username και password). Φυσικά, σε πραγματικές συνθήκες, θα είχαν οριστεί πολύ περισσότερα πεδία για καθεμιά από αυτές τις οντότητες, αλλά ας το κρατήσουμε όσο πιο απλό γίνεται.
Αυτό που δεν είναι τόσο προφανές, αλλά σίγουρα υπονοείται, είναι το γεγονός ότι τα παραπάνω σύνολα συχνά επικαλύπτονται. Για παράδειγμα, δεν υπάρχει λόγος να θεωρήσουμε ότι ένας υπάλληλος ή ένας προμηθευτής δεν μπορεί να είναι συγχρόνως και πελάτης. Επιπλέον, κάποια άτομα δεν ανήκουν σε κανένα από αυτά τα σύνολα — είναι απλά “άλλες επαφές”.
Ας συγκρίνουμε τώρα, χρησιμοποιώντας ένα δείγμα δεδομένων, τις τρεις μεθόδους που αναφέρθηκαν παραπάνω: τον αραιό πίνακα, τους πολλούς ανεξάρτητους πίνακες και την προσέγγιση IS-A.
1. Μεγάλος, αραιός πίνακας
Θα ξεκινήσω από τον αραιό πίνακα, όχι μόνο διότι δίνει μία καλή οπτική αναπαράσταση του παραδείγματος, αλλά επίσης διότι μπορεί να αποτελέσει ένα καλό σημείο εκκίνησης για μία διαδικασία κανονικοποίησης. Έχουμε λοιπόν:
| people | ||||||||
| id | fullname | phone | salary | department | company | credit | username | password |
| 3465 | John Doe | 555-12345 | 1345.67 | R&D | jdoe | ************** | ||
| 9674 | Jane Doess | 980.55 | Sales | 630.43 | jane | ************** | ||
| 5789 | Michael Smith | 555-98736 | Widgets Co. | 176.00 | msmith | ************** | ||
| 1287 | Samantha Blue | 555-03456 | 354.99 | sammyblue | ************** | |||
| 9824 | Alice Red | |||||||
| 7289 | Ben Purple | 555-98765 | Acme Ltd. | |||||
| 1485 | Vincent Maroon | 555-37108 | ||||||
Ο λόγος για τον οποίο τον χαρακτηρίσαμε “αραιό πίνακα” είναι τώρα ξεκάθαρος. Όσο θα μεγαλώνει ο πίνακας, τόσο περισσότερος χώρος θα ξοδεύεται άσκοπα. Στην πραγματικότητα όμως, αυτό δεν είναι το μεγαλύτερο πρόβλημα.
Επειδή όλα τα δεδομένα βρίσκονται στο ίδιο αντικείμενο της ΒΔ (έναν πίνακα) είναι σχεδόν αδύνατο να επιβάλουμε τους απαραίτητους περιορισμούς στο επίπεδο της ΒΔ. Για παράδειγμα, αφού η στήλη department δεν μπορεί να οριστεί ως “NOT NULL”, τι γίνεται αν ένα άτομο έχει μισθό αλλά το αντίστοιχο κελί τμήματος είναι κενό; Ή, πως μπορούμε να επιβάλουμε μοναδικότητα στη στήλη username τη στιγμή που δέχεται κενά;
2. Πολλοί ανεξάρτητοι πίνακες
Για καθεμία από τις οντότητες υπάλληλος, πελάτης, προμηθευτής, άλλη επαφή υλοποιούμε κι από έναν πίνακα. Κάθε πίνακας περιέχει τόσες στήλες όσα και τα κατηγορήματα της κάθε οντότητας ξεχωριστά, συν ένα που χρησιμοποιείται σαν κύριο κλειδί. Φυσικά, σε κάποιες από αυτές υπάρχουν ήδη πεδία που χαρακτηρίζουν την πλειάδα και είναι υποψήφια κύρια κλειδιά, αλλά ας το κάνουμε έτσι χάριν ομοιομορφίας με τις υπόλοιπες μεθόδους.
| employee | ||||||
| id | fullname | phone | salary | department | username | password |
| 4589 | John Doe | 555-12345 | 1345.67 | R&D | jdoe | ************** |
| 7856 | Jane Doess | 980.55 | Sales | jane | ************** | |
| customer | |||||
| id | fullname | phone | credit | username | password |
| 1085 | Jane Doess | 630.43 | doess | ************** | |
| 3718 | Michael Smith | 555-98736 | 176.00 | msmith | ************** |
| 3376 | Samantha Blue | 555-03456 | 354.99 | sammyblue | ************** |
| supplier | |||
| id | fullname | phone | company |
| 9836 | Michael Smith | 555-98736 | Widgets Co. |
| 3376 | Ben Purple | 555-98765 | Acme Ltd. |
| other_contact | ||
| id | fullname | phone |
| 1234 | Alice Red | |
| 4589 | Vincent Maroon | 555-37108 |
Η αδυναμία να εφαρμόσουμε περιορισμούς έχει πλέον μερικώς εξαλειφθεί. Τα usernames μπορούν πλέον να είναι μοναδικά, αλλά μόνο μέσα στα όρια του ίδιου πίνακα, και όχι σε όλο το σύστημα. Επίσης, μπορούμε να εξασφαλίσουμε ότι ο μισθός αλλά και το τμήμα θα είναι πάντα συμπληρωμένα.
Έχουμε πάντως κι εδώ πρόβλημα σπατάλης χώρου, λόγω της επικάλυψης των οντοτήτων. Η Jane και ο Michael εμφανίζονται σε δυο πίνακες ο καθένας, κουβαλώντας μαζί τους τα ονόματα και τηλέφωνα τους. Επιπλέον, επειδή η Jane είναι συγχρόνως υπάλληλος και πελάτης, τα στοιχεία αυθεντικοποίησης της εμφανίζονται και στους δυο πίνακες αντίστοιχα. Αυτό εισάγει την ανάγκη για συγχρονισμό δεδομένων που πρέπει να βρίσκονται σε πολλά μέρη συγχρόνως, που μπορεί να αποδειχτεί δύσκολη δουλειά. Ή, για τα στοιχεία αυθεντικοποίησης, το συμβιβασμό ότι η Jane θα έχει διαφορετικό username/password στα δύο υποσυστήματα (πράγμα που συμβαίνει και στο παράδειγμα).
Τέλος, αν θέλουμε να παράγουμε μία ενιαία λίστα με οποιαδήποτε από τα κοινά δεδομένα, θα πρέπει να το κάνουμε χρησιμοποιώντας ερωτήματα UNION. Για παράδειγμα, προκειμένου να ανακτήσουμε μία γενική λίστα με ονοματεπώνυμα και τηλέφωνα, πρέπει να κάνουμε συνένωση και των τεσσάρων πινάκων.
3. Η προσέγγιση IS-A
Για να υλοποιηθεί μία σχέση IS-A, θα πρέπει πρώτα να οριστούν όλες οι κλάσεις, από τη γενική υπερκλάση και όλες οι ενδιάμεσες μέχρι τις τελικές. Είναι προφανές ότι η γενική υπερκλάση είναι το άτομο (person) και περιέχει τα κατηγορήματα fullname και phone. Τα άτομα που έχουν στοιχεία αυθεντικοποίησης θα τα λέμε χρήστες (users) με κατηγορήματα username και password, που με τη σειρά τους χωρίζονται σε υπαλλήλους (employees: salary, department) και πελάτες (customers: credit). Εκτός από τους χρήστες υπάρχουν οι προμηθευτές (suppliers: company) και οι άλλες επαφές χωρίς επιπλέον κατηγορήματα. Συνοπτικά:
Έστω person(fullname,phone). Ένας user(username,password) είναι (is a) person. Ένας customer(credit) είναι user. Ένας employee(salary) είναι user. Ένας supplier(company) είναι person.
Κάθε πίνακας από τους παρακάτω περιέχει μόνο τα πεδία τα ειδικά για τη συγκεκριμένη (υπο)κλάση. Το πεδίο id είναι συγχρόνως κύριο κλειδί και ξένο κλειδί στην άμεση υπερκλάση της (με εξαίρεση το person που προφανώς δεν έχει δική του υπερκλάση). Για παράδειγμα, το user.id είναι κύριο κλειδί του πίνακα user και ξένο κλειδί στο person.id. Έτσι εξασφαλίζεται αφ’ ενός η σύνδεση της κάθε κλάσης με την υπερκλάση της και αφ’ ετέρου η μοναδικότητα του κάθε στιγμιότυπου μέσα στην κλάση του.
| person | ||
| id | fullname | phone |
| 3465 | John Doe | 555-12345 |
| 9674 | Jane Doess | |
| 5789 | Michael Smith | 555-98736 |
| 1287 | Samantha Blue | 555-03456 |
| 9824 | Alice Red | |
| 7289 | Ben Purple | 555-98765 |
| 1485 | Vincent Maroon | 555-37108 |
| user | ||
| id | username | password |
| 3465 | jdoe | ************** |
| 9674 | jane | ************** |
| 5789 | msmith | ************** |
| 1287 | sammyblue | ************** |
| customer | |
| id | credit |
| 9674 | 630.43 |
| 5789 | 176.00 |
| 1287 | 354.99 |
| employee | ||
| id | salary | department |
| 3465 | 1345.67 | R&D |
| 9674 | 980.55 | Sales |
| supplier | |
| id | company |
| 5789 | Widgets Co. |
| 7289 | Acme Ltd. |
Παρατηρούμε ότι δεν χρειάζεται να υλοποιήσουμε πίνακα για τις “άλλες επαφές”, αφού δεν έχουν επιπλέον κατηγορήματα.
Η σπατάλη χώρου είναι τώρα μηδενική. Μπορούμε να εφαρμόσουμε οτιδήποτε περιορισμούς χρειαζόμαστε. Κάθε στιγμιότυπο μπορεί να ανήκει σε οποιαδήποτε κλάση, αλλά μόνο μία φορά στην καθεμία, αρκεί πρώτα να εμφανίζεται στην άμεση υπερκλάση της. Μπορούν πολύ ευκολότερα να εξαχθούν συγκεντρωτικά αποτελέσματα, για οποιοδήποτε επίπεδο της ιεράρχησης. Κανένας συγχρονισμός δεν χρειάζεται, και ο κάθε χρήστης έχει ένα μοναδικό λογαριασμό στο σύστημα.
Συμπεράσματα
Οι σχέσεις IS-A είναι εναρμονισμένες με τη φιλοσοφία της αυτονομίας μίας ΒΔ, ανεξάρτητα από τις εφαρμογές που τη χρησιμοποιούν. Βοηθούν στη διάκριση των υποσυστημάτων και στην επεκτασιμότητα του συστήματος, καθώς αν χρειαστεί περαιτέρω ανάπτυξη της ιεραρχίας, κανένα από τα ανώτερα επίπεδα δεν χρειάζεται να αλλάξει, συνεπώς ούτε και οι σχετικές εφαρμογές. Επιπλέον, αν χρειαστούν αλλαγές σε κάποια κλάση, επηρεάζεται μόνο ο αντίστοιχος πίνακας. Κι όλα αυτά, χρησιμοποιώντας τις ήδη υπαρχουσες λειτουργίες ενός συστήματος διαχείρισης σχεσιακών βάσεων δεδομένων.

Καλησπέρα. Ωραίο το άρθρο και χρήσιμο με σωστή προσέγγιση.
Μερικές μόνο παρατηρήσεις:
α) Αραιός πίνακας δεν συνεπάγεται μη-κανονικοποιημένος.
β) Μπορείς να βάλεις unique index σε nullable στήλη. Άλλωστε, ως γνωστόν δύο null values δεν θεωρούνται ως μεταξύ τους ίσες ώστε να παραβιάζεται το constraint. και
γ) στην τρίτη περίπτωση υπάρχει σπατάλη χώρου γιατί ξαναποθηκεύεις το πρωτεύον κλειδί στα παιδιά και διατηρείς κ index φυσικά σε αυτό. Επίσης, το flag εάν ένα κελί είναι null να είσαι σίγουρος πως το έχουν βελτιστοποιήσει, άρα η πρώτη περίπτωση δεν συνεπάγεται τη σπατάλη χώρου που μοιάζει να έχει ένας πίνακας.
Σ’ ευχαριστώ για το σχόλιο σου. Πάντα μου αρέσει να βλέπω σχόλια με κριτική σκέψη!
Σχετικά με τις παρατηρήσεις σου:
α) Δίκιο έχεις, αλλά ποτέ δεν ισχυρίστηκα κάτι τέτοιο. Αν αναφέρεσαι στο
αναφερόμουν στην ύπαρξη ενός και μόνο πίνακα κατά την έναρξη μίας οποιασδήποτε διαδικασίας κανονικοποίησης, πράγμα με το οποίο είναι, θεωρητικά, εξοικειωμένος κάποιος που θα διάβαζε το άρθρο, άρα και ένα καλό σημείο για να αρχίσω. Αλλά τώρα που το αναφέρεις, ίσως θα έπρεπε να το έχω εκφράσει καλύτερα.
β) Κι εδώ δίκιο έχεις …εν μέρει! Δύο NULL τιμές δεν θεωρούνται ίσες, αλλά ούτε και διαφορετικές. Όπως για παράδειγμα στη Γεωμετρία δύο ασύμπτωτες ευθείες δεν μπορεί να θεωρηθούν ούτε παράλληλες ούτε τεμνόμενες: απλά δεν υφίσταται το δίλημμα.
Άρα, αφού κι εδώ δεν υφίσταται σύγκριση, το κάθε RDBMS το αντιμετωπίζει διαφορετικά. Ενώ όλα (που ξέρω) θα σε αφήσουν να ορίσεις το index (και γιατί όχι) στην πράξη η MySQL θα σε αφήσει να βάζεις NULL τιμές ενώ ο MSSQL θα σε αφήσει να βάλεις μόνο μία (δηλαδή δύο NULL παύει να είναι unique!).
Για να είμαι εντάξει, θα πρέπει να παραδεχθώ ότι τα ANSI Standards είναι “μαζί σου”, όμως οι απόψεις διίστανται για το αν αυτό είναι σωστό ή όχι.
γ)
Επανάληψη!=ΣπατάληΕίναι δύο διαφορετικά προβλήματα. Όταν επαναλαμβάνεται κάτι άσκοπα, δημιουργεί προβλήματα συγχρονισμού, όπως γίνεται στη δεύτερη περίπτωση με τα ονοματεπώνυμα και ειδικά με τα στοιχεία πιστοποίησης. Και φυσικά όταν έχεις ένα FK δεν μπορείς να μην επαναλάβεις την τιμή του πεδίου στον detail πίνακα… Εδώ το πρόβλημα το λύνουν οι ON UPDATE/ON DELETE constraints, που αν δεν υπήρχαν, δεν θα είχαμε και αξιοπρεπή FKs 😉
Για τη σπατάλη, εννοείται ότι γίνεται optimization σε επίπεδο αποθηκευτικού χώρου, αν εννοείς αυτό. Αλλά εγώ αναφέρομαι στη σπατάλη σε λογικό επίπεδο.