Referensattribut:
För att förenkla programmeringen av tillämpningsprogrammen har vi infört en enkel numerisk primärnyckel i hopkopplingstabellerna Har och Avser.
SQL-kommandon för att provköra:
create table Abonnenter (Anr integer not null primary key, Namn varchar(10) not null, Adress varchar(10) not null); create table "Hämtställen" (Snr integer not null primary key, Adress varchar(10) not null, Lat decimal(10,6) not null, Long decimal(10,6) not null, unique(Lat, Long)); create table "Hämtningar" (Hnr integer not null primary key, Datum date not null, Tid time, "Hämtställe" integer not null references "Hämtställen"(Snr)); create table Fakturor (Fnr integer not null primary key, Datum date not null, Belopp integer not null, Abonnent integer not null references Abonnenter(Anr)); create table Har (Id integer not null primary key, Abonnent integer not null references Abonnenter(Anr), "Hämtställe" integer not null references "Hämtställen"(Snr), Tunnor integer not null, unique(Abonnent, "Hämtställe")); create table Avser (Id integer not null primary key, "Hämtning" integer not null references "Hämtningar"(Hnr), Faktura integer not null references Fakturor(Fnr), unique("Hämtning", Faktura)); insert into Abonnenter values (1, 'Alice', 'Almby'); insert into Abonnenter values (2, 'Bengt', 'Borneo'); insert into Abonnenter values (3, 'Claes', 'Chicago'); insert into Abonnenter values (4, 'Dick', 'Dorotea'); insert into Abonnenter values (5, 'Eva-Lotta', 'Elvägen 2'); insert into "Hämtställen" values (1, 'Almby', 17.2, 33.9); insert into "Hämtställen" values (2, 'Almby', 17.4, 33.8); insert into "Hämtställen" values (3, 'Utlandet', 45.2, 17.8); insert into Har values (1, 1, 1, 1); insert into Har values (2, 1, 2, 7); insert into Har values (3, 2, 3, 2); insert into Har values (4, 3, 3, 1); insert into "Hämtningar" values (1, DATE '2010-01-10', TIME '08:17:00', 1); insert into "Hämtningar" values (2, DATE '2010-01-10', TIME '08:19:04', 2); insert into "Hämtningar" values (3, DATE '2010-01-11', TIME '09:01:31', 2); insert into "Hämtningar" values (4, DATE '2010-01-12', TIME '08:02:01', 2); insert into Fakturor values (1, DATE '2010-01-10', 1000, 1); insert into Avser values (1, 1, 1); insert into Avser values (2, 2, 1); select * from Abonnenter; select * from "Hämtställen"; select * from "Hämtningar"; select * from Fakturor; select * from Har; select * from Avser;
Vilken position (som anges med koordinaterna Lat och Long) har hämtstället på Grangrensvägen 2?
select Lat, Long from "Hämtställen" where Adress = 'Grangrensvägen 2';
b) På vilka hämtställen gjordes det hämtningar 2010-01-12 klockan 09:03? Vi vill veta adresserna för dessa hämtställen.
En lösning utan nästlade frågor:
select "Hämtställen".Adress from "Hämtställen", "Hämtningar" where "Hämtställen".Snr = "Hämtningar"."Hämtställe" and "Hämtningar".Datum = DATE '2010-01-12' and "Hämtningar".Tid = TIME '09:03:00';
En lösning med nästlade frågor:
select Adress from "Hämtställen" where Snr in (select "Hämtställe" from "Hämtningar" where Datum = DATE '2010-01-12' and Tid = TIME '09:03:00');
c) Ange namn och adress för de abonnenter som har hämtställe på Talltoppstrappan 9.
select Abonnenter.Namn, Abonnenter.Adress from Abonnenter, Har, "Hämtställen" where Abonnenter.Anr = Har.Abonnent and Har."Hämtställe" = "Hämtställen".Snr and "Hämtställen".Adress = 'Talltoppstrappan 9';
d) Jag heter Thomas Padron-McCarthy. När har mina sopor hämtats? Jag vill veta datum och tid.
select "Hämtningar".Datum, "Hämtningar".Tid from Abonnenter, Har, "Hämtställen", "Hämtningar" where Abonnenter.Anr = Har.Abonnent and Har."Hämtställe" = "Hämtställen".Snr and "Hämtställen".Snr = "Hämtningar"."Hämtställe" and Abonnenter.Namn = 'Thomas Padron-McCarthy';
Frågan ovan ger egentligen datum och tid för alla hämtningar från mitt hämtställe, så jag kan få med hämtningar som gjordes innan jag flyttade dit. Som alternativ kan man i stället gå via fakturorna, men då får man bara med de hämtningar som hunnit faktureras.
e) Vilka abonnenter har aldrig fått sina sopor hämtade? Vi vill veta de abonnenternas namn och adress.
Den här SQL-frågan ger de abonnenter som aldrig fått sina sopor hämtade, och som faktiskt har minst ett hämtställe att hämta sopor från. Det räcker för full poäng på deluppgiften.
select Abonnenter.Namn, Abonnenter.Adress from Abonnenter, Har, "Hämtställen" where Abonnenter.Anr = Har.Abonnent and Har."Hämtställe" = "Hämtställen".Snr and "Hämtställen".Snr not in (select "Hämtställe" from "Hämtningar");
Den här SQL-frågan ger de abonnenter som aldrig fått sina sopor hämtade, oavsett om de har något hämtställe att hämta sopor från.
select Abonnenter.Namn, Abonnenter.Adress from Abonnenter, Har, "Hämtställen" where Abonnenter.Anr = Har.Abonnent and Har."Hämtställe" = "Hämtställen".Snr and "Hämtställen".Snr not in (select "Hämtställe" from "Hämtningar") union select Abonnenter.Namn, Abonnenter.Adress from Abonnenter where Abonnenter.Anr not in (select Abonnent from Har);
Båda de här frågorna har samma problem som i deluppgift d, nämligen att de inte tar hänsyn till att abonnenter kan ha flyttat, och haft olika hämtställen.
f) Vilken abonnent har flest soptunnor? Vi vill veta namn och adress för den abonnenten.
Vi antar att det finns minst en abonnent med minst en soptunna, så att maxantalet inte är noll.
create view Abonnenttunnor as select Abonnenter.Anr, Abonnenter.Namn, Abonnenter.Adress, sum(Har.Tunnor) as Antal from Abonnenter, Har where Abonnenter.Anr = Har.Abonnent group by Abonnenter.Anr, Abonnenter.Namn, Abonnenter.Adress; select Namn, Adress from Abonnenttunnor where Antal = (select max(Antal) from Abonnenttunnor);
Exempelvis i fråga a används attributet Adress på det sättet, men inte attributen Lat och Long.
Notera att den nästlade versionen ovan av fråga b är ekvivalent med den onästlade, så även om det kanske inte ser ut som att attributet Hämtningar.Hämtställe används för en join i den nästlade frågan, så gör den det.
Exempel:
Ger användaren hjalmar rätt att göra sökningar i tabellen Abonnenter, och att lägga till rader i den tabellen, men inte att ta bort eller ändra rader.grant select, insert on Abonnenter to hjalmar;
Nu har Hjalmar även rätt att ändra existerande rader, men fortfarande inte att ta bort rader.grant update on Abonnenter to hjalmar;
Nu har Hjalmar inte längre rätt att ta lägga till nya rader, men han kan fortfarande både söka i tabellen och ändra existerande rader.revoke insert on Abonnenter from hjalmar;
Man kan styra åtkomsten mer finkornigt genom att skapa vyer, och dela ut rättigheter till dessa vyer, i stället för till de tabeller som de baseras på:
Nu har Hjalmar rätt att se nummer och belopp på de fakturor som är daterade idag, men inga andra kolumner, och inga andra fakturor.create view DagensFakturor as select Fnr, Belopp from Fakturor where Datum = CURRENT_DATE; grant select on DagensFakturor to hjalmar;
Nu kan Hjalmar dessutom ta bort fakturor som är daterade idag. Han kan dock varken se eller ta bort fakturor med andra datum.grant delete on DagensFakturor to hjalmar;
Man kan också ge rättigheter with grant option, vilket ger rättighet att dela ut rättigheter. Databasadministratören skriver:
Nu har Hjalmar rätt att se dagens fakturer, men även rätt att dela ut samma rättighet. Hjalmar kan nu skriva:grant select on DagensFakturor to hjalmar with grant option;
Nu har även användaren hulda rätt att se dagens fakturor.grant select on DagensFakturor to hulda;
Antag nu att databasadministratören ångrar sig, och tar tillbaka Hjalmars select-rättighet:
Nu har Hjalmar inte längre rätt att se dagens fakturor. Även Huldas rättigheter har försvunnit, eftersom hon hade fått dem via Hjalmar!revoke select on DagensFakturor from hjalmar cascade;
Här har jag valt att tolka scenariot bland annat som att varje person är antingen kapten eller sjöman, men inte båda, och att det inte finns några fiske-ubåtar. Andra tolkningar är möjliga.
b)
Tabeller, med primärnycklarna understrukna:
Exempel: Man ska flytta pengar från ett bankkonto till ett annat, vilket innebär att man drar bort pengar från det första kontot och lägger till pengar till det andra. Om den transaktionen avbryts mitt i, efter att pengarna dragits bort men innan de lagts till, har pengar försvunnit!
Exempel: Om transaktionen tar bort en rad i en tabell som andra rader refererar till med referensattribut ("främmande nycklar") kommer referensintegriteten att brytas, och databasen blir inkonsistent.
Exempel 1: En transaktion summerar behållningen på alla bankkonton. Medan den gör det genomförs också transaktionen från atomicitetsexemplet ovan, och summeringstransaktionen läser från-kontot efter att det minskats, men till-kontot innan det hunnit ökas. Det belopp som just höll på att flyttas kommer då att saknas i summeringen.
Exempel 2: En transaktion kan skriva över ändringar som en annan transaktion gjort. Den ena transaktion (kallad nummer 1) hämtar behållningen på ett bankkonto, lägger till 10, och skriver tillbaka resultatet. Den andra transaktionen (kallad nummer 2) hämtar också behållningen på samma bankkonto, lägger till 10, och skriver tillbaka resultatet. Om transaktion 1 och 2 båda hämtar behållningen, gör sina additioner, och sen skriver först transaktion 1 tillbaka den nya summan i databasen, och därefter skriver transaktion 2 tillbaka sin nya summa, så skriver transaktion 2 över transaktion 1:s ändring, och trots att båda transaktionerna lagt till 10 till bankkontot, har behållningen bara ökat med 10 och inte med 20.
Exempel: Om jag tar ut pengar i en bankomat, och det noteras i databasen, men databasservern kraschar pecis efteråt och noteringen om uttaget inte sparats ordentligt på disk, så blir jag kanske glad, men inte banken.