1.1 Μαγειρική Η κυρία Αντωνία είναι καλή μαγείρισσα και φημίζεται για την ποικιλία των φαγητών που ξέρει να φτιάχνει. Για κάθε συνταγή μαγειρικής, εκτός από το όνομά της, γνωρίζει τον σεφ που τη δημιούργησε, τον χρόνο προετοιμασίας (σε λεπτά), τον τρόπο μαγειρέματος (πχ ψήσιμο, βράσιμο, τηγάνισμα, φούρνισμα), πόσες μερίδες βγάζει, και αν είναι υγιεινή και βέβαια τα υλικά που απαιτεί. Για κάθε υλικό θυμάται, εκτός από όνομά του, την ποσότητα που βάζει, την μονάδα που μετράει την ποσότητα (γραμμάρια, πρέζα, μιλιλίτρα). Επέκταση: Αφού χρησιμοποίησε αρκετό καιρό με μεγάλη επιτυχία το παραπάνω πρόγραμμα και κατέγραψε κάμποσες συνταγές, τώρα θέλει να διατηρεί ένα ιστορικό με τα μαγειρέματα που έχει κάνει. Συγκεκριμένα, κάθε φορά που φτιάχνει μια συνταγή, θέλει να διατηρεί την ημερομηνία παρασκευής της, πόσο της κόστισαν τα υλικά, μια συνολική εκτίμηση για το α- Εικόνα 1 Σχεδίαση της ΒΔ για συνταγές μαγειρικής ποτέλεσμα (κακό - καλό - υπέροχο, κτλ), και τέλος προτάσεις για τη βελτίωση της συνταγής (ΒΔ ). Δείτε στην ενότητα 4.1 για μια υλοποίηση της ΒΔ. 2. Ορισμός σχήματος ΒΔ με SQL Η εντολή SELECT της SQL είναι αναμφισβήτητα η πιο συχνά χρησιμοποιούμενη εντολή για έναν προγραμματιστή, καθώς επιτρέπει τον συνδυασμό των δεδομένων των πινάκων και τον υπολογισμό χρήσιμης πληροφορίας. Ας πάρουμε όμως την πορεία μιας ΒΔ από την αρχή. Στο ξεκίνημα πρέπει να δημιουργήσουμε μια νέα, κενή ΒΔ και μετά να ορίσουμε τους πίνακες και τις σχέσεις τους, αυτό που λέμε σχήμα της ΒΔ (database schema). Αυτό μπορεί να γίνει με τον εύκολο τρόπο και με τον επαγγελματικό τρόπο. Ο εύκολος τρόπος είναι να χρησιμοποιήσει ο διαχειριστής της ΒΔ το περιβάλλον γραφικής διεπαφής, όπως αυτό που συναντάει κανείς στην MS Access, ή το SQL Server Management Studio, ή τον Enterprise Manager/SQL Developer της Oracle ή το MySQL Workbench. Εκεί εύκολα, γρήγορα και χωρίς να γράψει γραμμή κώδικα μπορεί να ορίσει πίνακες, πεδία, κλειδιά και περιορισμούς ακεραιότητας. Ο «επαγγελματικός» (ή παλιομοδίτικος) τρόπος είναι γράφοντας ειδικές εντολές SQL που ανήκουν σε ένα υποσύνολο της SQL που ονομάζεται Γλώσσα Ορισμού Δεδομένων (Data Definition Language, DDL) 1. Ας δούμε λοιπόν για λόγους πληρότητας της γνώσης, κάλυψης της διδακτέας ύλης και ιστορικής αναδρομής πώς συντάσσεται η κυριότερη εντολή DDL, η CREATE 2. Το παράδειγμα που θα χρησιμοποιήσουμε αφορά τη ΒΔ με άτομα που κατοικούν σε κάποια πόλη. Αναγνωρίζουμε ότι πρόκειται για μια σχέση ένα-προς-πολλά, αφού σε μια Άτομο Κατοικεί Πόλη πόλη ζουν πολλά άτομα και το κάθε άτομο κατοικεί σε μια μόνο πόλη. Αρχικά κατασκευάζουμε μια νέα, κενή ΒΔ με την εντολή CREATE DATABASE KATOIKOI; Οι επόμενες εντολές δημιουργούν πίνακες σε μια υπάρχουσα ΒΔ. Για να επιλέξουμε τη ΒΔ μέσα στην οποία θα λειτουργούμε, η ε- ντολή είναι USE KATOIKOI; Έπειτα δημιουργούμε τον πίνακα των πόλεων, ορίζοντας τα πεδία του και τον τύπο δεδομένων για καθένα από αυτά. 1 Παρεμπιπτόντως, η SELECT και άλλες εντολές που λειτουργούν στα δεδομένα της ΒΔ και δεν τροποποιούν το σχήμα της, αποτελούν τη Γλώσσα Χειρισμού Δεδομένων (Data Manipulation Language, DML). 2 Τα παραδείγματα έχουν δοκιμαστεί στα ΣΔΒΔ SQL Server, το οποίο θα χρησιμοποιήσουμε και αργότερα όταν θα ασχοληθούμε με ασφάλεια (χρήστες, δικαιώματα, κ.λπ.). Επίσης έχουν δοκιμαστεί με μικρές τροποποιήσεις στην Oracle. igaviotis@gmail.com 8-Νοε-16 Σελ. 1
CREATE TABLE POLH ( TAX_KOD DECIMAL(5,0) PRIMARY KEY, ONO_POL VARCHAR(40) NOT NULL, PERIFEREIA VARCHAR(20) Αναγνωρίζετε ότι για κάθε πεδίο, εκτός από το όνομά του, δηλώνουμε τον τύπο δεδομένων που αποθηκεύει και το μέγεθος του. Οι κυριότεροι τύποι δεδομένων της τυποποιημένης SQL φαίνονται στον διπλανό πίνακα περισσότερα για τους τύπους δεδομένων της SQL στην ενότητα 2.1. Επίσης, με την εντολή δημιουργίας του πίνακα καθορίσαμε ότι: Το πεδίο TAX_KOD είναι ένας πενταψήφιος αριθμός, χωρίς δεκαδικά ψηφία. Το πεδίο TAX_KOD είναι το κλειδί του πίνακα (φράση PRIMARY KEY), συνεπώς δυο πόλεις δεν επιτρέπεται να έχουν ίδια τιμή στο TAX_KOD. Το πεδίο ONO_POL πρέπει να συμπληρώνεται υποχρεωτικά δεν μπορεί να μείνει κενό (φράση NOT NULL) Το πεδίο PERIFEREIA μπορεί να πάρει ως τιμές συμβολοσειρές μήκους μέχρι 20 χαρακτήρων, αλλά επιτρέπεται να μείνει κενό (τιμή Null). Αν αλλάξουμε γνώμη για κάτι και πρέπει να τροποποιήσουμε τη σχεδίαση του πίνακα, το ευκολότερο είναι να τον διαγράψουμε με την εντολή DROP TABLE POLH; και να τον ξαναδημιουργήσουμε από την αρχή. Ακολουθεί ο πίνακας των ατόμων CREATE TABLE ATOMO ( ADT VARCHAR(10) PRIMARY KEY, ONO_ATOM VARCHAR(40) NOT NULL, HM_GEN DATE, YPSOS DECIMAL(5,2), POLH_POU_ZEI DECIMAL(5,0) REFERENCES POLH(TAX_KOD) Τι νέο μαθαίνουμε από αυτό το παράδειγμα: Το πεδίο YPSOS μπορεί να πάρει 2 δεκαδικά ψηφία και άλλα 3 ακέραια ψηφία, π.χ. την τιμή 123.52. Η POLH_POU_ZEI είναι ξένο κλειδί που συνδέεται με το πεδίο TAX_KOD του πίνακα POLH. Η μορφή της εντολής δημιουργίας νέου πίνακα είναι: CREATE TABLE ( πεδίο1 τύποςδεδομένων1, πεδίο2 τύποςδεδομένων2,... για όσα πεδία χρειάζονται. Μετά τον καθορισμό του τύπου δεδομένων, μπορούμε να βάλουμε τις φράσεις PRIMARY KEY για το πρωτεύον κλειδί, REFER- ENCES για σύνδεση με ξένο κλειδί και NOT NULL για απαγόρευση κενών τιμών. Ας πούμε ότι για τα άτομα μας ενδιαφέρει εκτός από τον Αριθμό Δελτίου Ταυτότητας (πεδίο ADT) και ο Εικόνα 3 Το σχήμα της ΒΔ KATOIKOI στον SQL Server αριθμός μητρώου κοινωνικής ασφάλισης που είναι και αυτός μοναδικός για τον καθένα μας. Για να προσθέσουμε το νέο αυτό πεδίο στον πίνακα, γράφουμε ALTER TABLE ATOMO ADD AMKA CHAR(11) NOT NULL UNIQUE; Ανακεφαλαιώνοντας, οι εντολές CREATE TABLE και DROP TABLE είναι οι δυο σημαντικότερες εντολές της Γλώσσας Ορισμού Δεδομένων που λειτουργούν πάνω σε πίνακες. Άλλες εντολές της DDL που δεν θα σας χρειαστούν, παρά μόνον αν εξαναγκαστείτε να διαχειρίζεστε τη ΒΔ από το περιβάλλον γραμμής εντολών (command line) χωρίς το γραφικό περιβάλλον του ΣΔΒΔ είναι η RE- NAME που αλλάζει το όνομα ενός πίνακα και η ALTER TABLE που τροποποιεί τη δομή του προσθέτοντας / αλλάζοντας / διαγράφοντας πεδία. 2.1 Τύποι δεδομένων της SQL VARCHAR (n) DECIMAL(m, d) Συμβολοσειρά μήκους μέχρι n χαρακτήρων Αριθμός με m ψηφία, εκ των οποίων d δεκαδικά Κατά τη σχεδίαση των πινάκων της ΒΔ, αφού προσδιορίσουμε τα πεδία που συμμετέχουν στον κάθε πίνακα πρέπει να ορίσουμε τι τύπου δεδομένα θα αποθηκεύονται σε κάθε πεδίο. Ο τύπος δεδομένων καθορίζει το πεδίο τιμών, δηλαδή τη γκάμα των πιθανών τι- igaviotis@gmail.com 8-Νοε-16 Σελ. 2 DATE TIME DATETIME Ημερομηνία Ώρα Ημερομηνία και ώρα Εικόνα 2 Οι βασικοί τύποι δεδομένων της SQL
μών που μπορεί να λάβει, και τις λειτουργίες (πχ αριθμητικές, σύγκρισης & ταξινόμησης) που μπορούν να επιτελεστούν στις τιμές του τύπου δεδομένων. Ήδη έχουμε χρησιμοποιήσει τους βασικούς τύπους δεδομένων (δείτε στην Εικόνα 2), τώρα θα πούμε λίγα πράγματα παραπάνω. Μπορούμε να εντάξουμε τους τύπους δεδομένων που αποθηκεύονται σε ΒΔ σε τέσσερεις ομάδες: Συμβολοσειρά Είναι ο πιο συνηθισμένος τύπος δεδομένων που δέχεται ως τιμές ακολουθίες χαρακτήρων μέχρι ενός ορισμένου μήκους. Οποιοσδήποτε χαρακτήρας (γράμμα, αριθμητικό ψηφίο, ειδικός χαρακτήρας) μπορεί να περιέχεται σε οποιαδήποτε θέση. Αριθμός Υπάρχουν διάφοροι τύποι αριθμών, τόσο ως προς την εμφάνισή τους, πχ που έχουν ή δεκαδικά ψηφία ή είναι ακέραιοι, όσο και ως προς την εσωτερική τους αναπαράσταση, πχ είναι κατάλληλοι για αριθμητικές πράξεις, ή συγκρίσεις, ή νομισματικές τιμές. Ημερομηνία/Ώρα Και εδώ υπάρχουν διαφοροποιήσεις ως προς την ακρίβεια. Για παράδειγμα, για το πεδίο ΓΕΝΕΘΛΙΑ αρκεί μια ημερομηνία 2002-02-25, για το ΠΡΟΣΕΛΕΥΣΗ μια τιμή 2012-12-12 07:04:56, ενώ για τον τερματισμό ενός αγώνα F1 04:39:45.12 Δυαδικά (binary) δεδομένα Πέρα από τα πεδία Ναι/Όχι, μπορεί να έχουμε ακολουθίες δυαδικών ψηφίων 3. Δυστυχώς, υπάρχουν μεγάλες διαφορές στους τύπους δεδομένων ανάμεσα στα διάφορα ΣΔΒΔ, οπότε παρακάτω θα παρουσιάσουμε την εκδοχή της τυποποιημένης ANSI SQL που τα καταραμένα ΣΔΒΔ δεν υποστηρίζουν πλήρως και ακόμα χειρότερα υπάρχουν περιπτώσεις που υποστηρίζουν το συντακτικό και τις δεσμευμένες λέξεις, αλλά η υλοποίησή τους διαφέρει, πχ ανάμεσα σε Oracle και SQL Server. Για τις συμβολοσειρές ξεκινάμε με τον τύπο δεδομένων CHAR(n) που δεσμεύει χώρο για n χαρακτήρες στο πεδίο, ακόμα κι αν α- ποθηκεύσουμε λιγότερους (ωστόσο, απαγορεύεται να αποθηκεύσουμε περισσότερους από n). Το υπόλοιπο παραγεμίζεται με χαρακτήρες κενού (space). Οι χαρακτήρες αναπαριστώνται κατά τον κώδικα ASCII, άρα καθένας καταλαμβάνει χώρο 1 byte. Αν φιλοδοξείτε η ΒΔ που κατασκευάζετε να λειτουργεί σε πολλές χώρες και χρειάζεται να αποθηκεύει συμβολοσειρές σε πολλά αλφάβητα του κόσμου, η λύση ονομάζεται NCHAR(n), που αποθηκεύει πάλι n χαρακτήρες αλλά κατά το πρότυπο Unicode αυτή τη φορά, άρα ο κάθε χαρακτήρας θα σας κοστίσει 2 byte χώρου. Τώρα αν σας έπιασαν (αδικαιολόγητες στις μέρες μας) τσιγκουνιές για χώρο, υπάρχει η εκδοχή NVARCHAR(n) που αποθηκεύει πάλι κατά Unicode, αλλά δεν δεσμεύει 2n bytes για όλες τις συμβολοσειρές. Αντίθετα, αποθηκεύει το πεδίο ως συμβολοσειρά μεταβλητού μήκους και χρειάζεται χώρο ανάλογο με το μήκος της εκάστοτε τιμής για κάθε εγγραφή. Βέβαια, ως γνωστόν, η φτώχεια τρώει τον παρά, και έτσι ίσως η αναπαράσταση μεταβλητού μήκους να είναι πιο αργή από την σταθερού μήκους. Πάντως, η συνήθης πρακτική είναι να δηλώνετε όλα τα πεδία τύπου συμβολοσειράς ως NVARCHAR(n) 4. Οι αριθμητικοί τύποι δεδομένων είναι μια άλλη πονεμένη ιστορία στην SQL. Οι ακέραιοι δηλώνονται ως INTEGER και βέβαια δεν έχουν δεκαδικά ψηφία, ενώ μπορούν να πάρουν αρνητικές και θετικές τιμές. Μη ρωτήσετε όμως ποια είναι η μεγαλύτερη τιμή που μπορούν να έχουν 5. Οι πραγματικοί δηλώνονται ως REAL είτε ως DOUBLE PRECISION (διπλής ακρίβειας) και για τις τιμές τους ισχύει το ίδιο μπάχαλο που είδαμε στους ακέραιους. Η πιο ασφαλής επιλογή για αριθμούς είναι η δήλωση DECIMAL(m, d) ή NUMERIC(m, d) που δέχεται αριθμούς με m το πολύ ψηφία, εκ των οποίων τα d είναι δεκαδικά. Για παράδειγμα, το πεδίο για το μισθό ενός εργαζόμενου μπορεί να δηλωθεί DECI- MAL(6, 2) που μπορεί να πάρει τιμή ως 9999.99. Έτσι καλύπτουμε μέχρι και τον μισθό του πρωθυπουργού μας που είναι 9416.67. Πάντως, όπως πάνε τα πράγματα, θα μπορούμε να δηλώνουμε μισθούς με DECIMAL(5, 2) και ευχαριστημένοι να 'μαστε... Για ημερομηνίες υπάρχει η δήλωση DATE και συνήθως οι τιμές γράφονται σε μορφή ΕΕΕΕ-ΜΜ-ΗΗ, για ώρες υπάρχει η δήλωση TIME και οι τιμές συνήθως γράφονται ΩΩ:ΛΛ:ΔΔ. Η δήλωση DATETIME περιλαμβάνει ημερομηνία και ώρα με ακρίβεια τουλάχιστον δευτερολέπτου, αλλά συνήθως τα δευτερόλεπτα δέχονται και δεκαδικά ψηφία. Για δυαδικά δεδομένα υπάρχει η δήλωση BIT(n) που ορίζει μια ακολουθία n δυαδικών ψηφίων. Αν n=1, τότε μπορούμε να αποθηκεύσουμε τις τιμές 0 και 1. Μια ασφαλής συμβουλή σε ότι αφορά τους τύπους δεδομένων είναι να γνωρίζετε αυτούς που βρίσκονται στην Εικόνα 2 και, αν ποτέ χρειαστείτε κάτι πιο συγκεκριμένο, να ανατρέξετε στην τεκμηρίωση του συγκεκριμένου ΣΔΒΔ που χρησιμοποιείτε. 2.2 Οριζόμενοι τύποι δεδομένων Εδώ θα κάνουμε μια σύντομη αναφορά στη δυνατότητα που δίνει η SQL στο διαχειριστή της ΒΔ να ορίζει δικούς του τύπους δεδομένων. Η εντολή λέγεται CREATE TYPE 6 και να δυο παραδείγματα: CREATE TYPE TYPOS_ONOMA FROM NVARCHAR(20) NOT NULL; --για ονόματα ανθρώπων CREATE TYPE TYPOS_TK FROM DECIMAL(5,0 -για ελληνικούς ταχυδρομικούς κώδικες 3 Αρκετά σύγχρονα ΣΔΒΔ έχουν ειδικούς τύπους δεδομένων για δυαδικά δεδομένα μεγάλου μεγέθους, όπως είναι οι εικόνες, βίντεο και άλλα δυαδικά αρχεία που επιθυμούμε να αποθηκεύονται μέσα στη ΒΔ και όχι στο σύστημα αρχείων. Για τέτοια δεδομένα έχει επικρατήσει η ονομασία BLOB (Binary Large OBjects). 4 Παρεμπιπτόντως το μήκος n δεν μπορεί να είναι πολύ μεγάλο. Στην Access φτάνει το 255 και στην Oracle to 2000. Αν θέλετε να αποθηκεύσετε το κείμενο ενός βιβλίου ως πεδίο συμβολοσειράς, δείτε τους τύπους δυαδικών δεδομένων. 5 Στην Access 32767 (16 bit), στον SQL Server 2 δις (32 bit), στην Oracle 38 δεκαδικά ψηφία! 6 Σε μερικά ΣΔΒΔ, όπως στην PostgreSQL ονομάζεται CREATE DOMAIN. igaviotis@gmail.com 8-Νοε-16 Σελ. 3
CREATE TABLE PERSON ( ONOMA TYPOS_ONOMA, EPONYMO TYPOS_ONOMA, ODOS NVARCHAR(40), TK TYPOS_TK Εδώ ορίσαμε δυο δικούς μας τύπους δεδομένων και στην τρίτη εντολή φτιάξαμε ένα πίνακα που τα πεδία του είναι ορισμένα με τους νέους τύπους δεδομένων μας. Έτσι έχουμε κάποιου είδους επαναχρησιμοποίηση: αν ξαναφτιάξουμε τη ΒΔ αλλάζοντας το μήκος του TYPOS_ONOMA από 20 σε 30, τότε όπου έχουμε δηλώσει τέτοια πεδία, θα πάρουν το νέο μήκος. 3. Περιορισμοί ακεραιότητας Είναι καλή πρακτική που σπανίως ακολουθείται στην πραγματικότητα όταν υλοποιούμε το σχήμα της ΒΔ, δημιουργώντας τους πίνακες και ορίζοντας τα πεδία τους, να προσπαθούμε να είμαστε αυστηροί με τις προδιαγραφές που θα πρέπει να τηρούν τα δεδομένα που θα τοποθετηθούν μέσα στους πίνακες. Αυτές οι προδιαγραφές, ή περιορισμοί ακεραιότητας προκύπτουν από τη φύση των δεδομένων και τη χρήση τους στη ΒΔ. 3.1 Περιορισμοί εγκυρότητας τιμών Οι πιο απλοί κανόνες περιορίζουν το εύρος των τιμών του πεδίου. Για παράδειγμα, το πεδίο μισθός είναι ένας αριθμός με 6 ψηφία από τα οποία τα δύο είναι δεκαδικά, αλλά, όπως ξέρουμε, ο μισθός δεν μπορεί να είναι αρνητικός. Επίσης, το ύψος εκφρασμένο σε εκατοστά, είναι ένας αριθμός με τιμές από 100 ως 250. Έτσι μπορούμε να δημιουργήσουμε τον πίνακα με την CREATE TABLE ERGAZOMENOS ( ONOMA NVARCHAR(40) NOT NULL, MISTHOS DECIMAL (6,2) ) NOT NULL CHECK (MISTHOS > 0), YPSOS INTEGER CHECK (YPSOS BETWEEN 100 AND 260), --ΣΕ ΕΚΑΤΟΣΤΑ HM_GEN DATE CHECK (HM_GEN < '1999-12-31'), FYLO NCHAR(1) CHECK (FYLO = 'Α' OR FYLO = 'Γ') Άρα για κάθε πεδίο του πίνακα μετά το όνομά του και τον τύπο δεδομένων του μπορούμε να έχουμε τη φράση CHECK που επιβάλει έναν κανόνα που αφορά τις τιμές του πεδίου 7. Με ευθύνη του ΣΔΒΔ, σε καμιά περίπτωση δεν μπορεί να προκύψει εγγραφή στον πίνακα που να παραβιάζει κάποιον από τους κανόνες ακεραιότητας, είτε από νεοεισερχόμενη εγγραφή, είτε από τροποποίηση υφιστάμενης εγγραφής. Ο κανόνας είναι εκεί και τηρείται πάντα και από όλους. Έτσι αν προσπαθήσω INSERT INTO ERGAZOMENOS VALUES ('ΝΙΚΟΣ ΑΝΔΡΕΟΥ', 1234.56, 261, '1995-06-09', 'Α' θα χτυπήσει σφάλμα με διαγνωστικό μήνυμα: The INSERT statement conflicted with the CHECK constraint "CK ERGAZOMEN YPSOS 31EC6D26". The conflict occurred in database "TEST", table "dbo.ergazomenos", column 'YPSOS'. Ίσως προσέξατε στον ορισμό του πεδίου ONOMA τη φράση NOT NULL. Αυτός ο κανόνας απαγορεύει να εισαχθεί μια εγγραφή στον πίνακα από την οποία να λείπει το όνομα του εργαζομένου. INSERT INTO ERGAZOMENOS (ONOMA, FYLO) VALUES ('ΑΝΝΑ ΠΑΠΠΑ', 'Γ' Cannot insert the value NULL into column 'MISTHOS', table 'TEST.dbo.ERGAZOMENOS'; column does not allow nulls. INSERT fails. Αντίθετα, μπορεί να λείπει πχ. το ύψος του και η ημερομηνία γέννησης, χωρίς πρόβλημα INSERT INTO ERGAZOMENOS (ONOMA, MISTHOS, FYLO) VALUES ('ΑΝΝΑ ΠΑΠΠΑ', 999.99, 'Γ' δίνει πίνακα ONOMA MISTHOS YPSOS HM_GEN ------------------------------------------------ ΝΙΚΟΣ ΑΝΔΡΕΟΥ 1234.56 182 1995-06-09 ΑΝΝΑ ΠΑΠΠΑ 999.99 NULL NULL Γενικά καλό είναι να αποφεύγουμε να επιτρέπουμε τις κενές τιμές. Εννοείται ότι αργότερα στη ζωή της ΒΔ οι κενές τιμές μπορούν να συμπληρωθούν από νόμιμες τιμές, σύμφωνα με τον κανόνα ακεραιότητας του πεδίου. Μερικές φορές θέλουμε να επιβάλουμε κανόνες που εμπλέκουν τιμές πολλών πεδίων του πίνακα. Για παράδειγμα, ας υποθέσουμε ότι το αφεντικό της επιχείρησης απαιτεί το (σεξιστικό) κανόνα να μην προσλαμβάνονται γυναίκες που έχουν γεννηθεί πριν το 1990, ενώ για τους άντρες δεν υπάρχει όριο ηλικίας. Μπορούμε να επιβάλουμε ένα κανόνα τροποποιώντας τον ορισμό του πίνακα με την εντολή ALTER TABLE ERGAZOMENOS ADD CONSTRAINT CK_oxi_grioules CHECK (FYLO = 'Α' OR (FYLO = 'Γ' AND HM_GEN > '1990-01-01') 7 Έπεσα από τα σύννεφα όταν έμαθα ότι η MySQL δεν υποστηρίζει την CHECK. Ντροπή! igaviotis@gmail.com 8-Νοε-16 Σελ. 4
Μέχρι στιγμής έχουμε καλύψει κανόνες ακεραιότητας που αφορούν τις νόμιμες τιμές που μπορούν να λάβουν τα πεδία των εγγραφών του πίνακα για να αντανακλούν τους περιορισμούς που ισχύουν στον πραγματικό κόσμο. Τώρα θα δούμε κανόνες που έχουν σχέση με την πολλαπλότητα των εγγραφών και τη σύνδεση πινάκων μέσω ξένων κλειδιών. 3.2 Περιορισμοί πολλαπλότητας Έχουμε δει ότι στους περισσότερους πίνακες υπάρχει ένα πεδίο που χαρακτηρίζει με μοναδικό τρόπο κάθε εγγραφή και αποτελεί το (πρωτεύον) κλειδί του πίνακα. Αυτό δηλώνεται με τη φράση PRIMARY KEY στο σχετικό πεδίο. CREATE TABLE OXHMA ( PINAKIDA NCHAR(7) NOT NULL PRIMARY KEY, AR_PLAISIOU DECIMAL(20,0), KYBIKA INTEGER Όπως γνωρίζουμε, κάθε αυτοκίνητο έχει διαφορετικό αριθμό πλαισίου και θα θέλαμε αυτό να το επιβάλουμε με κανόνα. Αλλιώς, κάποιος μπορεί να κλέβει αυτοκίνητα, να αφαιρεί τις πινακίδες τους, να πηγαίνει στο γραφείο του Υπουργείου Συγκοινωνιών και να βγάζει άλλη πινακίδα, για τον ίδιο αριθμό πλαισίου. Γι αυτό ALTER TABLE OXHMA ADD CONSTRAINT UC_oxi_kleftes UNIQUE (AR_PLAISIOU Εννοείται ότι τέτοιους περιορισμούς μπορούμε να τους δηλώσουμε από την αρχή στην CREATE TABLE. Ως προς τη λειτουργία τους, δεν υπάρχει διαφορά ανάμεσα σε ένα UNIQUE και σε ένα PRIMARY KEY. Ωστόσο, καταλαβαίνουμε ότι χρησιμοποιούμε το PRIMARY KEY για να συνδέσουμε τον πίνακα με άλλους πίνακες ή να αναζητήσουμε τις εγγραφές του 8, ενώ το UNIQUE απλώς αποκλείει διπλότυπα στις τιμές του πεδίου. Τόσο το PRIMARY KEY, όσο και το UNIQUE μπορεί να αποτελούνται από πολλά πεδία, όχι μόνο από ένα. Ας δοκιμάσουμε τώρα τον περιορισμό: INSERT OXHMA VALUES ('ΕΜΑ3205', 9876543210, 1400 INSERT OXHMA VALUES ('ΕΜΕ5392', 1234567890, 1400 Μέχρι εδώ όλα καλά, οι δυο εγγραφές μπαίνουν αδιαμαρτύρητα στον πίνακα. Όμως στη συνέχεια: INSERT OXHMA VALUES ('ΕΜΑ3205', 3333333333, 1400 Violation of PRIMARY KEY constraint 'PK OXHMA 76ECF26C97D0AE71'. Cannot insert duplicate key in object 'dbo.oxhma'. The duplicate key value is (ΕΜΑ3205). Η INSERT δεν ολοκληρώνεται εξαιτίας παραβίασης του πρωτεύοντος κλειδιού. INSERT OXHMA VALUES ('ΕΜΚ5195', 9876543210, 1600 Violation of UNIQUE KEY constraint 'UQ OXHMA AA8DD158A48768C1'. Cannot insert duplicate key in object 'dbo.oxhma'. The duplicate key value is (9876543210). Αυτή η INSERT δεν ολοκληρώνεται εξαιτίας παραβίασης του UNIQUE κλειδιού. 3.3 Περιορισμοί αναφοράς Έχουμε μιλήσει αρκετές φορές για πίνακες που συνδέονται με κάποια κοινά πεδία. Σε τέτοιες περιπτώσεις είναι χρήσιμο να επιβάλλεται η σχέση σύνδεσης μεταξύ των πεδίων και των πινάκων τους με τη φράση FOREIGN KEY ως εξής: CREATE TABLE EPISKEVH ( HMNIA DATE NOT NULL, PIN_OXHM NCHAR(7) NOT NULL, PRIMARY KEY (HMNIA, PIN_OXHM), FOREIGN KEY (PIN_OXHM) REFERENCES OXHMA (PINAKIDA) Η εντολή ξεκινά δηλώνοντας τα δυο πεδία του πίνακα και μετά δηλώνει ότι συναποτελούν το πρωτεύον κλειδί του πίνακα. Η τελευταία γραμμή ορίζει ότι το πεδίο PIN_OXHM συνδέεται με το πεδίο πινακίδα του πίνακα OXHMA. Και τι πετυχαίνουμε με αυτή τη δήλωση; Καταρχάς επιβάλλεται ότι για κάθε όχημα που πηγαίνει για επισκευή, υπάρχει σχετική εγγραφή στον πίνακα OXHMA. Αυτός ο έλεγχος γίνεται όχι μόνο κατά την εισαγωγή, αλλά και αργότερα, αν πχ προσπαθήσουμε να αλλάξουμε το PIN_OXHM σε κάποια τιμή που δεν υπάρχει στον πίνακα OXHMA. Επιπρόσθετα, ο περιορισμός ξένου κλειδιού δεν θα επιτρέψει τη διαγραφή οχήματος που έχει επισκευαστεί INSERT EPISKEVH VALUES ('2012-10-31', 'ΕΜΑ3205' -->OK DELETE FROM OXHMA WHERE PINAKIDA='ΕΜΑ3205'; The DELETE statement conflicted with the REFERENCE constraint "FK EPISKEVH PIN_OX 5CD6CB2B". The conflict occurred in database "TEST", table "dbo.episkevh", column 'PIN_OXHM'. 8 Αρκετές φορές το πρωτεύον κλειδί είναι κατασκευασμένο και η τιμή του δεν έχει κάποια φυσική σημασία, δεν είναι πχ ΑΦΜ ή ΑΔΤ. Τότε είναι βολικό να αφήσουμε το ΣΔΒΔ να δημιουργεί αυτόματα τις τιμές του πεδίου δηλώνοντας KOD_ERGAZ INTEGER IDENTITY(1,1) PRIMARY KEY που δημιουργεί τιμές αύξουσας αρίθμησης ξεκινώντας από το 1 και με βήμα 1. igaviotis@gmail.com 8-Νοε-16 Σελ. 5
Έτσι αποκλείεται να υπάρξουν «ξεκρέμαστες» εγγραφές. Τέλος μια χρήσιμη επέκταση του περιορισμού αναφοράς που κάνει τη ζωή πιο εύκολη είναι να χρησιμοποιήσουμε τη φράση ON DELETE CASCADE. Αυτή επιβάλλει ότι αν διαγραφεί μια εγγραφή του πίνακα OXHMA που έχει σχετικές εγγραφές στον EPISKEVH, όπως προσπαθήσαμε να κάνουμε πρωτύτερα, η διαγραφή θα ολοκληρωθεί με επιτυχία και θα προκαλέσει την αλυσιδωτή διαγραφή των σχετικών εγγραφών από τις επισκευές, έτσι ώστε να μην μείνουν ξεκρέμαστες. Παρομοίως, η ON UPDATE CAS- CADE πυροδοτείται κατά την αλλαγή της πινακίδας ενός οχήματος και προκαλεί την αλλαγή της πινακίδας γι αυτό το όχημα στον πίνακα των επισκευών (δείτε Σφάλμα! Το αρχείο προέλευσης της αναφοράς δεν βρέθηκε. για να το θυμηθείτε στην Access). Λίγο επικίνδυνο, αλλά βολικό, ε; Ανακεφαλαιώνοντας, είδαμε τρία είδη περιορισμών ακεραιότητας: CHECK που περιορίζει τις τιμές σε αποδεκτό εύρος PRIMARY KEY / UNIQUE που αποκλείει διπλότυπα FOREIGN KEY... REFERENCES που επιβάλλει συνέπεια στις σχέσεις πινάκων Με τους περιορισμούς ακεραιότητας είναι βέβαιο ότι η ΒΔ θα βρίσκεται πάντα σε νόμιμη κατάσταση και δεν θα γεμίσει «σκουπίδια» μετά από μερικά χρόνια λειτουργίας. 4. Παραδείγματα Ασκήσεις στον ορισμό σχήματος 4.1 Ορισμός σχήματος για τη μαγειρική Για παράδειγμα, ας δούμε τις εντολές DDL για να ορίσουμε το σχήμα της ΒΔ όπως περιγράφεται στην ενότητα 1.1. Θα χρησιμοποιήσουμε MySQL. Αρχικά κατασκευάζουμε τη ΒΔ με την CREATE SCHEMA mageiriki; και μετά για να εκτελούνται όλες οι εντολές σε αυτή τη ΒΔ, χωρίς να αναφέρουμε το όνομά της κάθε φορά USE mageiriki; Ξεκινάμε να φτιάχνουμε τους πίνακες από εκείνους που στέκονται μόνοι τους (δεν περιέχουν ξένα κλειδιά). CREATE TABLE syntagi ( onoma_syntagis VARCHAR(50) NOT NULL, chef VARCHAR(40) NOT NULL, xronos_proetoimasias INT UNSIGNED NOT NULL, tropos_mageirematos VARCHAR(45) NULL, merides INT NOT NULL, einai_ygieini TINYINT(1) NOT NULL, PRIMARY KEY syntagi_pk (onoma_syntagis) Και ακολουθούν οι εξαρτώμενοι πίνακες CREATE TABLE yliko ( onoma_ylikou VARCHAR(30) NOT NULL, posotita DOUBLE UNSIGNED NOT NULL, monada_metrisis VARCHAR(20) NOT NULL, xreiazetai_gia VARCHAR(50) NOT NULL, PRIMARY KEY yliko_pk (onoma_ylikou, xreiazetai_gia), CONSTRAINT yliko_syntagis_fk FOREIGN KEY (onoma_ylikou) REFERENCES syntagi (onoma_syntagis) ON DELETE CASCADE ON UPDATE CASCADE CREATE TABLE istoriko ( onoma_syntagis VARCHAR(50) NOT NULL, hmnia_paraskevis DATETIME NOT NULL, kostos_ylikwn DECIMAL(10, 2 ) NULL, apotelesma VARCHAR(20) NOT NULL, protaseis LONGTEXT NULL, PRIMARY KEY istoriko_pk (hmnia_paraskevis, onoma_syntagis), INDEX istoriko_syntagis_fk (onoma_syntagis), CONSTRAINT istoriko_syntagis_fk FOREIGN KEY (onoma_syntagis) REFERENCES syntagi (onoma_syntagis) ON DELETE CASCADE ON UPDATE CASCADE Παρατηρήστε στα ξένα κλειδιά τη φράση με τα CASCADE που εξασφαλίζει την τήρηση του περιορισμού ακεραιότητας σε διαγραφές και ενημερώσεις. igaviotis@gmail.com 8-Νοε-16 Σελ. 6
Τώρα που ακόμη δεν έχουμε εισάγει δεδομένα στους πίνακες, αν χρειαστεί να τροποποιήσουμε κάποιον, απλώς εκτελούμε DROP TABLE και τον ξαναφτιάχνουμε στο πι-και-φι με την κατάλληλη CREATE TABLE. Ναι, αλλά αν είχαν μπει δεδομένα μέσα στον πίνακα και δεν μπορούσαμε να τα σβήσουμε (πχ, αν η ΒΔ είναι σε παραγωγική λειτουργία Ή, ακόμη χειρότερα, αν προσπαθήσετε DROP TABLE syntagi; θα φάτε στα μούτρα το Error Code: 1217. Cannot delete or update a parent row: a foreign key constraint fails Και, παρότι το μήνυμα σφάλματος δεν είναι και πολύ επεξηγηματικό, ελπίζω να καταλαβαίνετε γιατί αποτυγχάνει η διαγραφή του πίνακα. Για να ξεφορτωθείτε τον πίνακα syntagi θα έπρεπε να διαγράψετε και όλους τους εξαρτώμενους πίνακες (yliko, istoriko), δεδομένα και σχήμα, και να τους ξαναφτιάξετε και να τους γεμίσετε πιασ το αβγό και κούρευτο. Η λύση βέβαια σε τέτοια σενάρια είναι η ALTER TABLE που τροποποιεί τον πίνακα χωρίς να τον σβήνει και χωρίς να πειράζει τα δεδομένα που περιέχει, ενώ δεν ενοχλεί και τους εξαρτώμενους πίνακες. 4.2 Διαγράμματα Αν μπήκατε στον κόπο να γράψετε τις CREATE εντολές της προηγούμενης ενότητας και να τις εκτελέσετε για να φτιάξετε τη ΒΔ, τότε θα εκτιμήσετε τη δυνατότητα κατασκευής του Extended Entity Relationship διαγράμματος μέσω της επιλογής Reverse Engineer από το Εικόνα 4 Διάγραμμα EER για τη ΒΔ μενού Database (Εικόνα 12) που μας προσφέρει το MySQL Workbench. Τα διαγράμματα είναι πολύ χρήσιμα, διότι μας δίνουν με εποπτικό τρόπο το σχήμα της ΒΔ με τους πίνακες και τις σχέσεις τους και έτσι μπορούμε ευκολότερα να καταλάβουμε (ή να θυμηθούμε) τη λογική λειτουργίας της ΒΔ. Ειδικά η MySQL έχει εξαιρετικές δυνατότητες μοντελοποίησης με τμηματικά διαγράμματα που βοηθούν όταν το σχήμα είναι τεράστιο, ή χρωματισμένους πίνακες για κατηγοριοποίηση, κλπ. Άφησα για το τέλος ένα μικρό μυστικό: δεν χρειαζόταν να παιδευτείτε με τις DDL εντολές το κάναμε μόνο για εκπαιδευτικούς λόγους. Θα μπορούσατε να ετοιμάσετε το διάγραμμα από το μενού File, επιλογή New Model και μετά Add Diagram. Στο διάγραμμα να προσθέσετε τους πίνακες με τα πεδία τους και να τους συσχετίσετε μέχρι να μοντελοποιήσετε όλη τη ΒΔ Κατόπιν από το μενού Database, η επιλογή Forward Engineer στον αυτόματο (πατώντας κάμποσα Next) θα σας φτιάξει τον DDL κώδικα που φτιάχνει τη ΒΔ 9, δηλαδή τις εντολές που σας έφαγαν προηγουμένως το χρόνο σας. Βέβαια, αν δείτε τον κώδικα που φτιάχτηκε αυτόματα θα δείτε ότι είναι λεπτομερειακός και λίγο φανφαρόνικος, αλλά παρόλα αυτά κατανοητός και, βέβαια, δίχως τα εκνευριστικά συντακτικά σφάλματα που κάνατε στην πληκτρολόγηση των DDL εντολών. Χώρια η πλάκα τώρα, η δυνατότητα να κινούμαστε από ένα διάγραμμα στον κώδικα DDL και αντίστροφα είναι πολύ χρήσιμη γιατί μας επιτρέπει να βλέπουμε τη ΒΔ με δυο οπτικές. Άσε που υπάρχει και η δυνατότητα να γίνονται οι επιθυμητές αλλαγές στο μοντέλο της ΒΔ και μέσω της λειτουργίας Synchronize Model With Database να γίνεται έλεγχος σε τι διαφέρουν και να παράγεται αυτόματα ο κώδικας με τις απαραίτητες ALTER TABLE. Για παράδειγμα, αν προσθέσουμε ένα πεδίο στον πίνακα istoriko στο διάγραμμα που φαίνεται στην Εικόνα 12 και εκτελέσουμε τη λειτουργία του συγχρονισμού θα παράγει τον κώδικα ALTER TABLE `mageiriki`.`istoriko` ADD COLUMN `new_column` VARCHAR(45) NULL DEFAULT NULL AFTER `protaseis`; Νομίζω ότι με αυτή την μικρή περιγραφή των διαγραμμάτων κατέστρεψα όλη τη χρησιμότητα του να μάθει κανείς να γράφει DDL. Ωραίος τρόπος να κλείσει το κεφάλαιο! 9 Reverse engineer: από το τελικό αποτέλεσμα (κώδικα) στην αρχική προδιαγραφή. Forward engineer: από την προδιαγραφή στον κώδικα που την υλοποιεί. igaviotis@gmail.com 8-Νοε-16 Σελ. 7