-- (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;