LUNDS TEKNISKA HÖGSKOLA 1(4) Institutionen för datavetenskap Lösningsförslag, tentamen i Databaser 2004-04-20 1. ER-diagram: Matsedel år vecka serveras 1..5 lagas-med Maträtt Ingrediens dag mängd Allergi förorsakas-av Barn pnr har-allergi Relationer av entitetsmängderna: Matsedlar(år, vecka) Maträtter() Ingredienser() Allergier() Barn(pNr, ) Relationer av sambanden (bara många-många-samband här, ovanligt nog): Serveras(år, vecka, maträttnamn, dag) LagasMed(maträttNamn, ingrediensnamn, mängd) FörorsakasAv(allergiNamn, ingrediensnamn) HarAllergi(pNr, allerginamn) Vi ser att relationen Serveras i översättningen fick nyckeln {år, vecka, maträttnamn, vilket innebär att man inte kan servera samma maträtt under två dagar i samma vecka. Det kanske är rimligt; om vi inte tycker att det är rimligt så kan vi ändra nyckeln till {år, vecka, dag. Då borde vi också ändra ER-modellen. Primärnycklarna i relationerna är understrukna. De främmande nycklarna framgår av en på attributen; till exempel är (år, vecka) i Serveras främmande nyckel till (år, vecka) i Matsedlar och maträttnamn i Serveras till i Maträtter. Det finns inte några andra funktionella beroenden än nyckelberoendena, så samtliga relationer är i BCNF.
2(4) Namn och personnummer för alla barn som inte kan äta pannkakor: select distinct Barn.pNr, Barn. from Barn, HarAllergi, FörorsakasAv, LagasMed where Barn.pNr = HarAllergi.pNr and HarAllergi.allergiNamn = FörorsakasAv.AllergiNamn and FörorsakasAv.ingrediensNamn = LagasMed.ingrediensNamn and LagasMed.maträttNamn = pannkakor ; SQL-satsen som är svaret på fråga d) liknar ovanstående sats. 2. ER-diagram: Person pnbr name address owns Car involved-in licensenbr model year Accident reportnbr date description damageamount Skapa tabellerna Accidents och InvolvedInAccidents: create table Accidents ( reportnbr varchar(10), adate char(10) not null, description varchar(100) not null default okänd, primary key (reportnbr) ); create table InvolvedInAccidents ( licensenbr char(6) not null, reportnbr varchar(10) not null, damageamount decimal(8,2) default 0, primary key (licensenbr, reportnbr), foreign key (licensenbr) references Cars(licenseNbr), foreign key (reportnbr) references Accidents(reportNbr) ); Övriga SQL-satser: select count(distinct licensenbr) from InvolvedInAccidents, Accidents where adate like 2003% and Accidents.reportNbr = InvolvedInAccidents.reportNbr; select adate, description from Persons, Owns, InvolvedInAccidents, Accidents where Persons.name = Bo Ek and Persons.pNbr = Owns.pNbr and Owns.licenseNbr = InvolvedInAccidents.licenseNbr and InvolvedInAccidents.reportNbr = Accidents.reportNbr; insert into Accidents(reportNbr, date, description) values ( XB2411, 2004-04-20, krock ); insert into InvolvedInAccidents(licenseNbr, reportnbr) values( ABC123, XB2411 ); insert into InvolvedInAccidents(licenseNbr, reportnbr) values( XYZ789, XB2411 ); update InvolvedInAccidents set damageamount = 10000 where reportnbr = XB2411 and licensenbr = XYZ789 ;
3(4) 3. För att beräkna nycklarna beräknar vi höljet av alla möjliga delmängder av attributen under de givna funktionella beroendena. Beräkningar, delmängder med ett attribut: {A+ => {B+ => {C+ => {D+ => {E+ => {A => {A, B, C (FD1) => {A, B, C, D (FD3) => {A, B, C, D, E (FD2) {B => {B, D (FD3) {C {D {E => {E, A (FD4) => {E, A, B, C (FD1) => {E, A, B, C, D (FD3) {A och {E är nycklar. Delmängder med två attribut som inte innehåller A eller E: {B, C+ =>{B, C => {B, C, D (FD3) => {B, C, D, E (FD2) => {B, C, D, E, A (FD4) {B, D+ =>{B, D {C, D+ =>{C, D => {C, D, E (FD2) => {C, D, E, A (FD4) => {C, D, E, A, B (FD1) Här ser vi att {B, C och {C, D är nycklar. Det finns inga delmängder med tre eller flera attribut som inte innehåller någon av nycklarna, så vi har alltså hittat samtliga nycklar. Relationen är inte i BCNF eftersom vänsterledet B i det funktionella beroendet FD3, B D, inte är en supernyckel. Däremot är relationen i 3NF eftersom högerledet D i beroendet ingår i en nyckel. Vi delar upp i mindre relationer utgående från FD3 och får: R1(B, D) R2(A, B, C, E) R1 är i BCNF eftersom relationen bara innehåller två attribut. För att ta reda på om R2 är i BCNF måste vi avgöra vilka funktionella beroenden som gäller i R2. Vi beräknar höljen på samma sätt som nyss och använder de ursprungliga funktionella beroendena FD1-FD4. Vi finner bara nyckelberoendena A BCE, E ABC och BC AE. Eftersom alla funktionella beroenden är nyckelberoenden är alltså också R2 i BCNF. Bevis av påståendet att alla relationer med två attribut är i BCNF: en relation är i BCNF om vänsterledet i samtliga funktionella beroenden är supernycklar. Det finns fyra olika fall vad gäller funktionella beroenden som kan gälla i relationen, som vi betecknar med R(A, B): a) Inga funktionella beroenden. Då är {A, B nyckel och relationen är i BCNF. b) A B. Då är A nyckel, och relationen är i BCNF. c) B A. Då är B nyckel, och relationen är i BCNF. d) A B och B A. Då är både A och B nycklar, och relationen är i BCNF.
4(4) 4. Anmärkning: eftersom damageamount är decimal(8.2), eller åtminstone ett numeriskt värde av något slag, borde programmet hämtat värdet med getdouble eller något liknande, inte med getstring. Formellt fel: vi har glömt att JDBC-metoderna kan generera SQLExceptions som vi måste ta hand om. Ineffektivitet: det är onödigt att hämta alla tupler från relationen och sedan testa vilka man vill ha; i stället skall man bara hämta dem som man behöver. Omskriven metod: public void printaccidentsinvolving(string carnbr) { System.out.println( Olyckor där + carnbr + varit inblandad ); System.out.println( Rapportnummer\tSkadebelopp ); System.out.println( ------------------------------------ ); try { Statement stmt = conn.createstatement(); ResultSet rs = stmt.executequery( select reportnbr, damageamount + from InvolvedInAccidents + where licensenbr = + carnbr + ); while (rs.next()) System.out.println(rs.getString( reportnbr ) + \t + rs.getstring( damageamount )); stmt.close(); catch (SQLException e) { System.err.println(e); e.printstacktrace(); finally { try { stmt.close(); catch (SQLException e2) { System.err.println( Could not finally close statement, impossible... ); System.err.println(e2); e2.printstacktrace(); 5. En lagrad procedur är programkod som exekveras av databashanteraren. Programmen skrivs till exempel i SQL/PSM (Persistent Stored Modules), som är SQL-standardspråket för lagrade procedurer. Men de flesta databasleverantörer följer inte standarden utan har någon egen variant av språk, till exempel Oracles PL/SQL. Eftersom man i SQL inte kan uttrycka programmeringskonstruktioner som if- och whilesatser eller rekursion är det ibland nödvändigt att använda lagrade procedurer som är skrivna i programspråk som innehåller dessa konstruktioner. Alternativet skulle vara att göra beräkningarna i tillämpningsprogrammet, till exempel i Java, men det skulle kunna medföra att mycket data behövde hämtas från databasen. Med en lagrad procedur exekveras allting inuti databasen. 6. Alla XML-dokument består av text. Strukturen hos dokumentet anger man med taggar en starttagg markerar att information av ett visst slag börjar, en sluttagg att denna information slutar. Exempel: <Person> <name>bo Ek</name> <address>lund</address> </Person>. I well-formed XML kan man använda vilka taggar som helst. I valid XML har man specificerat vilka taggar som får förekomma och i vilken ordning de skall skrivas. Denna specifikation skriver man i en DTD, Document Type Definition.
LUNDS TEKNISKA HÖGSKOLA 1(4) Institutionen för datavetenskap Lösningsförslag, tentamen i Databaser 2004-04-20 1. ER-diagram: Matsedel år vecka serveras 1..5 lagas-med Maträtt Ingrediens dag mängd Allergi förorsakas-av Barn pnr har-allergi Relationer av entitetsmängderna: Matsedlar(år, vecka) Maträtter() Ingredienser() Allergier() Barn(pNr, ) Relationer av sambanden (bara många-många-samband här, ovanligt nog): Serveras(år, vecka, maträttnamn, dag) LagasMed(maträttNamn, ingrediensnamn, mängd) FörorsakasAv(allergiNamn, ingrediensnamn) HarAllergi(pNr, allerginamn) Vi ser att relationen Serveras i översättningen fick nyckeln {år, vecka, maträttnamn, vilket innebär att man inte kan servera samma maträtt under två dagar i samma vecka. Det kanske är rimligt; om vi inte tycker att det är rimligt så kan vi ändra nyckeln till {år, vecka, dag. Då borde vi också ändra ER-modellen. Primärnycklarna i relationerna är understrukna. De främmande nycklarna framgår av en på attributen; till exempel är (år, vecka) i Serveras främmande nyckel till (år, vecka) i Matsedlar och maträttnamn i Serveras till i Maträtter. Det finns inte några andra funktionella beroenden än nyckelberoendena, så samtliga relationer är i BCNF.
2(4) Namn och personnummer för alla barn som inte kan äta pannkakor: select distinct Barn.pNr, Barn. from Barn, HarAllergi, FörorsakasAv, LagasMed where Barn.pNr = HarAllergi.pNr and HarAllergi.allergiNamn = FörorsakasAv.AllergiNamn and FörorsakasAv.ingrediensNamn = LagasMed.ingrediensNamn and LagasMed.maträttNamn = pannkakor ; SQL-satsen som är svaret på fråga d) liknar ovanstående sats. 2. ER-diagram: Person pnbr name address owns Car involved-in licensenbr model year Accident reportnbr date description damageamount Skapa tabellerna Accidents och InvolvedInAccidents: create table Accidents ( reportnbr varchar(10), adate char(10) not null, description varchar(100) not null default okänd, primary key (reportnbr) ); create table InvolvedInAccidents ( licensenbr char(6) not null, reportnbr varchar(10) not null, damageamount decimal(8,2) default 0, primary key (licensenbr, reportnbr), foreign key (licensenbr) references Cars(licenseNbr), foreign key (reportnbr) references Accidents(reportNbr) ); Övriga SQL-satser: select count(distinct licensenbr) from InvolvedInAccidents, Accidents where adate like 2003% and Accidents.reportNbr = InvolvedInAccidents.reportNbr; select adate, description from Persons, Owns, InvolvedInAccidents, Accidents where Persons.name = Bo Ek and Persons.pNbr = Owns.pNbr and Owns.licenseNbr = InvolvedInAccidents.licenseNbr and InvolvedInAccidents.reportNbr = Accidents.reportNbr; insert into Accidents(reportNbr, date, description) values ( XB2411, 2004-04-20, krock ); insert into InvolvedInAccidents(licenseNbr, reportnbr) values( ABC123, XB2411 ); insert into InvolvedInAccidents(licenseNbr, reportnbr) values( XYZ789, XB2411 ); update InvolvedInAccidents set damageamount = 10000 where reportnbr = XB2411 and licensenbr = XYZ789 ;
3(4) 3. För att beräkna nycklarna beräknar vi höljet av alla möjliga delmängder av attributen under de givna funktionella beroendena. Beräkningar, delmängder med ett attribut: {A+ => {B+ => {C+ => {D+ => {E+ => {A => {A, B, C (FD1) => {A, B, C, D (FD3) => {A, B, C, D, E (FD2) {B => {B, D (FD3) {C {D {E => {E, A (FD4) => {E, A, B, C (FD1) => {E, A, B, C, D (FD3) {A och {E är nycklar. Delmängder med två attribut som inte innehåller A eller E: {B, C+ =>{B, C => {B, C, D (FD3) => {B, C, D, E (FD2) => {B, C, D, E, A (FD4) {B, D+ =>{B, D {C, D+ =>{C, D => {C, D, E (FD2) => {C, D, E, A (FD4) => {C, D, E, A, B (FD1) Här ser vi att {B, C och {C, D är nycklar. Det finns inga delmängder med tre eller flera attribut som inte innehåller någon av nycklarna, så vi har alltså hittat samtliga nycklar. Relationen är inte i BCNF eftersom vänsterledet B i det funktionella beroendet FD3, B D, inte är en supernyckel. Däremot är relationen i 3NF eftersom högerledet D i beroendet ingår i en nyckel. Vi delar upp i mindre relationer utgående från FD3 och får: R1(B, D) R2(A, B, C, E) R1 är i BCNF eftersom relationen bara innehåller två attribut. För att ta reda på om R2 är i BCNF måste vi avgöra vilka funktionella beroenden som gäller i R2. Vi beräknar höljen på samma sätt som nyss och använder de ursprungliga funktionella beroendena FD1-FD4. Vi finner bara nyckelberoendena A BCE, E ABC och BC AE. Eftersom alla funktionella beroenden är nyckelberoenden är alltså också R2 i BCNF. Bevis av påståendet att alla relationer med två attribut är i BCNF: en relation är i BCNF om vänsterledet i samtliga funktionella beroenden är supernycklar. Det finns fyra olika fall vad gäller funktionella beroenden som kan gälla i relationen, som vi betecknar med R(A, B): a) Inga funktionella beroenden. Då är {A, B nyckel och relationen är i BCNF. b) A B. Då är A nyckel, och relationen är i BCNF. c) B A. Då är B nyckel, och relationen är i BCNF. d) A B och B A. Då är både A och B nycklar, och relationen är i BCNF.
4(4) 4. Anmärkning: eftersom damageamount är decimal(8.2), eller åtminstone ett numeriskt värde av något slag, borde programmet hämtat värdet med getdouble eller något liknande, inte med getstring. Formellt fel: vi har glömt att JDBC-metoderna kan generera SQLExceptions som vi måste ta hand om. Ineffektivitet: det är onödigt att hämta alla tupler från relationen och sedan testa vilka man vill ha; i stället skall man bara hämta dem som man behöver. Omskriven metod: public void printaccidentsinvolving(string carnbr) { System.out.println( Olyckor där + carnbr + varit inblandad ); System.out.println( Rapportnummer\tSkadebelopp ); System.out.println( ------------------------------------ ); try { Statement stmt = conn.createstatement(); ResultSet rs = stmt.executequery( select reportnbr, damageamount + from InvolvedInAccidents + where licensenbr = + carnbr + ); while (rs.next()) System.out.println(rs.getString( reportnbr ) + \t + rs.getstring( damageamount )); stmt.close(); catch (SQLException e) { System.err.println(e); e.printstacktrace(); finally { try { stmt.close(); catch (SQLException e2) { System.err.println( Could not finally close statement, impossible... ); System.err.println(e2); e2.printstacktrace(); 5. En lagrad procedur är programkod som exekveras av databashanteraren. Programmen skrivs till exempel i SQL/PSM (Persistent Stored Modules), som är SQL-standardspråket för lagrade procedurer. Men de flesta databasleverantörer följer inte standarden utan har någon egen variant av språk, till exempel Oracles PL/SQL. Eftersom man i SQL inte kan uttrycka programmeringskonstruktioner som if- och whilesatser eller rekursion är det ibland nödvändigt att använda lagrade procedurer som är skrivna i programspråk som innehåller dessa konstruktioner. Alternativet skulle vara att göra beräkningarna i tillämpningsprogrammet, till exempel i Java, men det skulle kunna medföra att mycket data behövde hämtas från databasen. Med en lagrad procedur exekveras allting inuti databasen. 6. Alla XML-dokument består av text. Strukturen hos dokumentet anger man med taggar en starttagg markerar att information av ett visst slag börjar, en sluttagg att denna information slutar. Exempel: <Person> <name>bo Ek</name> <address>lund</address> </Person>. I well-formed XML kan man använda vilka taggar som helst. I valid XML har man specificerat vilka taggar som får förekomma och i vilken ordning de skall skrivas. Denna specifikation skriver man i en DTD, Document Type Definition.