-- (a) create domain SmallInt as integer check (value between 1 and 100); --or create domain SmallInt as integer check (value>=1 and value<=100); -- (b) create domain PosNum as numeric check (value > 0); --or create domain PosNum as real check (value > 0); -- (c) create domain Name as varchar(50); --or create domain Name as text check (length(value) <= 50); -- (d) create domain Code as char(3) check (value ~ '[A-Z][A-Z][A-Z]'); --or create domain Code as char(3) check (value ~ '[A-Z]{3}'); --or create domain Code as text check (value ~ '^[A-Z]{3}$'); --or ... -- Notes: -- * other variations are possible; allow anything reasonable -- * if they used the domain name rather than keyword "value" -- in the check expressions, give them B
-- (a) create table U ( id serial primary key, a SmallInt, f String ); create table UM ( u integer foreign key references U(id), m Code, primary key (u,m) ); -- (b) create table W ( id serial primary key, e PosNum ); create table V ( r integer not null, h String, a SmallInt, primary key (r,h), foreign key (r) references W(id) ); -- (d) create table P ( id serial primary key, f Name, ); create table C1 ( id integer primary key references P(id), m Code ); create table C2 ( id integer primary key references P(id) ); create table C3 ( id integer primary key references P(id), g Name ); -- Cannot represent the disjoint subclass condition
create view StonesCDs as select c.* from MusicCD c, MusicGroup g where c.madeBy = g.id and g.name = 'Rolling Stones' ; create view Q4a as select title from StonesCDs where year = (select max(year) from StonesCDs); ; --or create view Q4a as select title from MusicCD c, MusicGroup g where c.madeBy = g.id and g.name = 'Rolling Stones' and year = (select max(year) from MusicCD c, MusicGroup g where c.madeBy = g.id and g.name = 'Rolling Stones' ) ; --or create view StonesCDs as select c.* from MusicCD c, MusicGroup g where c.madeBy = g.id and g.name = 'Rolling Stones' ; create view mostRecentStonesCDYear as select max(year) as year from StonesCDs ; create view Q4a as select title from StonesCDs cd, mostRecentStonesCDYear y where cd.year = y.year ;
create or replace view Q4b as select g.name, count(c.id) as numOfCDsMade from MusicGroup g, MusicCD c where g.id = c.madeBy group by g.name ; -- renaming the second attribute is optional
create view CDlength as select cd.id, cd.title, sum(s.length) as length from MusicCD cd, Song s where s.onCD = cd.id group by cd.id, cd.title ; create view Q4c as select title, length from CDlength where length = (select max(length) from CDlength) ; --or create view CDlength as select onCD, sum(length) as length from Song group by onCD ; create view Q4c as select title, length from CDlength cl, MusicCD cd where cl.onCD = cd.id and cl.length = (select max(length) from CDlength) ;
create view nPlayed as select m.id, m.name, count(p.instrument) as ninst from Musician m, PlaysOn p where m.id = p.musician group by m.id, m.name ; create view Q4d as select name from nPlayed where ninst = (select max(ninst) from nPlayed) ;
create view ComposersOnly as (select musician from Composer) except (select musician from PlaysOn); create view q4e as select m.name from Musician m, ComposersOnly c where m.id = c.musician ; -- or create view q4e as select name from Musician where id in (select * from ComposersOnly) ; -- or create view Musicians as select m.id, m.name from Musician ; create view Composers as select m.id, m.name from Musician m, Composer c where m.id = c.musician ; create view ComposersOnly as (select * from Composers) except (select * from Musicians) ; create view Q4e as select name from ComposersOnly ;
create view Q4f as select m.name from Musician m, Member r where m.id = r.musician group by m.id having count(distinct r.musicGroup) > 1 ;
create view Q4g as select distinct m.name from Musician m, MusicGroup g, Member r, PlaysOn p where g.name = 'White Stripes' and r.musicGroup = g.id and r.musician = m.id and m.id = p.musician and p.instrument = 'drums' ;
create view SongsOnCureCDs as select s.id from Song s, MusicGroup g, MusicCD cd where s.onCD = cd.id and cd.madeBy = g.id and g.name = 'The Cure' ; create view Q4h as select m.name from Musicians m where not exists ( (select id from SongsOnCureCDs) except (select s.id from SongsOnCureCDs s, PlaysOn p where s.id = p.song and p.musician = m.id) ) ; -- some people may assume create view SongsOnCureCDs as select s.id from Song s, MusicGroup g where s.performedBy = g.id and g.name = 'The Cure' ; -- the above does not strictly answer the question => grade:B --or create view SongsOnCureCDs as select s.id from Song s, MusicGroup g, MusicCD cd where s.onCD = cd.id and cd.madeBy = g.id and g.name = 'The Cure' ; create view Q4h as select m.name from Musicians m where (select count(id) from SongsOnCureCDs) = (select count(s.id) from SongsOnCureCDs s, PlaysOn p where s.id = p.song and p.musician = m.id) ;
-- Overall strategy: -- * produce a set of CDs with more than one band -- * check that this set is empty create function oneGroupPerCD() returns boolean as $$ select count(*) = 0 from MusicCD cd, Song s where s.onCD = cd.id group by cd.id having count(distinct s.performedBy) > 1 $$ language sql;
create function playsOn(integer) returns setof Musician as $$ select distinct m.* from Song s, PlaysOn p, Musician m where s.onCD = $1 and p.song = s.id and p.musician = m.id $$ language sql; --memberOf(Musician.id,MusicGroup.id,Year) create function memberOf(integer, integer, integer) returns boolean as $$ declare _joined integer; _departed integer; begin select extract(year from m.joined),extract(year from m.departed) into _joined, _departed from Member m where m.musician = $1 and musicGroup = $2; if (_joined is null) then return false; elsif (_joined <= $3 and _departed is null) return true; elsif (_joined <= $3 and $3 < _departed) return true; else return false; end if; end; $$ language plpgsql; create function discography(groupName text) returns text as $$ declare _gid integer; _out text := ''; _mem text; _non text; _cd record; _mus record; begin select id into _gid from MusicGroup where name = groupName; if (not found) then raise exception 'No such group' end if; for _cd in select * from MusicCD where madeBy=_gid order by year loop _out := _out||'CD: '||_cd.title||' ('||_cd.year||')\n'; _mem := ''; _non := ''; for _mus in select * from playsOn(_cd.id) loop if (memberOf(_mus.id, _cd.madeBy, _cd.year) then _mem := ', '||_mus.name; else _non := ', '||_mus.name; end if; end loop; _out := _out||'Group members: ' ||substr(_mem,2,length(_mem))||'\n'; _out := _out||'Other musicians: ' ||substr(_non,2,length(_non))||'\n'; end loop; return _out; end; $$ language plpgsql;
create trigger DisbandGroup after update on GroupMember for each row execute procedure disbandGroup(); create function disbandGroup() returns trigger as $$ declare _nremaining integer; begin if (old.departed is null and new.departed is not null) then -- this is a departing band member select count(*) into _nremaining from GroupMember where musicGroup = old.musicGroup and departed is not null; if (_nremaining = 0) then update MusicGroup set disbanded = new.departed where id = old.musicGroup; end if; end if; return new; -- return value ignored for after-trigger end; $$ language plpgsql;
create trigger RenameGroup before update on MusicGroup for each row execute procedure renameGroup(); create function renameGroup() returns trigger as $$ declare _gid integer; _mid integer; _today date := CURRENT_DATE; begin if (old.name <> new.name) then select max(id) into _gid from MusicGroup; _gid := _gid + 1; insert into MusicGroup values (_gid, new.name, _today, null, old.id); new.name := old.name; new.disbanded = _today; for _mid in select musician from Member where musicGroup = old.id and departed is null loop update Member set departed = _today where musicGroup = old.id and musician = _mid; insert into Member values (_gid, _mid, _today, null); end loop; end if; return new; -- required end; $$ language plpgsql;