ΣΤΟΧΟΣ Postgress Το 9ο εργαστήριο εισάγει τον/ην φοιτητή/τρια στη χρήση και προγραµµατισµό συναρτήσεων στην PostgreSQL. ΣΧΕΤΙΚΟ ΕΚΠΑΙ ΕΥΤΙΚΟ ΥΛΙΚΟ Η σχετική ύλη του βιβλίου του µαθήµατος (διαφάνειες και σχετικό υλικό από τις διαλέξεις του θεωρητικού µέρους του µαθήµατος: διαθέσιµα προς ανάκτηση µέσω του χώρου του µαθήµατος στο Blackboard). ΠΕΡΙΒΑΛΛΟΝ ΓΙΑ ΕΞΑΣΚΗΣΗ pgadmin ΥΠΟΒΑΘΡΟ Συναρτήσεις στην PostgreSQL 1. Γενικά Οι SQL functions εκτελούν µία λίστα από SQL εντολές. Στην απλή περίπτωση επιστρέφουν την πρώτη γραµµή (εγγραφή) του αποτελέσµατος της τελευταίας εντολής. Αν η τελευταία εντολή δεν επιστρέφει καθόλου εγγραφές επιστρέφεται η τιµή null. Εναλλακτικά, µια SQL function µπορεί να οριστεί ώστε να επιστρέφει ένα σύνολο γραµµών. Σε αυτή την περίπτωση, επιστρέφονται όλες οι γραµµές του αποτελέσµατος της τελευταίας εντολής. Το σώµα µιας SQL function πρέπει να είναι µια λίστα µιας ή περισσότερων εντολών SQL, χωρισµένων µε semicolon (;). Σηµειώστε ότι επειδή η σύνταξη της CREATE FUNCTION απαιτεί το σώµα της function να περικλείεται µέσα σε µονά εισαγωγικά (), όταν χρειαστεί η χρήση µονών εισαγωγικών στο σώµα της function, τότε αντί για µονό εισαγωγικό γράφουµε δύο µονά εισαγωγικά () ή backslash και µονό εισαγωγικό (\). Για να αναφερθούµε στις παραµέτρους της function χρησιµοποιούµε την σύνταξη $n. Για να αναφερθούµε δηλαδή στην πρώτη παράµετρο γράφουµε $1, στην δεύτερη $2 κ.τ.λ. Αν η παράµετρος είναι σύνθετου τύπου, για να αναφερθούµε στο πεδίο π.χ. name της πρώτης παραµέτρου γράφουµε $1.name. 2. SQL Functions επί βασικών τύπων Μια απλή SQL function µε όνοµα one φαίνεται στο παρακάτω παράδειγµα. Η function δεν δέχεται παραµέτρους και επιστρέφει ένα βασικό τύπο integer. Ενηµέρωση: 10- εκ-11 Σελίδα 1 από 7
CREATE FUNCTION one() RETURNS integer AS SELECT 1 AS result; Για να καλέσουµε την function one χρησιµοποιούµε την παρακάτω εντολή: SELECT one(); Το αποτέλεσµα της παραπάνω function είναι: ΠΡΟΣΟΧΗ: Στη function ονοµάσαµε την επιστρεφόµενη στήλη σαν result αλλά επειδή το όνοµα result δεν µπορεί να φανεί έξω από την function η στήλη παίρνει αυτόµατα το όνοµα της function που είναι one. H παρακάτω function δέχεται δύο ακεραίους (integer) ως παραµέτρους και επιστρέφει το άθροισµά τους: CREATE FUNCTION add_em(integer, integer) RETURNS integer AS SELECT $1 + $2; Καλούµε την συνάρτηση ορίζοντας το όνοµα της στήλης µε την οποία θα επιστραφεί το αποτέλεσµα: SELECT add_em(1, 2) AS answer; Το αποτέλεσµα είναι: Μια πιο χρήσιµη function για τη χρέωση ενός λογαριασµού τραπέζης που επιστρέφει το νέο υπόλοιπο του λογαριασµού θα ήταν: CREATE FUNCTION tf1 (integer, numeric) RETURNS numeric AS UPDATE bank SET balance = balance - $2 WHERE accountno = $1; SELECT balance FROM bank WHERE accountno = $1; Προϋποθέτει να υπάρχει η σχέση bank π.χ. CREATE TABLE BANK ( accountno integer primary key, balance numeric,onoma varchar(20)); INSERT INTO BANK VALUES(17,5000,Maria); Ένας χρήστης θα µπορούσε εκτελέσει τη function για να χρεώσει το λογαριασµό 17 µε 100.00 ως εξής: SELECT tf1(17, 100.0); Και µε αποτέλεσµα το της τελευταίας εντολής: Ενηµέρωση: 10- εκ-11 Σελίδα 2 από 7
Ένα σύνολο SQL εντολών µπορεί να οµαδοποιηθεί σε µία function. Εκτός από SELECT εντολές µπορούν να συµπεριληφθούν και οι INSERT, UPDATE και DELETE. Η τελευταία εντολή, όµως, θα πρέπει να είναι µία SELECT της οποίας το αποτέλεσµά της θα είναι και η επιστρεφόµενη τιµή της function. Αν πάλι, δε θέλουµε να µας επιστραφεί καµία τιµή τότε θα βάλουµε σαν επιστρεφόµενο τύπο της function το void. Σε αυτή την περίπτωση η τελευταία εντολή δε θα πρέπει να είναι SELECT. Στο παρακάτω παράδειγµα η function clean_emp διαγράφει τις εγγραφές του πίνακα emp στις οποίες το πεδίο salary έχει αρνητική ή µηδενική τιµή. Η function δε χρειάζεται να επιστρέψει καµία τιµή και για αυτό ο επιστρεφόµενος τύπος της είναι void. CREATE FUNCTION clean_emp() Προϋποθέτει να υπάρχει η σχέση emp π.χ. RETURNS void AS CREATE TABLE EMP ( name varchar(20) primary key, DELETE FROM emp WHERE salary <= 0; salary integer,age integer); INSERT INTO EMP VALUES(Sam,1000,30); Αφού καλέσουµε την function SELECT clean_emp(); το αποτέλεσµα που θα πάρουµε δεν επιστρέφει καθόλου εγγραφές άρα επιστρέφεται η τιµή null. 3. SQL Functions επί σύνθετων τύπων Όταν ορίζουµε functions µε παραµέτρους σύνθετων τύπων, πρέπει να προσδιορίσουµε όχι µόνο ποια παράµετρο θέλουµε (όπως κάναµε µε τα $1 και $2) αλλά και τα πεδία αυτής της παραµέτρου. Στο ακόλουθο παράδειγµα ο emp είναι ένας πίνακας υπαλλήλων. Ως εκ τούτου, είναι και το όνοµα του σύνθετου τύπου κάθε γραµµής του πίνακα. Η function double_salary υπολογίζει το διπλάσιο του µισθού κάποιου υπαλλήλου: CREATE FUNCTION double_salary(emp) RETURNS integer AS SELECT $1.salary * 2 AS salary; INSERT INTO EMP VALUES (Nikos,-200,25); Με κλήση SELECT name, double_salary(emp) AS dream FROM emp WHERE emp.name = Nikos; Θα δώσει Εναλλακτικά, η κλήση θα µπορούσε να είναι: SELECT name, double_salary(emp.*) AS dream FROM emp WHERE emp.name = Nikos; Είναι δυνατό να χτίσουµε µια function που επιστρέφει σύνθετο τύπο. Στο παράδειγµα που ακολουθεί, η function επιστρέφει µια µοναδική γραµµή τύπου emp: Ενηµέρωση: 10- εκ-11 Σελίδα 3 από 7
CREATE FUNCTION new_emp() RETURNS emp AS $$ SELECT text None AS name1,1000 AS salary1,25 AS age1; $$ SELECT new_emp() ; Φυσικά, αντί σταθερών τιµών θα µπορούσαµε να επιστρέφουµε τα αποτελέσµατα οποιωνδήποτε υπολογισµών. ΠΡΟΣΟΧΗ: Τα ονόµατα των στηλών που δώσαµε δεν είναι ορατά έξω από τη function. Όταν την καλέσουµε θα χρησιµοποιηθούν τα ονόµατα που έχουν χρησιµοποιηθεί στον ορισµό του emp. Για να µας επιστραφεί το πεδίο name καλούµε την function µε έναν από τους δύο τρόπους που φαίνεται παρακάτω: SELECT (new_emp()).name; SELECT name(new_emp()) ; Οι παρενθέσεις είναι απαραίτητες για να µην υπάρξει σύγχυση στον συντακτικό αναλυτή. Το αποτέλεσµα είναι: Η new_emp µπορεί να χρησιµοποιηθεί και ως παράµετρος σε άλλη function που δέχεται παράµετρο τύπου emp. Π.χ. CREATE FUNCTION getname(emp) RETURNS text AS SELECT $1.name; SELECT getname(new_emp()); 4. SQL Functions ως πηγές πινάκων Όλες οι SQL functions µπορούν να χρησιµοποιηθούν στο τµήµα FROM ενός query. Αυτό είναι ιδιαίτερα χρήσιµο για functions που επιστρέφουν σύνθετους τύπους. Εάν η function έχει οριστεί να επιστρέφει ένα βασικό τύπο, τότε η χρήση της στη θέση πίνακα παράγει ένα µονόστηλο πίνακα. Εάν η function έχει οριστεί να επιστρέφει ένα σύνθετο τύπο, τότε η χρήση της στη θέση πίνακα παράγει µια στήλη για κάθε χαρακτηριστικό του σύνθετου τύπου. Π.χ. ηµιουργούµε τον πίνακα foo και εισάγουµε τρεις εγγραφές: CREATE TABLE foo (fooid int, foosubid int, fooname text); Ενηµέρωση: 10- εκ-11 Σελίδα 4 από 7
INSERT INTO foo VALUES (1, 1, Joe); INSERT INTO foo VALUES (1, 2, Ed); INSERT INTO foo VALUES (2, 1, Mary); ηµιουργούµε την συνάρτηση: CREATE FUNCTION getfoo(int) RETURNS foo AS SELECT * FROM foo WHERE fooid = $1; και την καλούµε µε παράµετρο την τιµή 1: SELECT *, upper(fooname) FROM getfoo(1) AS t1; Όπως βλέπουµε µπορούµε να δουλέψουµε µε τις στήλες του αποτελέσµατος µιας function όπως και αυτές ενός πίνακα. Το αποτέλεσµα είναι το εξής: 5. SQL Functions που επιστρέφουν σύνολα Όταν µια SQL function έχει δηλωθεί να επιστρέφει SETOF κάποιου τύπου, η τελική εντολή SELECT της function εκτελείται µέχρι τέλους, και κάθε γραµµή που παράγει επιστρέφεται ως ένα στοιχείο του συνόλου αποτελεσµάτων. Αυτό το χαρακτηριστικό χρησιµοποιείται κανονικά όταν η function καλείται στο τµήµα FROM. Π.χ. αν ο πίνακας foo έχει τα ίδια περιεχόµενα και είχαµε: CREATE FUNCTION getfoo(int) RETURNS SETOF foo AS SELECT * FROM foo WHERE fooid = $1; Μετά την κλήση SELECT *, upper(fooname) FROM getfoo(1) AS t1; θα παίρναµε: Στην παρούσα φάση, οι functions που επιστρέφουν σύνολα µπορούν να κληθούν και στη λίστα select ενός query. Αυτή η δυνατότητα έχει δεχθεί αρνητική κριτική και θα αποσυρθεί στο µέλλον. ΑΣΚΗΣΕΙΣ οκιµάστε τα παραπάνω παραδείγµατα Ενηµέρωση: 10- εκ-11 Σελίδα 5 από 7
Εργαστείτε στην βάση που δηµιουργήσατε κατά το 8ο εργαστήριο και υλοποιήστε τα παρακάτω: 1. Υλοποιήστε µια συνάρτηση µε όνοµα raise_rating. Η συνάρτηση θα πρέπει να δέχεται 1 όρισµα. Το όρισµα θα εκφράζει όνοµα ναυτικού. Η συνάρτηση θα αυξάνει κατά ένα την βαθµίδα του ναυτικού και δεν θα επιστρέφει τίποτε. 2. Καλέστε την παραπάνω συνάρτηση για τον ναυτικό Maria και τον ναυτικό Giannis. 3. Υλοποιήστε µια συνάρτηση µε όνοµα set_age. Η συνάρτηση θα δέχεται ακριβώς 2 ορίσµατα. Το 1ο θα εκφράζει κωδικό ναυτικού και το 2ο θα εκφράζει ηλικία. Η συνάρτηση θα θέτει τη νέα τιµή στο πεδίο ηλικία του ναυτικού µε κωδικό αυτόν που δόθηκε Η συνάρτηση δεν θα επιστρέφει τίποτε. Φροντίστε για την χρήση του καταλληλότερου τύπου για τα ορίσµατα. 4. Καλέστε την παραπάνω συνάρτηση µε ορίσµατα (1,18) και (27,15.5). 5. Υλοποιήστε µια συνάρτηση µε όνοµα krathseis_varkas η οποία θα δέχεται ως όρισµα τον κωδικό µιας βάρκας και θα επιστρέφει το πλήθος των κρατήσεων που έχουν γίνει σε αυτήν την βάρκα. Τι τύπο πρέπει να επιστρέφει η συνάρτηση; 6. Καλέστε την συνάρτηση για το σκάφος 88. 7. οκιµάστε να εκτελέσετε το query: select *, krathseis_varkas(k_skafous) from skafos Τι κάνει το παραπάνω query? Υλοποιήστε query το οποίο να βρίσκει ακριβώς το ίδιο αποτέλεσµα χωρίς την χρήση συναρτήσεων. 8. ηµιουργείστε µια συνάρτηση µε όνοµα mesi_ilikia, η οποία δεν θα δέχεται κανένα όρισµα και θα επιστρέφει τις µέσες ηλικίες των ναυτικών ανά βαθµίδα. Η κλήση SELECT * FROM mesi_ilikia() ; θα πρέπει να δώσει (περίπου 1 ) το αποτέλεσµα: 9. ηµιουργείστε µια συνάρτηση µε όνοµα meses_ilikies, η οποία δεν θα δέχεται κανένα όρισµα και θα επιστρέφει τις µέσες ηλικίες των ναυτικών ανά βαθµίδα. Θα πρέπει να επιστρέφεται δίστηλος πίνακας µε 1η στήλη την βαθµίδα και 2η στήλη την µέση ηλικία. Η κλήση: SELECT * FROM meses_ilikies() ; θα πρέπει να δώσει (περίπου) το αποτέλεσµα: 1 Εξαρτάται από τις αλλαγές των ηλικιών που κάνατε στις πρώτες ασκήσεις. Ενηµέρωση: 10- εκ-11 Σελίδα 6 από 7
Σε αυτήν την άσκηση πρέπει να δηµιουργήσετε δικό σας τύπο ή πίνακα πχ: create type meses_il as (vathmida int, mesi_ilikia double precision); 10. Χρησιµοποιήστε την προηγούµενη συνάρτηση για να βρείτε την µικρότερη µέση ηλικία 11. Βρείτε την µικρότερη µέση ηλικία χωρίς την χρήση συναρτήσεων. 12. Χρησιµοποιήστε την προηγούµενη συνάρτηση (meses_ilikies) για να βρείτε σε ποια βαθµίδα αντιστοιχεί η µικρότερη µέση ηλικία. 13. Βρείτε σε ποια βαθµίδα αντιστοιχεί η µικρότερη µέση ηλικία χωρίς την χρήση συναρτήσεων. Ενηµέρωση: 10- εκ-11 Σελίδα 7 από 7