❖ Week 05 Tuesday |
vxdb2
❖ Submitting/Marking Assignment 1 |
Test on vxdb2
Submit via: give cs3311 ass1 ass1.sql
Will be tested via:
dropdb ass1; createdb ass1; psql ass1 -f ass1.dump psql ass1 -f ass1.sql # your submission 3311 autotest ass1 dropdb ass1; createdb ass1; psql ass1 -f ass1a.dump psql ass1 -f ass1.sql # your submission 3311 autotest ass1
❖ Submitting/Marking Assignment 1 (cont) |
Tutors will look at: ass1.sql
Up to 1 mark for style; ugly code gets 0
Consistency of style is important; use SQL from lectures as guide
If view fails auto-test
❖ Assignment Hints |
Some ideas ...
case..when
regexp_replace()
string_agg()
raise notice
❖ Assignment Hints (cont) |
If you get this error ...
nw-syd-vxdb2 % psql ass1 -f ass1.sql psql: error: ass1.sql: No such file or directory
you're in the wrong directory.
Try cd
ls
ass1.sql
If you get this error ...
ass1=# ERROR: already defined V
change create view
create or replace view
❖ Developing Complex SQL Queries |
Work backwards from the final query
select x from R where y = (select min(y) from R)
Use attribute names in the create or replace view
(easier to keep track of what results the view is producing for use in other views)
Cannot change number/types of view attributes without dropping view
❖ Developing PLpgSQL Functions |
Determine function signature
for Rec in Query loop ...
❖ Iteration in PLpgSQL |
Use SQL queries within function do as much work as possible
Don't do using code what you could do using
where
for each tuple T in Table { replace Table by if Cond(T) then ... select * from Table } where Cond
join
for each tuple T in T1 { replace by for each tuple S in T2 { select * if T.x = S.y then ... from T1 join T2 on x=y } }
❖ Iteration in PLpgSQL (cont) |
Example: function returning setof Beers
create or replace function bb(_patt text) returns setof Beers as $$ declare b Beers; begin for b in select * from Beers loop if b.name ~ _name then return next b; end if end loop; end; $$ language plpgsql;
vs
create or replace function bb(_patt text) returns setof Beers as $$ declare b Beers; begin for b in select * from Beers where name ~ _name loop return next b; end loop; end; $$ language plpgsql;
❖ Exercise: Poor use of loop |
Which of the following is the preferred way of filtering
for rec in select * from R loop if (rec.a = 5) then ... end if; end loop; OR for rec in select * from R where a = 5 loop ... end loop;
Why? Assume: |R| = 1000 tuples, 10 tuples with a=5
❖ Dynamic Queries |
Queries can be "built" as strings of SQL code within functions
And then evaluated using execute
Including constant and identifiers in queries needs
quote_literal
quote_ident
Use execute Query into Var
Can also use in for Rec in execute Query loop ...
❖ Exercise: Dynamic Queries |
Write a PLpgSQL function
create or replce function nTuples(tableName text) return integer as $$ ...
The function
dbPop()
❖ Exceptions |
Exceptional conditions can arise during function execution
PLpgSQL provides mechanisms
begin ... some code ... exceptionexceptionName then ... code to handle exception ... end; when
❖ Exercise: Exceptions |
Implement a "safe" division function
create or replace function div(n integer, m integer) returns integer
❖ Aggregates |
Aggregates reduce a collection of values to a single value
Example:
select avg(mark) from Enrolments where course='COMP3311'
How to achieve this? ... Maintain state, update value-by-value
State = initial state
for each tuple T in query {
# update State to include T
State = updateState(State, T)
}
return makeFinal(State)
❖ Aggregates (cont) |
New aggregates are defined using CREATE AGGREGATE
CREATE AGGREGATE AggName(BaseType) ( stype = StateType, initcond = InitialValue, sfunc = UpdateStateFunction, finalfunc = MakeFinalFunction );
initcond
finalfunc
❖ Exercise: User-defined Aggregates |
product
select product(a) from R → 6
select cat(b) from R → 'this,is,fun'
count
select mycount(a) from R → 3 select mycount(b) from R → 3
create table R (a integer, b text); insert into R values (1,'this'), (2,'is'), (3,'fun');
❖ Global Constraints |
Column and table constraints ensure validity of one table
Ref. integrity constraints ensure connections between tables are valid
Global constraints may involve conditions over many tables
Simple example (from banking domain):
-- accounts are held at branches -- assets of branch is sum of balances of its accounts for all Branches b b.assets == (select sum(acct.balance) from Accounts acct where acct.branch = b.location)
❖ Global Constraints (cont) |
SQL implementation of global constraints is ASSERTION
Example: #students in any UNSW course must be < 10000
create assertion ClassSizeConstraint check ( not exists ( select c.id from Courses c join Enrolments e on (c.id = e.course) group by c.id having count(e.student) > 9999 ) );
Must be checked after every change to either Courses
Enrolments
Too expensive, so DBMSs provide triggers to do targetted checking.
❖ Triggers |
Triggers are
❖ Triggers (cont) |
Sequence of activities during database update:
Reminder: BEFORE
UPDATE
❖ Triggers in PostgreSQL |
PostgreSQL triggers provide a mechanism for
INSERT
DELETE
UPDATE
TriggerName { CREATE TRIGGER
| AFTER
} Event1 [ BEFORE
Event2 ...] OR
TableName [ ON
( Condition ) ] WHEN
{ROW|STATEMENT} FOR EACH
FunctionName(args...); EXECUTE PROCEDURE
BEFORE
old
new
❖ Exercise: Trigger Example (i) |
Consider two tables
create table R (id integer, val text); create table S (r integer references R(id), value text);
Write a trigger to check that the foreign key value is valid
S
S
❖ Exercise: Trigger Example (ii) |
Class enrolments:
Classes(id,name,room,day,start,end,quota,nstu) ClassEnrolments(student,class)
Define triggers to maintain nstu
insert into ClassEnrolments values (5012345, 6732); insert into ClassEnrolments values (9999999, 99999); delete from ClassEnrolments where student = 5012345 and class = 6732;
❖ Programming with Databases |
So far, we have seen ...
❖ Programming with Databases (cont) |
Consider this (imaginary) PL/DBMS access method:
-- establish connection to DBMS db = dbAccess("DB"); query = "select a,b from R,S where ... "; -- invoke query and get handle to result set results = dbQuery(db, query); -- for each tuple in result set while (tuple = dbNext(results)) { -- process next tuple process(tuple['a'], tuple['b']); }
Estimated costs: dbAccess
dbQuery
dbNext
❖ Programming with Databases (cont) |
Example: find mature-age students (e.g. 10000 students, 100 over 40)
query = "select * from Student";
results = dbQuery(db, query);
while (tuple = dbNext(results)) {
if (tuple['age'] >= 40) {
-- process mature-age student
}
}
We transfer 10000 tuples from DB, 9500 are irrelevant
Cost = 1 query + 10000 tuple fetches
❖ Programming with Databases (cont) |
Should be implemented as:
query = "select * from Student where age >= 40";
results = dbQuery(db, query);
while (tuple = dbNext(results)) {
-- process mature-age student
}
Transfers only the 100 tuples that are needed.
Cost = 1 query + 100 tuple fetches
❖ Programming with Databases (cont) |
Example: find info about all marks for all students
query1 = "select id,name from Student order by id";
res1 = dbQuery(db, query1);
while (tuple1 = dbNext(res1)) {
query2 = "select course,mark from Marks"
+ " where student = " + tuple1['id'];
res2 = dbQuery(db,query2);
while (tuple2 = dbNext(res2)) {
-- process student/course/mark info
}
}
E.g. 10000 students, each with 8 marks, ⇒ run 10001 queries
Cost = 10001 queries + 80000 tuple fetches
❖ Programming with Databases (cont) |
Should be implemented as:
query = "select id,name,course,mark"
+ " from Student s join Marks m "
+ " on (s.id=m.student)"
+ " order by s.id"
results = dbQuery(db, query);
while (tuple = dbNext(results)) {
-- process student/course/mark info
}
We invoke 1 query, and transfer same number of tuples.
Cost = 1 query + 80000 tuple fetches
Produced: 14 Mar 2023