| A | correct solution, with no problems at all |
| B | almost-correct solution, but has non-trivial syntax problems and/or some semantic problems as well |
| C | partly correct solution, but with major flaws |
| D | has some elements of a correct solution, but is basically a mess |
| E | hopeless ... has some correct syntax, not much else |
| . | did not attempt the question |
Note that some things that might be regarded as incorrect from the samples below, would be treated as correct if there is an accompanying assumption or justification.
In the examples where OrderItems are an entity, the attributes for quantity and unitPrice could equally-well be attributes of OrderItems or of the relationship between OrderItems and Books.
Use any of the recognised notations for indicating cardinality and participation constraints is allowed, not just the notation above.
Books: isbn -> title,authors,publisher,yearPublished,price,qtyInStock Customers: id -> name,address,phone OnLineCustomers: id -> username,password,email Orders: id -> customer,datePlaced,discount,dateShipped,datePaid OrderItems: orderid,itemSeq -> bookisbn,quantity,unitPriceProoof of 3NF:
If the above "obvious" FDs are the ones given, then the proof of 3NF has to say something like ...
In each table, all non-key attributes in each relation are functionally dependent on the entire key => each table is in 3NF => the schema is 3NF
This solution is awarded an A grade.
If some steps are omitted (e.g. don't say "for each table", or don't say "each table is 3NF, etc.), then B grade.
For lower grades, based on how much of the above solution was given.
It's ok (but slightly irrelevant) to mention that there is enough "linkage" between the tables so that the data can be put together again. (If we'd started from a single "super-table" and done a decomposition, this comment would make more sense).
More complex answers might consider additional *possible* FDs. If they do, each FD has to be supported by an assumption, e.g.
OnLineCustomers: id -> username,password,email [ email -> username ] must also have assumption only one account per user [ username -> email ] must also have assumption only one account per userIf these FDs are included, then ought to conclude that the schema is *not* in 3NF, using the argument:
Some tables (e.g. OnLineCustomers) have non-key attributes that are functionally dependent on other non-key attributes => this table (and thus the whole schema) is not in 3NF (in fact, not even 2NF)
A complete argument like this, is awarded an A grade.
Lower grades are awarded if omitted assumptions to justify other FDs or skipped some of the "obvious" FDs or skipped parts of the proof.
select count(distinct title||authors) from Books where qtyInStock > 0;
select title,authors from Books group by title,authors having count(distinct yearPublished) > 1;
select distinct c.email from OnLineCustomers c, Orders o where c.id=o.customer and o.dateShipped is not null and o.datePaid is null;
-- using PostgreSQL syntax select c.id,c.username,count(o.id) from OnLineCustomers c left outer join Orders o on (c.id=o.customer) group by c.id,c.username; -- using Oracle 8 syntax select c.id,c.username,count(o.id) from OnLineCustomers c, Orders o where c.id = o.customer(+) group by c.id,c.username;
update Books set price = price * 1.10 where price < 100.00;
create or replace function
shippable(ordid integer) return varchar
is
n integer;
ok varchar(10);
begin
select id into n from Orders where id=ordid;
ok := 'true';
n := 0;
for it in
(select b.isbn,i.quantity,b.qtyInStock
from OrderItems i, Books b
where i.orderid=ordid and i.bookisbn=b.isbn)
loop
n := n + 1;
if (it.quantity > it.qtyInStock) then
ok := 'false';
end if;
end loop;
return ok;
exception
when no_data_found then
dbms_output.put_line('No such order: '||ordid);
return 'false';
end;
/
-- OR
create or replace function
shippable(ordid integer) return varchar
is
n integer;
begin
select id into n from Orders where id=ordid;
select count(isbn) into n
from OrderItems i, Books b
where i.orderid=ordid and i.bookisbn=b.isbn
and i.quantity > b.qtyInStock;
return (n = 0);
exception
when no_data_found then
dbms_output.put_line('No such order: '||ordid);
return 'false';
end;
/
create or replace function
orderAmount(ordid integer) return real
is
val real;
tot real;
discnt real;
begin
select id into n from Orders where id=ordid;
select discount into discnt from Orders where id=ordid;
tot := 0.00;
for it in
(select quantity,unitprice
from OrderItems
where orderid=ordid)
loop
val := it.quantity * it.unitprice;
tot := tot + val;
end loop;
tot := tot * (1.0 - discnt);
return tot;
exception
when no_data_found then
dbms_output.put_line('No such order: '||ordid);
return null;
end;
/
-- OR
create or replace function
orderAmount(ordid integer) return real
is
tot real;
begin
select id into n from Orders where id=ordid;
select sum(i.quantity*i.unitprice*(1.0-discount)) into tot
from OrderItems i, Orders o
where i.orderid=ordid and o.id=i.orderid;
return tot;
exception
when no_data_found then
dbms_output.put_line('No such order: '||ordid);
return null;
end;
/
create trigger orderInsertTrigger before insert on Orders for each row declare nords integer; discnt real; begin select count(*) into nords from Orders o where o.customer = :new.customer and orderAmount(o.id) > 100 and (sysdate()-o.datePlaced) < 90; discnt := nords * 0.05; if (discnt > 0.3) then discnt := 0.3; end if; :new.discount := discnt; end; /
Failed operation ... C: grant select on Orders to D;
Why it fails ... because C didn't get the [select] privilege with grant option.
select insert delete update
A v v v v
B v v v v
C v v v v
D X X X v
E v v v X
select insert delete update
A v v v v
B v v v v
C X v v X
D X X X X
E v v v X