Βάσεις δεδομένων
Πίνακες, κλειδιά, σχέσεις και ερωτήματα SQL.
45 λεπτά
Θα μάθεις να
- Να εξηγείς γιατί μια βάση δεδομένων αποφεύγει τον πλεονασμό και την ασυνέπεια.
- Να περιγράφεις πίνακες, εγγραφές, πεδία, πρωτεύοντα και ξένα κλειδιά.
- Να σχεδιάζεις σχέσεις ένα προς πολλά και πολλά προς πολλά με πίνακες.
- Να γράφεις ερωτήματα SQL με SELECT, WHERE, ORDER BY και JOIN.
Γιατί όχι ένα λογιστικό φύλλο
Φαντάσου ότι το σχολείο κρατά τους μαθητές σε ένα λογιστικό φύλλο, με μια στήλη για το τμήμα και δίπλα τον υπεύθυνο καθηγητή του τμήματος. Για κάθε μαθητή του Β1, το όνομα του υπεύθυνου γράφεται ξανά. Αν αλλάξει υπεύθυνος, πρέπει να διορθωθούν είκοσι πέντε γραμμές, και αρκεί να ξεχαστεί μία για να λέει το αρχείο δύο διαφορετικά πράγματα.
Αυτά είναι τα δύο κλασικά προβλήματα:
- Πλεονασμός (redundancy): η ίδια πληροφορία αποθηκεύεται πολλές φορές.
- Ασυνέπεια (inconsistency): τα αντίγραφα κάποια στιγμή διαφωνούν μεταξύ τους.
Μια βάση δεδομένων οργανώνει τα δεδομένα ώστε κάθε γεγονός να γράφεται μία φορά. Το πρόγραμμα που τη διαχειρίζεται λέγεται Σύστημα Διαχείρισης Βάσεων Δεδομένων (ΣΔΒΔ, DBMS). Φροντίζει ακόμα να μπορούν πολλοί χρήστες να δουλεύουν ταυτόχρονα, να μη χάνονται δεδομένα σε μια διακοπή ρεύματος, και να βλέπει ο καθένας μόνο ό,τι επιτρέπεται.
Το σχεσιακό μοντέλο
Το 1970 ο Edgar Codd πρότεινε το σχεσιακό μοντέλο (relational model), που κυριαρχεί από τότε. Τα δεδομένα οργανώνονται σε πίνακες:
| Όρος | Τι είναι | Άλλα ονόματα |
|---|---|---|
| Πίνακας (table) | ένα σύνολο ομοειδών στοιχείων, π.χ. όλοι οι μαθητές | σχέση (relation) |
| Εγγραφή (record) | μία γραμμή, π.χ. ένας μαθητής | πλειάδα (tuple) |
| Πεδίο (field) | μία στήλη, π.χ. το όνομα | γνώρισμα (attribute) |
Κάθε πεδίο έχει τύπο δεδομένων: ακέραιος, πραγματικός, κείμενο, ημερομηνία. Το ΣΔΒΔ δεν δέχεται κείμενο σε πεδίο βαθμού.
Κλειδιά
Το πρωτεύον κλειδί (primary key) είναι ένα πεδίο που ξεχωρίζει μοναδικά κάθε εγγραφή: δεν επιτρέπεται να επαναληφθεί ούτε να μείνει κενό. Το όνομα δεν κάνει για κλειδί, γιατί δύο μαθητές μπορεί να λέγονται Μαρία. Γι' αυτό συνήθως χρησιμοποιείται ένας αύξων αριθμός, το id.
Το ξένο κλειδί (foreign key) είναι ένα πεδίο που παίρνει τιμές από το πρωτεύον κλειδί άλλου πίνακα. Έτσι συνδέονται οι πίνακες.
Πίνακας classes (τμήματα)
| id | name |
|---|---|
| 1 | Β1 |
| 2 | Β2 |
Πίνακας students (μαθητές)
| id | name | class_id | grade |
|---|---|---|---|
| 1 | Άννα | 1 | 18.5 |
| 2 | Νίκος | 1 | 14 |
| 3 | Μαρία | 2 | 19 |
| 4 | Γιώργος | 2 | 11.5 |
| 5 | Ελένη | 1 | 15.5 |
Το class_id είναι ξένο κλειδί προς το classes.id. Το όνομα του τμήματος γράφεται μία φορά, στον πίνακα classes. Αν αλλάξει, αλλάζει σε ένα σημείο.
Οι σχέσεις ανάμεσα σε πίνακες είναι τριών ειδών:
| Σχέση | Παράδειγμα | Πώς υλοποιείται |
|---|---|---|
| Ένα προς ένα (1:1) | μαθητής και ατομικό ντουλάπι | ξένο κλειδί με μοναδική τιμή |
| Ένα προς πολλά (1:N) | ένα τμήμα, πολλοί μαθητές | ξένο κλειδί στην πλευρά των «πολλών» |
| Πολλά προς πολλά (N:M) | μαθητές και όμιλοι: κάθε μαθητής σε πολλούς ομίλους, κάθε όμιλος με πολλούς μαθητές | τρίτος, ενδιάμεσος πίνακας με δύο ξένα κλειδιά |
Η γλώσσα SQL
Η SQL (Structured Query Language) είναι η τυποποιημένη γλώσσα των σχεσιακών βάσεων. Είναι δηλωτική: περιγράφεις ποια δεδομένα θέλεις, και το ΣΔΒΔ αποφασίζει πώς θα τα βρει.
Οι πίνακες του παραδείγματος φτιάχνονται έτσι:
CREATE TABLE classes (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE students (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
class_id INTEGER REFERENCES classes(id),
grade REAL
);
INSERT INTO classes VALUES (1, 'Β1'), (2, 'Β2');
Το βασικό ερώτημα έχει τέσσερα μέρη, πάντα με αυτή τη σειρά:
SELECT name, grade -- ποια πεδία
FROM students -- από ποιον πίνακα
WHERE grade >= 15 -- ποιες εγγραφές
ORDER BY grade DESC; -- με ποια σειρά
| name | grade |
|---|---|
| Μαρία | 19 |
| Άννα | 18.5 |
| Ελένη | 15.5 |
Το DESC σημαίνει φθίνουσα σειρά· χωρίς αυτό, η σειρά είναι αύξουσα (ASC). Το SELECT * επιστρέφει όλα τα πεδία.
Η σύζευξη (JOIN) ενώνει πίνακες μέσω των κλειδιών τους. Για να πάρεις το όνομα κάθε μαθητή μαζί με το όνομα του τμήματός του:
SELECT students.name, classes.name
FROM students
JOIN classes ON students.class_id = classes.id
WHERE classes.name = 'Β2';
| students.name | classes.name |
|---|---|
| Μαρία | Β2 |
| Γιώργος | Β2 |
Όταν δύο πίνακες έχουν πεδίο με το ίδιο όνομα, εδώ το name και το id, γράφεις μπροστά το όνομα του πίνακα για να μην υπάρχει αμφιβολία.
Η SQL υπολογίζει και συγκεντρωτικά μεγέθη, όπως COUNT, AVG, MAX, MIN και SUM. Με GROUP BY τα υπολογίζει χωριστά ανά ομάδα:
SELECT classes.name, COUNT(*), AVG(students.grade)
FROM students
JOIN classes ON students.class_id = classes.id
GROUP BY classes.name;
Το αποτέλεσμα είναι μία γραμμή ανά τμήμα: Β1 με μαθητές και μέσο όρο , Β2 με μαθητές και μέσο όρο .
Μπορείς να δοκιμάσεις όλα τα παραπάνω χωρίς να εγκαταστήσεις τίποτα, με τη βάση SQLite που περιέχει η Python:
import sqlite3
db = sqlite3.connect(":memory:") # μια βάση που ζει μόνο στη μνήμη
db.executescript("""
CREATE TABLE classes (id INTEGER PRIMARY KEY, name TEXT NOT NULL);
CREATE TABLE students (id INTEGER PRIMARY KEY, name TEXT NOT NULL,
class_id INTEGER REFERENCES classes(id), grade REAL);
INSERT INTO classes VALUES (1, 'Β1'), (2, 'Β2');
INSERT INTO students VALUES (1, 'Άννα', 1, 18.5), (2, 'Νίκος', 1, 14),
(3, 'Μαρία', 2, 19), (4, 'Γιώργος', 2, 11.5), (5, 'Ελένη', 1, 15.5);
""")
for row in db.execute("SELECT name, grade FROM students WHERE grade >= 15 ORDER BY grade DESC"):
print(row)
Λυμένα παραδείγματα
Παράδειγμα 1
Γράψε ερώτημα SQL που δίνει τα ονόματα και τους βαθμούς των μαθητών του Β1, από τον μεγαλύτερο βαθμό προς τον μικρότερο.
Δοκίμασέ το πρώτα. Μετά δες τη λύση.
Παράδειγμα 2
Μια δανειστική βιβλιοθήκη έχει μέλη και βιβλία. Κάθε μέλος δανείζεται πολλά βιβλία, και κάθε βιβλίο δανείζεται σε πολλά μέλη με τα χρόνια. Σχεδίασε τους πίνακες.
Δοκίμασέ το πρώτα. Μετά δες τη λύση.
Παράδειγμα 3
Ένα λογιστικό φύλλο έχει στήλες: μαθητής, τμήμα, υπεύθυνος τμήματος. Ποιο πρόβλημα έχει, και πώς το λύνει η βάση;
Δοκίμασέ το πρώτα. Μετά δες τη λύση.
Ασκήσεις
Πρώτα ερωτήσεις με επιλογές: διαλέγεις, πατάς «Έλεγξε» και μαθαίνεις αμέσως αν το βρήκες και γιατί. Στο τέλος, λίγες ασκήσεις χωρίς επιλογές — εκεί κρύβεται μόνο η απάντηση.
Τα κατάφερες;
- Να εξηγείς γιατί μια βάση δεδομένων αποφεύγει τον πλεονασμό και την ασυνέπεια.
- Να περιγράφεις πίνακες, εγγραφές, πεδία, πρωτεύοντα και ξένα κλειδιά.
- Να σχεδιάζεις σχέσεις ένα προς πολλά και πολλά προς πολλά με πίνακες.
- Να γράφεις ερωτήματα SQL με SELECT, WHERE, ORDER BY και JOIN.
Λύσε τις ασκήσεις πιο πάνω και θα δεις εδώ πού στέκεσαι.