1 Εναύσματα και Περιορισμοί Database Management Systems, Antonis Sidiropoulos
2 Integrity Constraints (Review) – Περιορισμοί Ακαιρεότητας An IC describes conditions that every legal instance of a relation must satisfy. Inserts/deletes/updates that violate IC’s are disallowed. Can be used to ensure application semantics (e.g., sid is a key), or prevent inconsistencies (e.g., sname has to be a string, age must be < 200) Types of IC’s: Domain constraints, primary key constraints, foreign key constraints, general constraints. Domain constraints: Field values must be of right type. Always enforced.
3 General Constraints Useful when more general ICs than keys are involved. Can use queries to express constraint. Constraints can be named.
4 Constraints Over Multiple Relations Awkward and wrong! If Sailors is empty, the number of Boats tuples can be anything! ASSERTION is the right solution; not associated with either table. Number of boats plus number of sailors is < 100
5 Triggers Trigger: procedure that starts automatically if specified changes occur to the DBMS Three parts: Event (activates the trigger) Condition (tests whether the triggers should run) Action (what happens if the trigger runs)
6 Triggers CREATE TRIGGER youngSailorUpdate AFTER INSERT ON SAILORS REFERENCING NEW ROW as NewSailors FOR EACH ROW do something
7 Triggers – Παράδειγμα στην DB2 Έχουμε προσθέσει στον πίνακα Reserves το πεδίο time_of_reservation τύπου timestamp το οποίο θέλουμε κάθε φορά να περιέχει την ώρα/μέρα που έγινε η κράτηση (που καταχωρήθηκε η πλειάδα στον πίνακα) CREATE TRIGGER Reserv_time BEFORE INSERT ON RESERVES REFERENCING NEW ROW as N FOR EACH ROW MODE DB2SQL BEGIN ATOMIC set N.time_of_reservation = current timestamp END;
8 Triggers – Σύνταξη CREATE TRIGGER [ΟΝΟΜΑ trigger] [activation time] [trigger event] ON [subject table] REFERENCING [object] AS [name] [granularity] MODE DB2SQL WHEN [condition] BEGIN ATOMIC [triggered action ] END [activation time]: BEFORE, AFTER, INSTEAD [trigger event] : INSERT, UPDATE, DELETE [subject table] : όνομα πίνακα [object]: NEW, OLD [granularity]: FOR EACH ROW, FOR EACH STATEMENT (To FOR EACH STATEMENT δεν μπορεί να συνδυαστεί με το BEFORE)
9 Triggers – BEFORE ή AFTER ? v Το BEFORE INSERT σημαίνει πως ο χρήστης έδωσε εντολή INSERT, έχει αυτή ερμηνευτεί αλλά δεν έχει ακόμη εκτελεστεί. Άρα η νέα πλειάδα δεν έχει μπει στην βάση. v Το AFTER INSERT σημαίνει πως ο χρήστης έδωσε εντολή INSERT και αυτή έχει εκτελεστεί ήδη… η νέα πλειάδα έχει αποθηκευτεί στον πίνακα στην βάση.
10 Triggers – BEFORE ή AFTER ? v Το BEFORE INSERT σημαίνει πως ο χρήστης έδωσε εντολή INSERT, έχει αυτή ερμηνευτεί αλλά δεν έχει ακόμη εκτελεστεί. Άρα η νέα πλειάδα δεν έχει μπει στην βάση. CREATE TRIGGER Reserv_time BEFORE INSERT ON RESERVES REFERENCING NEW ROW as N FOR EACH ROW MODE DB2SQL BEGIN ATOMIC set N.time_of_reservation = current timestamp END; INSERT INTO RESERVES VALUES(4,5,’ ’,NULL) Ή INSERT INTO RESERVES (sid, bid, day, time_of_reservation) VALUES(4,5,’ ’,NULL) Ή INSERT INTO RESERVES (sid, bid, day) VALUES(4,5,’ ’) SidBidDayTime_of_res NULL NEW ROW (N) =
11 Triggers – BEFORE ή AFTER ? v Κατά την εκτέλεση του trigger θα εκτελεστεί η εντολή: –set N.time_of_reservation = current timestamp Άρα το αντικείμενο Ν θα αλλάξει τιμή SidBidDayTime_of_res :21: NEW ROW (N) = SidBidDayTime_of_res :29: :26: :21: Ο Πίνακας RESERVES στην Βάση v Μετά την εκτέλεση του trigger θα εκτελεστεί το INSERT στην βάση, δηλαδή το Ν θα αποθηκευτεί στον πίνακα RESERVES v H τιμή που θα αποθηκευτεί θα έχει ενημερωμένο το πεδίο Time_of_reservation :21:44.543
12 Triggers – BEFORE ή AFTER ? v Το AFTER INSERT σημαίνει πως ο χρήστης έδωσε εντολή INSERT και αυτή έχει εκτελεστεί ήδη… η νέα πλειάδα έχει αποθηκευτεί στον πίνακα στην βάση. v ΑΡΑ ΑΚΟΜΗ ΚΑΙ ΑΝ ΑΛΛΑΞΟΥΜΕ ΤΟ [NEW ROW] δεν θα ξαναγίνει αποθήκευση στον πίνακα. CREATE TRIGGER Reserv_time AFTER INSERT ON RESERVES REFERENCING NEW ROW as N FOR EACH ROW MODE DB2SQL BEGIN ATOMIC set N.time_of_reservation = current timestamp END; To παραπάνω είναι λάθος
13 Triggers – BEFORE ή AFTER ? ΤΟ ΛΑΘΟΣ ΠΑΡΑΔΕΙΓΜΑ CREATE TRIGGER Reserv_time AFTER INSERT ON RESERVES REFERENCING NEW ROW as N FOR EACH ROW MODE DB2SQL BEGIN ATOMIC set N.time_of_reservation = current timestamp END; INSERT INTO RESERVES VALUES(4,5,’ ’,NULL) Ή INSERT INTO RESERVES (sid, bid, day, time_of_reservation) VALUES(4,5,’ ’,NULL) Ή INSERT INTO RESERVES (sid, bid, day) VALUES(4,5,’ ’) SidBidDayTime_of_res NULL NEW ROW (N) =
14 Triggers – BEFORE ή AFTER ? ΤΟ ΛΑΘΟΣ ΠΑΡΑΔΕΙΓΜΑ v Πριν την εκτέλεση του TRIGGER θα αποθηκευτεί στον πίνακα RESERVES η νέα πλειάδα. SidBidDayTime_of_res :21: NEW ROW (N) = SidBidDayTime_of_res :29: :26: :21: Ο Πίνακας RESERVES στην Βάση v Μετά την εκτέλεση του INSERT θα εκτελεστεί το TRIGGER v Κατά την εκτέλεση του trigger θα εκτελεστεί η εντολή: set N.time_of_reservation = current timestamp Άρα το αντικείμενο Ν θα αλλάξει τιμή To αποτέλεσμα όμως στην βάση θα παραμείνει με time_of_res=NULL διότι δεν θα ξαναγίνει ενημέρωση NULL
15 Triggers – BEFORE ή AFTER ? v Το AFTER INSERT σημαίνει πως ο χρήστης έδωσε εντολή INSERT και αυτή έχει εκτελεστεί ήδη… η νέα πλειάδα έχει αποθηκευτεί στον πίνακα στην βάση. v Πρέπει να αλλάξουμε πλέον δεδομένα στην βάση και όχι στην μνήμη (δηλαδή όχι το αντικείμενο Ν) CREATE TRIGGER Reserv_time AFTER INSERT ON RESERVES REFERENCING NEW ROW as N FOR EACH ROW MODE DB2SQL BEGIN ATOMIC UPDATE RESERVES set time_of_reservation = current timestamp WHERE sid=N.sid AND bid=N.bid AND day = N.day END; Θέλουμε να αλλάζουμε στην βάση την συγκεκριμένη πλειάδα. Αναφερόμαστε σε αυτήν με βάση το κύριο κλειδί.
16 Triggers – BEFORE ή AFTER ? CREATE TRIGGER Reserv_time AFTER INSERT ON RESERVES REFERENCING NEW ROW as N FOR EACH ROW MODE DB2SQL BEGIN ATOMIC UPDATE RESERVES set time_of_reservation = current timestamp WHERE sid=N.sid AND bid=N.bid AND day = N.day END; INSERT INTO RESERVES VALUES(4,5,’ ’,NULL) Ή INSERT INTO RESERVES (sid, bid, day, time_of_reservation) VALUES(4,5,’ ’,NULL) Ή INSERT INTO RESERVES (sid, bid, day) VALUES(4,5,’ ’) SidBidDayTime_of_res NULL NEW ROW (N) =
17 Triggers – BEFORE ή AFTER ? ΤΟ ΛΑΘΟΣ ΠΑΡΑΔΕΙΓΜΑ v Πριν την εκτέλεση του TRIGGER θα αποθηκευτεί στον πίνακα RESERVES η νέα πλειάδα. SidBidDayTime_of_res NULL NEW ROW (N) = SidBidDayTime_of_res :29: :26: :21: Ο Πίνακας RESERVES στην Βάση v Μετά την εκτέλεση του INSERT θα εκτελεστεί το TRIGGER v Κατά την εκτέλεση του trigger θα εκτελεστεί η εντολή: UPDATE RESERVES set time_of_reservation = current timestamp WHERE sid=N.sid AND bid=N.bid AND day = N.day Όπου Ν.sid είναι 4, Ν.bid = 5, Ν.day=‘ ’ To αποτέλεσμα της παραπάνω εντολής είναι να γίνει ενημέρωση στον πίνακα το πεδίο Time_of_res της πλειάδας που εισήχθηκε, στην τιμή [τρέχουσα ώρα] Σε ένα TRIGGER που εκτελείται AFTER INSERT δεν υπάρχει λόγος να αλλάξουμε το N (ουσιαστικά είναι σφάλμα να αλλάξουμε το Ν, αφού η αλλαγή αυτή δεν θα ληφθεί υπόψη) NULL
18 Triggers – BEFORE ή AFTER ? v BEFORE: –Αν θέλουμε να αλλάξουμε τα δεδομένα που θα εισάγουμε –Αν θέλουμε να ελέγξουμε τα δεδομένα και να ακυρώσουμε την εντολή. –Υπάρχει περίπτωση τελικά η εντολή (INSERT,UPDATE,DELETE) να μην εκτελεστεί. v AFTER: –Αν θέλουμε να αλλάξουμε δεδομένα σε άλλους πίνακες –Αν θέλουμε να αλλάξουμε δεδομένα (και) σε άλλες εγγραφές –Είμαστε σίγουροι ότι η εντολή (INSERT,UPDATE,DELETE) εκτελέστηκε. v An AFTER trigger occur after the trigger event executes, and after the database manager checks all constraints that the trigger event may affect, including actions of referential constraints. v A BEFORE trigger is activated before integrity constraints are checked and may be violated by the trigger event.
19 Triggers – BEFORE ή AFTER ? v Στο προηγούμενο παράδειγμα θα έπρεπε να υλοποιήσουμε BEFORE INSERT trigger. v Έστω ότι στην βάση μας έχουμε ακόμη έναν πίνακα Sailors_Resv( sid: int, n_reserves: int) v Θα θέλαμε αυτός ο πίνακας να περιέχει το πλήθος των κρατήσεων που έχει κάνει ο κάθε ναυτικός. Άρα κάθε φορά που εισάγουμε μια κράτηση να αυξάνουμε το n_reserves, κάθε φορά που διαγράφεται μια κράτηση να μειώνουμε το n_reserves. v Επίσης θα θέλαμε κάθε φορά που εισαγουμε έναν ναυτικό, να εισάγεται και στον πίνακα Sailors_Resv το sid του νέου ναυτικού. v Κάθε φορά που διαγράφουμε έναν ναυτικό, να διαγράφεται και η αντίστοιχη γραμμή από τον Sailors_Resv.
20 Triggers – BEFORE ή AFTER ? CREATE TABLE Sailors_Resv ( sid int NOT NULL references Sailors(sid) ON DELETE CASCADE, n_reserves int NOT NULL DEFAULT 0, PRIMARY KEY(sid)) Με αυτόν τον τρόπο πετυχαίνουμε κάθε φορά που διαγράφεται ένας ναυτικός, να διαγράφεται και η αντίστοιχη γραμμή του Sailors_Resv. CREATE TRIGGER Sailors_Resv_del AFTER INSERT ON Sailors REFERENCING NEW ROW as N FOR EACH ROW MODE DB2SQL BEGIN ATOMIC INSERT INTO Sailors_Resv(sid) VALUES(N.sid) END; Με αυτόν τον τρόπο πετυχαίνουμε κάθε φορά που εισάγεται ένας ναυτικός, να εισάγεται και η αντίστοιχη γραμμή του Sailors_Resv.
21 Triggers – BEFORE ή AFTER ? CREATE TRIGGER Sailors_Resv_Inc AFTER INSERT ON Reserves REFERENCING NEW ROW as N FOR EACH ROW MODE DB2SQL BEGIN ATOMIC UPDATE Sailors_Resv set n_reserves= n_reserves+1 WHERE sid = N.sid END; Με αυτόν τον τρόπο πετυχαίνουμε κάθε φορά που εισάγεται μια κράτηση ενός ναυτικού, αυξάνεται το πεδίο n_reserves στ ην αντίστοιχη γραμμή του Sailors_Resv. Όμοια στο delete. CREATE TRIGGER Sailors_Resv_Inc AFTER DELETE ON Reserves REFERENCING NEW ROW as N FOR EACH ROW MODE DB2SQL BEGIN ATOMIC UPDATE Sailors_Resv set n_reserves= n_reserves- 1 WHERE sid = N.sid END;
22 Triggers – Προσοχή v Μέσα στον κώδικα ενός trigger, μπορούμε να κάνουμε «ό,τι» θέλουμε. Χρειάζεται όμως προσοχή διότι: –Κάποια ενέργεια που εκτελείται μέσα σε ένα trigger ίσως προκαλέσει ξανά την εκτέλεση του ίδιου trigger – ατέρμον βρόγχος –Κάποια ενέργεια που εκτελείται μέσα σε ένα trigger ίσως προκαλέσει την εκτέλεση κάποιου άλλου trigger το οποίο ίσως προκαλέσει την εκτέλεση του 1 ου - ατέρμον βρόγχος
23 Triggers – Προσοχή v Παράδειγμα κακού trigger (θα εκτελεστεί μετά από update στον Reserves και στο πεδίο time_of_reservation. Όμως μέσα στο trigger γίνεται ενημέρωση του time_of_reservation, οπότε και θα ξανακληθεί το trigger κ.ο.κ.: CREATE TRIGGER Reserv_time AFTER UPDATE ON RESERVES OF time_of_reservation REFERENCING NEW ROW as N FOR EACH ROW MODE DB2SQL BEGIN ATOMIC UPDATE RESERVES set time_of_reservation = current timestamp WHERE sid=N.sid AND bid=N.bid AND day = N.day END;
24 Triggers – Σημειώσεις v Οι τιμές NEW, OLD δεν είναι πάντα διαθέσιμες, αλλά εξαρτάται από τον τύπο του trigger Τύπος triggerROW triggerSTATEMENT trigger BEFORE INSERTNEW- BEFORE UPDATEOLD,NEW- BEFORE DELETEOLD- AFTER INSERTNEWNEW_TABLE AFTER UPDATENEW OLD NEW_TABLE, OLD_TABLE AFTER DELETEOLDOLD_TABLE
25 Triggers - Σημειώσεις v Ένα trigger BEFORE XXXX, μπορεί να δώσει “exception” και να ακυρώσει την ενέργεια του χρήστη: CREATE TRIGGER Check_Future_res BEFORE INSERT ON Reserves REFERENCING NEW AS N FOR EACH ROW MODE DB2SQL BEGIN ATOMIC IF 10<(SELECT count(*) from Reserves where bid=N.bid and day>current timestamp) THEN SIGNAL SQLSTATE '70005' (‘No more 10 reservations in the future are allowed'); END IF; END
26 Triggers – Σημειώσεις v Τα triggers δεν συμπεριλαμβάνονται στο πρότυπο SQL-92 v Τα περισσότερα ΣΔΒΔ είναι ασύμβατα μεταξύ τους όσον αφορά τον ορισμό των triggers (η φιλοσοφία παραμένει η ίδια) v Τα triggers συμπεριλαμβάνονται στο πρότυπο SQL-99
27 Triggers – Κανόνες καλής τακτικής v Rule 1: Do not change data in the primary key, foreign key, or unique key columns of any table v Rule 2: Do not update records in the same table you read during the same transaction v Rule 3: Do not aggregate over the same table you are updating v Rule 4: Do not read data from a table which is updated during the same transaction
28 Triggers in SQL:1999 CREATE TRIGGER trigger-name {BEFORE | AFTER] {INSERT | DELETE | UPDATE [OF column(s)]} ON table-name [REFERENCING [OLD AS varRefToOldTuple] [NEW AS varRefToNewTuple] [OLD TABLE AS nameToRefOldTable] [NEW TABLE AS nameToRefNewTable] [FOR EACH {ROW | STATEMENT } ] [WHEN (condition) ] statement list v Action of before trigger cannot modify the database