Κεφάλαιο 9 Συναθροίσεις
Σύνοψη
Στο παρόν κεφάλαιο θα παρουσιαστούν ερωτήματα συνάθροισης χρησιμοποιώντας τις βασικές
συναρτήσεις MAX, MIN, AVG, SUM και COUNT. Θα παρουσιαστεί η χρήση της ομαδοποίησης
εγγραφών με την εντολή GROUP BY και της επιλογής ομάδων με την εντολή HAVING. Τέλος θα
γίνει εκτενέστερη αναφορά της εντολής ταξινόμησης αποτελεσμάτων ORDER BY.
Προαπαιτούμενη γνώση
Προαπαιτούμενα για την κατανόηση των εννοιών αλλά και την εργαστηριακή άσκηση είναι τα
ακόλουθα:
Η ύλη του Κεφ. 8.
9.1 Εισαγωγικές Έννοιες
Στη συνέχεια θα παρουσιαστεί μία σύνοψη του τρόπου σύνταξης ερωτημάτων συνάθροισης
(aggregation queries). Στα ερωτήματα-παραδείγματα που θα δοθούν θα χρησιμοποιηθούν οι
παρακάτω πίνακες με τις αντίστοιχες εγγραφές τους.
Customer
cid afm address name sname dateOfBirth
1 077783234 56 Baltetsiou st. Kostas Kostantinou 1990-10-30
2 175783239 107 Diakou st. Eleni Kostantinou 1985-11-02
3 095111139 12 Rodon st. Maria Papantoniou 1967-03-20
Phones
cid pnum
1 2231011111
1 6944444444
2 2103333333
Account
accid balance dateOfCreation
100 10000 2014-01-01
200 40000 2013-02-01
300 30000 2005-03-10
400 20000 2005-03-10
Owns
cid accid
1 100
2 200
1 300
1 400
Action
accid actid amount type dateOfAction
100 1 500 2 2014-01-14
200 1 500 2 2014-01-14
200 2 1000 2 2015-11-11
200 3 2000 1 2015-12-11
300 1 1000 2 2015-11-11
300 2 500 1 2015-12-11
Transfer
accidSource actidSource accidDest actidDest
200 1 100 1
300 1 200 2
9.1.1 Συναρτήσεις συνάθροισης
Όλες οι συναρτήσεις συνάθροισης χρησιμοποιούνται στο SELECT τμήμα ενός ερωτήματος.
Παίρνουν ως όρισμα μία έκφραση και επιστρέφουν ως αποτέλεσμα μία τιμή. Η τιμή αυτή
υπολογίζεται ως εξής: υπολογίζεται πρώτα η τιμή της έκφρασης για κάθε γραμμή του πίνακα
και στη συνέχεια εφαρμόζεται η συνάρτηση που περιγράφεται για το σύνολο των τιμών που
υπολογίστηκαν. Αν σε κάποια γραμμή η έκφραση αποτιμείται σε NULL, η συγκεκριμένη
γραμμή δε μετέχει στον υπολογισμό.
Οι βασικές συναρτήσεις συνάθροισης αριθμητικών τιμών στην SQL είναι οι εξής:
SUM(έκφραση): επιστρέφει το άθροισμα.
AVG(έκφραση): επιστρέφει το μέσο όρο.
MAX(έκφραση): επιστρέφει τη μέγιστη τιμή.
MIN(έκφραση): επιστρέφει την ελάχιστη τιμή.
STDDEV_POP(έκφραση): επιστρέφει την τυπική απόκλιση.
COUNT(έκφραση): επιστρέφει το πλήθος γραμμών.
Ακολουθούν παραδείγματα. Για περισσότερες πληροφορίες αλλά και πλήρη κατάλογο των
υποστηριζόμενων από τη MySQL συναρτήσεων, ο αναγνώστης μπορεί να απευθυνθεί στο
Κεφ.12.20.1 του εγχειριδίου της MySQL5.7.
Ερώτημα 1. Βρες το άθροισμα, το μέσο όρο, την τυπική απόκλιση, το μεγαλύτερο και το
μικρότερο ποσό χρημάτων στους λογαριασμούς.
SELECT SUM(balance) AS sum, AVG(balance) AS avg, STDDEV_POP(balance) AS stddev,
MAX(balance) AS max, MIN(balance) AS min
FROM Account;
Επιστρέφει:
sum avg stddev max min
100000 25000 11180.339887498949 40000 10000
Ερώτημα 2. Βρες το πλήθος των εγγραφών στον Customer για τις οποίες υπάρχουν διευθύνσεις.
SELECT COUNT(address)
FROM Customer;
Επιστρέφει: 3.
Αν υποθέσουμε το ακόλουθο στιγμιότυπο του πίνακα Customer:
Customer
cid afm address name sname dateOfBirth
1 077783234 56 Baltetsiou st. Kostas Kostantinou 1990-10-30
2 175783239 107 Diakou st. Eleni Kostantinou 1985-11-02
3 095111139 NULL Maria Papantoniou 1967-03-20
τότε θα επιστρέφονταν 2 καθώς η γραμμή με cid=3 έχει NULL στο address.
Για να μετρήσουμε τις γραμμές στον πίνακα Customer (ασχέτως αν σε κάποια πεδία υπάρχουν
NULL τιμές) μπορούμε να δώσουμε:
SELECT COUNT(*)
FROM Customer;
ή
SELECT COUNT(TRUE)
FROM Customer;
ή
SELECT COUNT(1)
FROM Customer;
Εκ των τριών η συνηθέστερα χρησιμοποιούμενη μορφή είναι η πρώτη με την οποία στην ουσία
λέμε ότι μία γραμμή θα «μετρηθεί» στο αποτέλεσμα αν κάποιο από τα πεδία της είναι διάφορο
του NULL.
Ερώτημα 3 (απαλοιφή διπλότυπων). Βρες το πλήθος των διαφορετικών ον/μων των πελατών.
SELECT COUNT(DISTINCT name, sname)
FROM Customer;
Το ερώτημα θα επιστρέψει το πλήθος των εγγραφών με διαφορετικά ον/μα (στο παράδειγμα 3).
Αν δίναμε:
SELECT COUNT(DISTINCT sname)
FROM Customer;
θα λαμβάναμε ως απάντηση το 2, καθώς υπάρχουν δύο εγγραφές με ξεχωριστά επώνυμα.
9.1.2 Χρήση ομαδοποίησης
Ας υποθέσουμε ότι θέλουμε να γραφεί query για το παρακάτω ερώτημα:
Ερώτημα 4. Βρες το σύνολο των χρημάτων που έχει καταθέσει κάθε πελάτης.
Έστω ότι επιχειρούμε να δώσουμε την ακόλουθη query:
SELECT cid, SUM(balance)
FROM Owns INNER JOIN Account USING (accid);
Η απάντηση που παίρνουμε είναι η εξής:
cid SUM(balance)
1 100000
Αυτό που συμβαίνει είναι ότι μετά την εκτέλεση του FROM ο πίνακας που προκύπτει είναι ο
εξής:
accid cid balance dateOfCreation
100 1 10000 2014-01-01
200 2 40000 2013-02-01
300 1 30000 2005-03-10
400 1 20000 2005-03-10
Σε αυτόν τον πίνακα υπολογίζεται το SUM(balance) για όλες τις εγγραφές και επιστρέφεται ως
αποτέλεσμα (100000) μαζί με το cid της πρώτης εγγραφής. Γίνεται λοιπόν αντιληπτό ότι
χρειάζεται ένας τρόπος να ομαδοποιηθούν οι εγγραφές (ως προς cid) και να υπολογιστεί το
SUM ξεχωριστά για κάθε ομάδα. Αυτό γίνεται με χρήση του GROUP BY ως εξής:
SELECT cid, SUM(balance)
FROM Owns INNER JOIN Account USING (accid)
GROUP BY cid;
Η απάντηση που παίρνουμε είναι η εξής:
cid SUM(balance)
1 60000
2 40000
Στο παραπάνω ερώτημα αφού φτιαχτεί ο τελικός πίνακας του FROM οι εγγραφές
ομαδοποιούνται βάσει της τιμής του cid. Έτσι θα υπάρξουν δύο ομάδες η μία θα έχει τις
εγγραφές που έχουν cid=1 (3 εγγραφές) και η άλλη αυτές που έχουν cid=2 (1 εγγραφή). Το
SELECT θα επιστρέψει ένα αποτέλεσμα για κάθε ομάδα.
Ερώτημα 5. Βρες το σύνολο των χρημάτων που κινήθηκαν ανά λογαριασμό για τους
λογαριασμούς που το άθροισμα των κινήσεών τους είναι >1000.
Ένα καταφανές λάθος θα ήταν να δίναμε:
SELECT accid, SUM(amount)
FROM Action
WHERE amount>1000
GROUP BY accid;
Με τον πίνακα Action ως ακολούθως:
Action
accid actid amount type dateOfAction
100 1 500 2 2014-01-14
200 1 500 2 2014-01-14
200 2 1000 2 2015-11-11
200 3 2000 1 2015-12-11
300 1 1000 2 2015-11-11
300 2 500 1 2015-12-11
η απάντηση που παίρνουμε είναι η εξής:
accid SUM(amount)
200 2000
Ο λόγος είναι γιατί από τον πίνακα Account φιλτράρονται οι εγγραφές σύμφωνα με το WHERE
και ως εκ τούτου μένει μόνο μία εγγραφή (η 4η). Στη συνέχεια εφαρμόζεται το GROUP BY και
κατόπιν το SELECT.
Το προηγούμενο SQL ερώτημα ήταν εμφανώς λάθος καθώς η εκφώνηση ζητάει το άθροισμα
και όχι η κάθε μία πράξη να είναι άνω των 1000. Αν γράψουμε:
SELECT accid, SUM(amount)
FROM Action
WHERE SUM(amount)>1000
GROUP BY accid;
θα λάβουμε ως απάντηση το εξής μήνυμα λάθους:
ERROR 1111 (HY000): Invalid use of group function
Ο λόγος είναι γιατί η συνθήκη του WHERE εφαρμόζεται ανά εγγραφή και ως εκ τούτου η
χρήση του SUM(amount) δεν είναι ορθή καθώς δεν μπορεί να καθοριστεί η ομάδα εγγραφών
στην οποία θα επενεργήσει. Χρειάζεται λοιπόν ένας τρόπος να μπορούμε να δηλώνουμε σε μία
SQL query κριτήρια βάσει των οποίων μπορεί να φιλτραριστούν ολόκληρες ομάδες εγγραφών.
Τον τρόπο αυτό προσφέρει η εντολή HAVING. Στο παράδειγμα η ορθή query θα ήταν ως εξής:
SELECT accid, SUM(amount)
FROM Action
GROUP BY accid
HAVING SUM(amount)>1000;
και θα επέστρεφε:
accid SUM(amount)
200 3500
300 1500
καθώς η ομάδα με accid=100 δεν πληροί το HAVING αφού έχει σύνολο χρηματικών κινήσεων
500.
Συνοψίζοντας, η βασική μορφή ενός ερωτήματος συνάθροισης (aggregation query) έχει ως
ακολούθως:
SELECT f1, f2, .., fn, a1, a2,..,am
FROM T1, T2, …. Tk
WHERE d
GROUP BY a1, a2, ..,am
HAVING p;
Η παραπάνω query εκτελείται (συντακτικά) ως εξής: πρώτα σχηματίζεται ο τελικός πίνακας στο
FROM. Στη συνέχεια οι εγγραφές του πίνακα που προκύπτει φιλτράρονται βάσει της λογικής
συνθήκης d στο WHERE. Οι εγγραφές που απομένουν ομαδοποιούνται βάσει των πεδίων
a1,..,am. Από τις ομάδες που προκύπτουν κρατούνται οι ομάδες που πληρούν τη λογική
συνθήκη p του HAVING. Για κάθε μία από τις ομάδες η SELECT επιστρέφει μία εγγραφή που
έχει τα αποτελέσματα των f1,.,fn συναθροίσεων και τις αντίστοιχες τιμές στα πεδία
ομαδοποίησης. Σημειώνουμε ότι δεν είναι απαραίτητο αν και είναι σύνηθες στο SELECT να
επιστρέφονται τα πεδία ομαδοποίησης μαζί με τις συναθροίσεις. Επίσης η συνθήκη στο
HAVING τυπικά πρέπει να έχει συγκρίσεις που εμπλέκουν συναθροίσεις. Για παράδειγμα, το
HAVING accid<300 αν και επιτρεπτό δεν είναι δόκιμο καθώς η συνθήκη accid<300 μπορεί να
υπολογιστεί στο επίπεδο των εγγραφών (ως μέρος του WHERE) και όχι των ομάδων.
9.1.3 Εμφωλευμένα ερωτήματα και συναθροίσεις
Στη συνέχεια δείχνουμε μερικά παραδείγματα ερωτημάτων στα οποία θέλουμε να βρούμε
εγγραφές ενός πίνακα που πληρούν κριτήριο που εμπλέκει συναθροίσεις.
Ερώτημα 6. Βρες τους λογαριασμούς με υπόλοιπα μεγαλύτερα του μέσου όρου υπολοίπων.
1ος τρόπος
Βρίσκουμε το μέσο όρο των υπολοίπων σε εμφωλευμένο και στη συνέχεια προσθέτουμε αυτό
το πεδίο στον πίνακα Account (με cross join). Επιλέγουμε τις εγγραφές που έχουν υπόλοιπο
μεγαλύτερο του μέσου όρου.
SELECT Account.*
FROM Account CROSS JOIN (SELECT AVG(balance) AS avb
FROM Account) T1
WHERE Account.balance>T1.avb;
2ος τρόπος
Με εμφωλευμένο στο WHERE.
SELECT *
FROM Account
WHERE Account.balance > (SELECT AVG(balance) FROM Account);
Ερώτημα 7. Βρες τους λογαριασμούς που έχουν σύνολο χρηματικών κινήσεων μεγαλύτερο ή
ίσο με το μέσο όρο.
SELECT accid
FROM Action
GROUP BY accid
HAVING SUM(amount) >= (SELECT AVG(amount) FROM Action);
Επιστρέφει:
accid
200
300
Σημείωση: Εάν γράφαμε την προηγούμενη query ως εξής:
SELECT accid
FROM Action
GROUP BY accid
HAVING SUM(amount) >= AVG(amount);
θα επιστρέφονταν:
accid
100
200
300
καθώς το AVG υπολογίζεται για κάθε ομάδα ξεχωριστά, επομένως είναι φυσικό όλοι οι
λογαριασμοί να πληρούν τη συνθήκη ότι το άθροισμα των κινήσεών τους είναι μεγαλύτερο ή
ίσο από το μέσο όρο τους.
9.1.4 Χρήση των ORDER BY και LIMIT
Μπορούμε να επιστρέψουμε ταξινομημένα τα αποτελέσματα ενός ερωτήματος με χρήση της
ORDER BY. Η σύνταξη είναι:
ORDER BY <πεδίο> [ASC|DESC], ..
Με άλλα λόγια μετά την ORDER BY ακολουθεί ένα ή περισσότερα πεδία στα οποία θα γίνει η
ταξινόμηση των τελικών αποτελεσμάτων χωρισμένα με κόμμα. Σε κάθε πεδίο μπορεί
προαιρετικά να επιλεγεί η ταξινόμηση να γίνει σε αύξουσα σειρά (ASC) ή φθίνουσα (DESC).
Σημείωση: Η χρήση της ORDER BY εμπλέκει ταξινόμηση που στη γενικότερη περίπτωση
(εξωτερική ταξινόμηση) είναι αρκετά ακριβή πράξη. Κατά συνέπεια η χρήση της πρέπει να
γίνεται μόνο όταν είναι απολύτως επιβεβλημένη. Περισσότερες πληροφορίες περιλαμβάνονται
στο Κεφ. 9.2.1.15 του εγχειριδίου της MySQL5.7.
Αν δεν επιλεγεί κανένας προσδιοριστής η σειρά ταξινόμησης στο συγκεκριμένο πεδίο θα είναι
αύξουσα.
Είναι δυνατόν να περιορίσουμε το πλήθος των εγγραφών που επιστρέφονται στο αποτέλεσμα με
χρήση της LIMIT. Η σύνταξη είναι:
LIMIT [<αριθμόςΠαραλειπόμενων>,] αριθμόςΕμφανιζόμενων
Για παράδειγμα δίνοντας: LIMIT 2, 5 σημαίνει ότι από τα αποτελέσματα πρέπει να
παραληφθούν οι 2 πρώτες εγγραφές και να επιστραφούν οι επόμενες 5.
Σημείωση: Η χρήση της LIMIT συνήθως οδηγεί σε γρηγορότερη εκτέλεση ερωτημάτων καθώς
στη γενικότερη περίπτωση δε χρειάζεται να υπολογιστεί όλο το αποτέλεσμα ενός ερωτήματος.
Αντίθετα όταν έχουν υπολογιστεί στο αποτέλεσμα τόσες εγγραφές όσες προδιαγράφονται στο
LIMIT η εκτέλεση του ερωτήματος μπορεί να περαιωθεί. Περισσότερες πληροφορίες
περιλαμβάνονται στο Κεφ. 9.2.1.19 του εγχειριδίου της MySQL5.7.
Σημείωση 2: Στη σύνταξη SQL ερωτήματος το ORDER BY έπεται του HAVING και το LIMIT
του ORDER BY.
Παράδειγμα 1:
SELECT accid, SUM(amount) AS sum
FROM Action
WHERE amount<2000
GROUP BY accid
HAVING COUNT(*)>1
ORDER BY accid DESC
LIMIT 1;
Επιστρέφει:
accid sum
300 1500
Παράδειγμα 2:
SELECT accid, SUM(amount) AS sum
FROM Action
GROUP BY accid
ORDER BY COUNT(*) DESC
LIMIT 2,1;
Το αποτέλεσμα πριν το LIMIT είναι (ταξινομημένο ως προς πλήθος εγγραφών ανά ομάδα):
accid sum
200 3500
300 1500
100 500
Το τελικό αποτέλεσμα που θα επιστραφεί μετά το LIMIT:
accid sum
100 500
9.2 Παράδειγμα Εργαστηριακής Άσκησης
Εκφώνηση: Στην Βάση Δεδομένων του ΑΕΙ της εργαστηριακής άσκησης 4 θα πρέπει να
εκτελέσετε τις ακόλουθες ενέργειες:
1. Εμφανίστε τη μέση βαθμολογία για το σύνολο των μαθημάτων.
2. Εμφανίστε τη μέση βαθμολογία σε κάθε μάθημα ξεχωριστά.
3. Εμφανίστε τα μαθήματα (κωδικούς), τη συμμετοχή και τη μέση
βαθμολογία σε όλα τα μαθήματα που είχαν συμμετοχή μεγαλύτερη
από το μάθημα «Baseis Dedomenwn».
Ζητούμενα: Συναρτήσεις Συνάθροισης COUNT, AVG, MAX, MIN, SUM, GROUP BY.
Ζητούμενο 1: Εμφανίστε τη μέση βαθμολογία για το σύνολο των μαθημάτων.
Η Εικόνα 9.1 δείχνει τα περιεχόμενα του πίνακα των βαθμολογιών.
Εικόνα 9.1: Οι κωδικοί των μαθημάτων μαζί με τους βαθμούς των φοιτητών.
Για το ζητούμενο δίνουμε:
SELECT AVG(E.Grade)
FROM Exam as E;
Το αποτέλεσμα φαίνεται στην Εικόνα 9.2.
Εικόνα 9.2: Παράδειγμα συνάθροισης χωρίς GROUP BY.
Ζητούμενο 2: Εμφανίστε τη μέση βαθμολογία σε κάθε μάθημα ξεχωριστά.
Για να βρούμε τη μέση βαθμολογία ανά μάθημα θα ομαδοποιήσουμε τις εγγραφές ως προς cid.
Δίνουμε:
SELECT E.Cid, AVG(E.Grade)
FROM Exam as E
GROUP BY E.Cid;
Το αποτέλεσμα φαίνεται στην Εικόνα 9.3.
Εικόνα 9.3: Παράδειγμα συνάθροισης με GROUP BY.
Ζητούμενο 3: Εμφανίστε τα μαθήματα (κωδικούς), τη συμμετοχή και τη μέση βαθμολογία σε
όλα τα μαθήματα που είχαν συμμετοχή μεγαλύτερη από το μάθημα «Baseis Dedomenwn».
Δίνουμε:
SELECT E.Cid, C.Title, COUNT(E.Grade) as Summetoxi, AVG(E.Grade)
FROM Exam as E, Course as C
WHERE E.Cid=C.Cid
GROUP BY E.Cid
HAVING Summetoxi>
(SELECT COUNT(E.Grade)
FROM Exam as E, Course as C
WHERE C.Title='Baseis Dedomenwn' AND E.Cid=C.Cid);
Παράδειγμα αποτελέσματος φαίνεται στην Εικόνα 9.4.
Εικόνα 9.4: Το μάθημα που έχει συμμετοχή μεγαλύτερη από τις Βάσεις Δεδομένων είναι οι Δομές
Δεδομένων.
9.3 Άλυτες Εργαστηριακές Ασκήσεις
Άσκηση 1
Χρησιμοποιώντας τη βάση δεδομένων που δημιουργήσατε στο κεφάλαιο 4 (άλυτη άσκηση 1),
απαντήστε στα ακόλουθα ερωτήματα:
1. Εμφανίστε τη μέση προϋπηρεσία για το σύνολο των εργαζομένων.
2. Εμφανίστε τη μέση προϋπηρεσία εργαζομένων για κάθε εταιρία.
3. Εμφανίστε το διευθυντή με τα περισσότερα έτη εμπειρίας σε κάθε εταιρία.
4. Εμφανίστε τους εργαζόμενους που έχουν προϋπηρεσία μεγαλύτερη από το μέσο όρο.
Άσκηση 2
Χρησιμοποιώντας τη βάση δεδομένων που δημιουργήσατε στο κεφάλαιο 4 (άλυτη άσκηση 2),
απαντήστε στα ακόλουθα ερωτήματα:
1. Εμφανίστε τις ταινίες μαζί με τη μέση βαθμολογία της κάθε ταινίας.
2. Βρείτε την ταινία με τη μέγιστη βαθμολογία και την ταινία με την ελάχιστη.
3. Εμφανίστε τις ταινίες μαζί με τον αριθμό των θεατών που παρακολούθησαν την κάθε
ταινία.
4. Εμφανίστε τους σκηνοθέτες των ταινιών που τις παρακολούθησαν περισσότεροι θεατές
από αυτούς που παρακολούθησαν την ταινία ‘Spectre’.
Άσκηση 3
Χρησιμοποιώντας τη βάση δεδομένων που δημιουργήσατε στο κεφάλαιο 4 (άλυτη άσκηση 3),
απαντήστε στα ακόλουθα ερωτήματα:
1. Εμφανίστε τους παίκτες με τις περισσότερες και λιγότερες νίκες για το 2015.
2. Βρείτε τη μέση ηλικία των παικτών του τουρνουά ‘Australia Open’.
3. Εμφανίστε τα τουρνουά που είχαν συμμετοχή μεγαλύτερη από 3 αθλητές τα έτη 2013-
2015.
4. Εμφανίστε τα τουρνουά που έχουν τα περισσότερα χρόνια ίδρυσης.
Βιβλιογραφία/Αναφορές
R. Elmasri & S.B. Navathe "Θεμελιώδεις Αρχές Συστημάτων ΒΔ - 4η Έκδοση". Κεφάλαια 8, 9
R. Ramakrishnan & J. Gehrke. 2002. Database Management Systems (3 ed.). McGraw-Hill, Inc.,
New York, NY, USA. Κεφάλαιο 5.
ORDER BY. http://www.w3schools.com/sql/sql_orderby.asp
GROYP BY.
http://www.w3schools.com/sql/sql_groupby.asp
http://dev.mysql.com/doc/refman/5.7/en/group-by-functions-and-modifiers.html
HAVING. http://www.w3schools.com/sql/sql_having.asp
LIMIT. http://www.mysqltutorial.org/mysql-limit.aspx
SUM. http://www.w3schools.com/sql/sql_func_sum.asp
MIN. http://www.w3schools.com/sql/sql_func_min.asp
MAX. http://www.w3schools.com/sql/sql_func_max.asp
AVG. http://www.w3schools.com/sql/sql_func_avg.asp
COUNT. http://www.w3schools.com/sql/sql_func_count.asp
EXISTS. http://dev.mysql.com/doc/refman/5.7/en/exists-and-not-exists-subqueries.html