Η κανονικοποίηση βάσεων δεδομένων συνοψίζεται σε μία πρόταση: "Το ίδιο γεγονός να αποθηκεύεται σε ένα μόνο σημείο." Ο συστηματικός τρόπος για να το πετύχουμε αυτό είναι το 1NF → 2NF → 3NF.
Γιατί είναι σημαντικό; — Αν η ίδια πληροφορία είναι σκορπισμένη σε πολλές γραμμές, τη στιγμή που διορθώσεις μόνο τη μία και αφήσεις τις υπόλοιπες ως έχουν, τα δεδομένα γίνονται αντιφατικά. Αυτό λέγεται ανωμαλία ενημέρωσης. Δες το στην πράξη παρακάτω.
Αν αλλάξει το εργαστήριο του Prof.A, σε έναν μη κανονικοποιημένο πίνακα πρέπει να διορθώσεις χειροκίνητα και τις 2 γραμμές. Αν ξεχάσεις έστω και μία, προκύπτει η αντίφαση: "ο Prof.A είναι και στο Room 301 και στο Room 502". Σε έναν πίνακα 3NF, αρκεί να αλλάξεις μόνο 1 γραμμή.
Αυτή είναι όλη η ουσία της κανονικοποίησης — όλα τα υπόλοιπα είναι κανόνες για να αποφασίζεις "ποια στήλη πρέπει να μπει πού, ώστε να αποθηκεύεται μόνο μία φορά".
Συναρτησιακή εξάρτηση — το κριτήριο της κανονικοποίησης
Η κανονικοποίηση εξετάζει "από τι εξαρτάται αυτή η στήλη". Αυτό λέγεται συναρτησιακή εξάρτηση.
Για παράδειγμα, το student_id → student_name σημαίνει "αν ξέρεις το ID του φοιτητή, τότε καθορίζεται και το όνομά του".
Στο παρακάτω διάγραμμα, πρόσεξε τα χρώματα των βελών:
- Πράσινο = εξάρτηση από ολόκληρο το κλειδί (σωστό)
- Κίτρινο = εξάρτηση μόνο από μέρος του κλειδιού (μερική εξάρτηση → παραβίαση 2NF)
- Κόκκινο διακεκομμένο = εξάρτηση μέσω μη-κλειδικής στήλης (μεταβατική εξάρτηση → παραβίαση 3NF)
Το να εξαφανίσεις τα κίτρινα και τα κόκκινα βέλη = να σπάσεις τον πίνακα σε μικρότερους πίνακες = κανονικοποίηση.
1NF → 2NF → 3NF με μια ματιά
Παρακάτω μπορείς να κάνεις κλικ βήμα-βήμα και να δεις πώς διασπάται ο πίνακας σε κάθε στάδιο. Τα κόκκινα κελιά δείχνουν ότι "αυτή η στήλη δεν πρέπει να βρίσκεται εδώ".
1NF: μία τιμή σε κάθε κελί
Αν βάλεις πολλές τιμές σε ένα κελί, όπως MATH101, CS102, τότε ούτε σωστή αναζήτηση μπορείς να κάνεις ούτε JOIN.
Κανόνας: κάθε κελί πρέπει να είναι ατομικό, δηλαδή να περιέχει μία μόνο τιμή.
Το ίδιο πρόβλημα υπάρχει και με στήλες όπως phone1, phone2, phone3 — απλώς έχεις κρύψει τις τιμές μέσα στα ονόματα των στηλών.
2NF: αφαίρεση μερικών εξαρτήσεων
Σε έναν πίνακα με σύνθετο κλειδί (student_id, course_id), το student_name εξαρτάται μόνο από το student_id. Δεν εξαρτάται από ολόκληρο το κλειδί, αλλά μόνο από ένα μέρος του — αυτό είναι μερική συναρτησιακή εξάρτηση.
Κανόνας: κάθε μη-κλειδική στήλη πρέπει να εξαρτάται από ολόκληρο το κλειδί.
Αν υπάρχει μερική εξάρτηση, αφαιρείς τη συγκεκριμένη στήλη και τη βάζεις σε ξεχωριστό πίνακα.
Αν το κλειδί είναι μία μόνο στήλη; Τότε η μερική εξάρτηση είναι εκ των πραγμάτων αδύνατη, άρα αν είσαι σε 1NF, είσαι αυτόματα και σε 2NF.
3NF: αφαίρεση μεταβατικών εξαρτήσεων
Στον πίνακα Courses, το instructor_office δεν εξαρτάται άμεσα από το course_id (το κλειδί). Περνά από τη διαδρομή course_id → instructor_id → instructor_office — δηλαδή μια μη-κλειδική στήλη εξαρτάται από άλλη μη-κλειδική στήλη. Αυτό είναι μεταβατική εξάρτηση.
Κανόνας: οι μη-κλειδικές στήλες πρέπει να εξαρτώνται μόνο μέσω του κλειδιού. Δεν πρέπει να μεσολαβεί άλλη μη-κλειδική στήλη.
Λύση: μεταφέρεις το instructor_office σε έναν ξεχωριστό πίνακα Instructors.
Αντί για αποστήθιση, κράτα μία ερώτηση
"Κάθε μη-κλειδικό γνώρισμα πρέπει να εξαρτάται από το κλειδί, ολόκληρο το κλειδί, και μόνο το κλειδί."
Αυτή η πρόταση τα λέει όλα:
- το κλειδί = 1NF (το γεγονός πρέπει να μπορεί να ταυτοποιηθεί από κλειδί)
- ολόκληρο το κλειδί = 2NF (εξάρτηση από όλο το κλειδί, όχι από μέρος του)
- και μόνο το κλειδί = 3NF (άμεση εξάρτηση από το κλειδί, όχι μέσω μη-κλειδικής στήλης)
Αν σε εξέταση πέσει άσκηση κανονικοποίησης, κάνε μόνο αυτή την ερώτηση: "Από ποιο πράγμα εξαρτάται αυτή η στήλη; Από ολόκληρο το κλειδί, από μέρος του κλειδιού ή από μη-κλειδική στήλη;"
Συχνά λάθη
"Αν φτάσω μέχρι 3NF, τελείωσα" — Η 3NF είναι ένα πολύ καλό σημείο εκκίνησης, όχι το τέλος. Σε κάποιες περιπτώσεις χρειάζεται η πιο αυστηρή BCNF, ενώ σε άλλες γίνεται σκόπιμα αποκανονικοποίηση για λόγους απόδοσης.
"Όσο περισσότερους πίνακες σπάσω, τόσο καλύτερα" — Ο στόχος της κανονικοποίησης δεν είναι να αυξήσεις τον αριθμό των πινάκων, αλλά να κάνεις έτσι ώστε "κάθε πίνακας να είναι υπεύθυνος μόνο για ένα είδος γεγονότος". Αν σπάσεις πίνακες χωρίς πραγματικό λόγο (δηλαδή χωρίς σχέση εξάρτησης), απλώς κάνεις τα JOIN πιο περίπλοκα.
"Αν είναι NoSQL, δεν χρειάζεται κανονικοποίηση" — Η κανονικοποίηση είναι όρος που χρησιμοποιείται κυρίως στις σχεσιακές βάσεις δεδομένων, αλλά το πρόβλημα της "διατήρησης συνέπειας σε πλεονάζοντα δεδομένα" υπάρχει σε κάθε DB. Ακόμα κι όταν κάνεις αποκανονικοποίηση, πρέπει να ξέρεις "τι ακριβώς θυσιάζεις".
Δοκίμασέ το στο δικό σου σχήμα
Διάλεξε έναν πίνακα από το δικό σου project και κάνε τα εξής:
- Γράψε ποιο είναι το κλειδί.
- Έλεγξε αν κάθε μη-κλειδική στήλη εξαρτάται από ολόκληρο το κλειδί ή μόνο από μέρος του. → Αν είναι μόνο από μέρος του, τότε υπάρχει παραβίαση 2NF.
- Έλεγξε αν κάποια μη-κλειδική στήλη εξαρτάται από άλλη μη-κλειδική στήλη. → Αν ναι, τότε υπάρχει παραβίαση 3NF.
Μόνο με αυτά τα 3 βήματα μπορείς να εντοπίσεις τα περισσότερα προβλήματα κανονικοποίησης.
Χρειάζεσαι βοήθεια με μια άσκηση;
Ανέβασε την ερώτησή σου και πάρε επαληθευμένη λύση βήμα-βήμα σε δευτερόλεπτα.
Άνοιξε το GPAI Solver →