TDA357-L08-Triggers
TDA357-L08-Triggers
• No! If someone asks you to use code like this, never speak to them again.
• So why should we accept this from a database interface?
Side note: atomicity
• All SQL modifications are atomic: If I execute a DELETE/UPDATE/INSERT and
there is an error, no rows will have changed
• For instance, for a delete, if any matching row can not be deleted, nothing
gets deleted
• Intermediate changes are never visible to other users of the database (rows
do not disappear and then reappear again, they either change or they don't)
• This says something about how intricate a DBMS is, imagine implementing
this on a data structure in Java! (A method that removes all members
satisfying a criteria, but rolls back all changes if there is an error)
• Works even if the server loses power in the middle of an update(!)
(The update will either be performed completely, or nothing is changed)
Cascading
• Remember: deletes/updates may fail due to references from other tables
• I can not remove a student unless I first remove all that students' grades
• By default, a query that attempts to delete a referenced row fails and nothing is
deleted, this can be changed when creating the reference:
• Delete all referencing rows as well:
student TEXT REFERENCES Students(id) ON DELETE CASCADE
• Can potentially lead to deleting the whole database
Modes for ON DELETE/UPDATE
• ON [DELETE/UPDATE] [CASCADE/SET NULL /RESTRICT/NO ACTION]
• ON DELETE CASCADE: Delete this row if the referenced value is deleted
• ON UPDATE CASCADE: Update this value if the referenced value is updated
• SET NULL: Set this value to NULL if the referenced value is updated/deleted
• RESTRICT and NO ACTION: Raise an error, NO ACTION is the default behavior
• ON UPDATE is usually OK to CASCADE, for ON DELETE, be more careful
A complete example
• Here is the LimitedCourses table, extended with the follow behavior:
• If a course is deleted (in Courses), its limit is also deleted
• If a course code is changed in Courses, it is also changed here
• Both of these make sense: Why give an error when deleting a course
simply because it has a limitation?
CREATE TABLE LimitedCourses(
course CHAR(6) PRIMARY KEY,
seats INTEGER NOT NULL,
FOREIGN KEY (course) REFERENCES Courses
ON DELETE CASCADE ON UPDATE CASCADE
);
• Note that this says nothing about the other direction (if a course code
is changed in LimitedCourses for instance)
What is the sensible ON UPDATE/DELETE here?
• Argue for a sensible policy for the references in Lectures:
Lectures(course, semester, weekday, hour, room)
(course, semester) -> Courses.(code, year)
room -> Rooms.name
• For Courses.(code, year):
ON DELETE CASCADE ON UPDATE (CASCADE/nothing)
If a course is deleted, delete its lectures, if a semester is changed…?
• For Rooms.name:
ON UPDATE CASCADE ON DELETE (nothing/SET NULL)
If a room is renamed, update lectures
If a used room is removed, give an error (or set to NULL? Needs to allow null
values for room attribute)
• The default of raising an error is never totally wrong (it will never corrupt your
database, but may make it less usable)
How to identify ON DELETE/ON UPDATE
• If you need to do something like "When a student is deleted, it should
automatically be unregistered from all courses"
• Note how this is clearly not something that we can model in ER
• This would be a modifier of a reference wherever registrations are
stored, not in the student table.
Advanced cross-row/cross-table constraints
• Often, we want constraints affecting multiple rows/multiple tables
• Some such constraints can already be implemented:
• Unique/primary key constraints are cross-row constraints
(You need to look across rows to know there are no conflicts)
• Foreign keys are cross-table constraints
(You need to look in another table to know if a value is allowed)
• Other constraints can not be expressed using references/keys, examples:
• Two columns should be mutually exclusive ("shared keys")
• A column should contain consecutive numbers starting from 1
•…
ASSERTIONS
• Assertions are part of the SQL standard
• They allow us to write conditions that should be globally true for the database
• Syntax:
CREATE ASSERTION <assertion name> AS
CHECK <condition>;
• Very difficult to implement efficiently in a DBMS
• For instance: We can write an assertion that states that all course registrations
have happened within the last year from today
• When should this be checked? What happens when it is suddenly false?
• Assertions are not implemented in Postgres, or in most major DBMS
User created functions
• Most DBMS allows users to create functions, these can be used
similar to COALESCE() and other functions we have seen
• Functions are stored in the DB server and executed in queries etc.
• Sometimes called "stored procedures"
• Reasons not to use functions
• They do not fit nicely into the relational data model
• May be poorly optimized (compared to SQL)
• Poorly standardized between DBMSs (code from one DBMS may
work in another with some alterations, or it may not work at all)
• Reasons to use functions
• There are some things that simply cannot be done without them
CREATE FUNCTION in Postgres
CREATE FUNCTION <name>(<parameter types>) RETURNS <return type> AS
<code>
unnamed parameter
Function name (in this case a course code)
• Example:
• There is also a PL/Python is Postgres. Try it out and let me know if it works!
Table: WaitingList
Similar to the table in the assignment student course position
Student1 TDA357 1
What does this function do? Student2 TDA357 2
RETURNS BIGINT AS
$$ SELECT COUNT(*)+1 FROM WaitingList WHERE course=$1
$$ LANGUAGE SQL;
$$
DECLARE
cnt INT; Declares two variables
myBool BOOLEAN;
BEGIN
...
SELECT ... INTO
• Used to run a query and store the result in a variable (declared earlier)
• Either raises an error or gives null/first row if the query does not give exactly
one row, depending on DBMS and setting
• Only use things like
SELECT credits INTO creds FROM Courses WHERE code=x;
if you know x is a valid code
• Tip: Simple aggregates always give one row (possibly containing null)
SELECT MAX(credits) INTO creds
FROM Courses WHERE code=x;
• Variable creds will be null if x is not an existing course code
IF-statements
• You know them, you love them.
• Syntax for if-elseif-else:
IF (<condition>) THEN
...
ELSIF (<condition> THEN
...
ELSE
...
END IF;
• Both ELSIF and ELSE are optional
• Will run ELSE when condition is UNKNOWN
• In Postgres, you can have queries in the conditions, in original PL/SQL
you can not (need to use variables)
Exists is your friend
• To test something like "Is this student registered for this course", you
can use EXISTS:
IF (EXISTS (SELECT *
FROM WaitingList
WHERE course=NEW.course)
) THEN …