❖ 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..whenregexp_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 cdlsass1.sql
If you get this error ...
ass1=# ERROR: already defined V
change create viewcreate 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_literalquote_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
);
initcondfinalfunc❖ Exercise: User-defined Aggregates |
productselect product(a) from R → 6
select cat(b) from R → 'this,is,fun'
countselect 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 CoursesEnrolments
Too expensive, so DBMSs provide triggers to do targetted checking.
❖ Triggers |
Triggers are
❖ Triggers (cont) |
Sequence of activities during database update:
Reminder: BEFOREUPDATE
❖ Triggers in PostgreSQL |
PostgreSQL triggers provide a mechanism for
INSERTDELETEUPDATETriggerName { CREATE TRIGGER| AFTER} Event1 [ BEFOREEvent2 ...] ORTableName [ ON( Condition ) ] WHEN{ROW|STATEMENT} FOR EACHFunctionName(args...); EXECUTE PROCEDURE
BEFOREoldnew❖ 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
SS❖ 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: dbAccessdbQuerydbNext
❖ 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