Databashanteraren kan förstås inte lyckas helt och hållet med att hålla databasen fri från felaktiga uppgifter. Den kan inte hoppa ut ur datorn och springa iväg och kolla upp Bengts lön. (Åtminstone inte med dagens teknik. I framtiden kanske städernas gator är fulla av databashanterare som rusar fram och tillbaka och kontrollerar löneuppgifter. Övning: Rita en bild av en gata i framtiden, med databashanterare och svävande bilar!) Men databashanteraren kan hjälpa till lite i alla fall.
Den som skapar och ansvarar för databasen (databasadministratören) kan berätta för databashanteraren vilka regler som alla data i databasen måste uppfylla. Reglerna kallas integritetsvillkor. Om nu en persons lön kan stå på två ställen, så kanske vi har integritetsvillkoret att båda de uppgifterna måste vara lika. Eller så har vi helt enkelt en regel som säger att alla löner måste ligga mellan 0 och 40000. (ABB-direktörer och chefsöverläkare får inte vara med i databasen.)
|
Integritetsvillkor ("integrity constraints" på engelska) är alltså villkor som begränsar vilka data som kan lagras i databasen. Åtminstone en del av integritetsvillkoren är egentligen också begränsningar som gäller i den riktiga världen, och inte bara i databasen, för databasen beskriver en del av världen. Om man har ett integritetsvillkor som säger att alla löner måste ligga mellan 0 och 40000, så säger man ju också att ingen människa, av de som ska vara med i databasen, har en lön som ligger utanför det intervallet.
Här är två vanliga typer av integritetsvillkor i relationsdatabaser:
Anställd
|
Avdelning
|
Även om man inte kan så mycket om databaser så verkar det väl rimligt att:
(Man måste ange not null för kolumnen Nummer, för att den ska kunna deklareras som primärnyckel, eftersom en nyckel aldrig får innehålla null-värden.)create table Avdelning (Nummer integer not null, Namn varchar(10), primary key (Nummer));
Nu har vi alltså skapat tabellen Avdelning, och talat om för databashanteraren att kolumnen Nummer är primärnyckel. Databashanteraren kommer nu att se till att varje avdelning har ett unikt nummer.
Vi stoppar in data i databasen:
De tre sista kommandona kommer att misslyckas, eftersom databashanteraren hindrar oss:insert into Avdelning values (1, 'Data'); insert into Avdelning values (2, 'Städning'); insert into Avdelning values (3, 'Ekonomi'); insert into Avdelning (Namn) values ('Lager'); /* Ger fel */ insert into Avdelning values (3, 'Lager'); /* Ger fel */ update Avdelning set Nummer = 2 where Namn = 'Data'; /* Ger fel */
Om tabellen Anställd redan finns, så kan man skriva så här för att lägga till ett referensintegritetsvillkor:create table Anställd (Nummer integer not null, Namn varchar(10), JobbarPå integer, primary key (Nummer), foreign key (JobbarPå) references Avdelning(Nummer));
Eller, om man vill ge referensintegritetsvillkoret ett eget namn ("Anställd_till_avdelning"), så att man kan ta bort det sen om man skulle behöva:alter table Anställd add foreign key (JobbarPå) references Avdelning(Nummer);
Nu har vi alltså skapat tabellen Anställd, och talat om för databashanteraren att kolumnen JobbarPå är ett referensattribut som refererar till tabellen Avdelning. Databashanteraren kommer nu att se till att varje anställd jobbar på en avdelning som faktiskt finns i avdelningstabellen. (Undantag: Vi sa aldrig not null för kolumnen JobbarPå, så man kan också lämna tomt i rutan, om en anställd inte jobbar på någon avdelning.)alter table Anställd add constraint Anställd_till_avdelning add foreign key (JobbarPå) references Avdelning(Nummer);
Ett referensattribut refererar alltid till primärnyckeln i en annan tabell (eller, ibland, samma tabell). Den har alltså samma domän som nyckeln i en annan tabell. Därför kallas ett referensattribut ibland för främmande nyckel (engelska: foreign key).
Vi stoppar in data i databasen:
De fyra sista kommandona kommer att misslyckas, eftersom databashanteraren hindrar oss:insert into Anställd values (1, 'Svea', 1); insert into Anställd values (2, 'Sten', 3); insert into Anställd (Nummer, Namn) values (3, 'Bengt'); insert into Anställd values (4, 'Sergio', 5); /* Ger fel */ update Anställd set Avdelning = 7 where Namn = 'Svea'; /* Ger fel */ delete from Avdelning where Namn = 'Data'; /* Ger fel */ update Avdelning set Number = 9 where Namn = 'Data'; /* Ger fel */
Det brukar betyda att operationen avbryts, och att ingen ändring görs i databasen. Beroende på vad man använder för databashanterare och vad det är för sorts integritetsvillkor så sker det antingen direkt när man försökte göra ändringen, eller när hela transaktionen försöker göra commit.
Men man kan också tala om för databashanteraren att den inte ska avbryta operationen, utan "laga" databasen på lämpligt sätt. Titta på referensintegritetsvillkoret från exemplet ovan:
foreign key (JobbarPå) references Avdelning(Nummer)I stället kan man skriva på följande olika sätt:
Det här betyder att om jag tar bort en avdelning som det finns anställda som jobbar på, så sätts JobbarPå för alla dessa anställda till null.
Om jag tar bort en avdelning som det finns anställda som jobbar på, så tas även dessa anställda bort.
Om jag tar bort en avdelning som det finns anställda som jobbar på, så sätts JobbarPå för alla dessa anställda till den kolumnens defaultvärde.
Om jag ändrar numret på en avdelning som det finns anställda som jobbar på, så sätts JobbarPå för alla dessa anställda avdelningens nya nummer.
Det sista kommandot tar bort städavdelningen, men också de båda konsulterna Sture och Sally som jobbade där.create table Konsult (Nummer integer not null, Namn varchar(10), InhyrdAv integer, primary key (Nummer), foreign key (InhyrdAv) references Avdelning(Nummer) on delete cascade); insert into Konsult values (3, 'Sture', 2); insert into Konsult values (4, 'Sally', 2); insert into Konsult values (5, 'Sune', 3); delete from Avdelning where namn = 'Städning'; /* Kaskad! */
Ibland talar man om allmänna semantiska integritetsvillkor (engelska: "general semantic integrity constraints"). Det är vilka villkor som helst, som beror på hur det ser ut i den verklighet som databasen ska modellera. "Semantik" har ju med "betydelse" att göra, och de här villkoren bestäms av vad databasens data betyder. Eftersom den riktiga världen kan vara hur komplicerad som helst, så kan också de här integritetsvillkoren vara hur komplicerade som helst.
Exempel på allmänna semantiska integritetsvillkor:
Det andra villkoret, om att löner bara kan höjas, är ett dynamiskt villkor (engelska: "transition constraint", dvs "ändringsvillkor"). Det spelar bara in vid ändringar i databasen, och för att kontrollera det måste man jämföra innehållet i databasen före och efter ändringen.
Faktaruta
om orden "statiskt" och "dynamiskt":
I vardagsspråket brukar "statiskt" betyda något som står still och är tråkigt, medan "dynamiskt" är något som rör på sig med stor fart. Här använder vi orden i deras mer tekniska betydelser:
|
Faktaruta
om ordet "assertion":
Det engelska ordet "assertion" betyder ungefär samma sak som svenskans "påstående", fast lite starkare: så här är det minsann, och hör sen! |
Vi tänker oss att vi använder samma exempeldatabas som i avsnittet om SQL. Villkoret att ingen får tjäna mer än sin närmaste chef kan skrivas så här:
Det är alltså ett villkor, uttryckt i SQL, som databashanteraren nu har i uppgift att kontrollera, och hela tiden se till att det är sant. Om någon transaktion försöker ändra i databasen så att någon får högre lön än sin chef, så kommer den transaktionen att avbrytas.create assertion checksalary check (not exists (select * from employee proletarian, employee boss where proletarian.boss = boss.number and proletarian.salary > boss.salary))
Dynamiska villkor, som begränsar vilka ändringar som får göras i databasen,
kan normalt inte uttryckas med en assertion. Det beror på att man
måste titta på tillståndet i databasen både före och efter ändringen
för att kunna kontrollera villkoret.
Därifrån är inte steget långt till att införa en liknande mekanism,
inte bara för integritetsvillkor, utan för vilka villkor som helst.
Man skriver en regel som anger ett villkor och en åtgärd,
och när det villkoret är uppfyllt,
så ska databashanteraren utföra åtgärden.
Då har vi det som kallas en
aktiv databas.
Om vi använder en aktiv databas
som låter oss ange ECA-regler,
kan vi skriva en ECA-regel om villkoret att löner bara kan höjas:
Om någon transaktion försöker ändra kolumnen salary i tabellen employee
så att det nya värdet blir lägre än det gamla,
så kommer regeln att utlösas, och den transaktionen kommer att avbrytas.
I en riktig aktiv databashanterare kan man göra mer än bara avbryta
transaktionen. Ofta finns så kallade
lagrade procedurer,
som är små (eller stora) programsnuttar som man kan lagra i databasen,
och som kan köras när villkorsdelen av en regel är uppfylld.
Reglerna kan därför användas till mycket annat än att bara kontrollera
integritetsvillkor. Till exempel kan en aktiv databashanterare automatiskt
beställa mer varor när lagret i en butik börjar bli tomt.
Hittills i det här avsnittet har vi bara sett den deklarativa metoden:
att man med olika former av regler talar om för
databashanteraren vad som ska gälla,
och sen är det databashanterarens uppgift att se till att
inga transaktioner kan ändra data så att integritetsvillkoren bryts.
Alternativet är att ange integritetsvillkoren procedurellt,
dvs med vanlig programkod som kontrollerar att de är uppfyllda.
Det kan vara i ett programmeringsspråk som Java eller C i ett
applikationsprogram,
eller kanske inuti en
lagrad procedur i databasen.
Programmeraren måste skriva programkod som kontrollerar varje ändring
som ska göras, så att den uppfyller alla integritetsvillkor.
Helst vill man att databashanteraren ska sköta om kontrollen,
automatiskt och utan att användare eller
applikationsprogrammerare behöver bry sig.
Det har flera fördelar:
Aktiva databaser
Ett integritetsvillkor hanteras ju av databashanteraren genom
att villkoret kontrolleras,
och om det inte är uppfyllt så sker någon typ av åtgärd.
(Skrivsättet är något förenklat jämfört med den databashanterare
som exemplet är hämtat från.
Exakt hur man skriver varierar mellan olika databashanterare.)
create exception bad_salary "Kan inte sänka lönen!";
create trigger salarycheck for employee after update as begin
if (new.salary < old.salary) then
exception bad_salary;
end;
Vem kollar villkoren? Procedurellt eller deklarativt?
Det finns två olika sätt att ange integritetsvillkoren
i en databas: procedurellt och deklarativt.
Ibland måste man förstås göra kontrollen procedurellt,
till exempel eftersom villkoren är för komplicerade för att uttrycka
i enkla regler,
eller för att man vill åtgärda brott mot reglerna på ett mer avancerat
sätt än vad databashanteraren klara av,
eller helt enkelt för att just den databashanterare man använder
inte har stöd för alla integritetsvillkor man behöver.
En möjlig uppdelning i olika typer av integritetsvillkor
Om man vill kan man dela upp integritetsvillkor i tre olika klasser,
nämligen inneboende, implicita och explicita:
Databasens interna integritet
Hittills har vi talat om integriteten för uppgifter i databasen,
och att de inte får strida mot integritetsvillkoren.
Men alla databashanterare har också olika typer av interna datastrukturer.
Till exempel finns det index,
som inte är synliga för den vanliga användaren,
men som används av databashanteraren när den söker efter data i databasen.
Indexen, och alla andra interna datastrukturer, måste förstås vara
korrekta.
Varje index måste stämma överens med den riktiga tabell som det pekar in i. Om man lägger till eller tar bort rader i tabellen, och indexet av någon anledning inte ändras för att reflektera detta, så kan databashanteraren kanske bli så förvirrad att den kraschar.
Därför måste databashanteraren vara mycket noga med att upprätthålla
integriteten på de interna datastrukturerna.
Annars kanske man inte längre kan komma åt några data alls i databasen.
Kom ihåg:
Vi tar inte upp så mycket om personlig integritet här,
men vi ska i alla fall nämna de svenska personnumren,
som ibland ger upphov till både debatt och förvirring.
Är personnummer hemliga?
Är författaren dum när han nu talar om att
hans personnummer är 631211-1658?
Nej, svenska personnummer är inte hemliga. De är inte alls hemliga.
Om du vet namn och adress på en person,
så att det går att avgöra vem det är,
så kan du ringa till skattemyndigheten och få den personens personnummer.
Du behöver inte tala om vem du är eller vad du ska ha personnumret till.
Eventuella problem och risker med personnummer handlar alltså
inte om att personnummer är hemliga,
utan om att de är unika. De fungerar som en nyckel
eller ett unikt namn, inte som ett lösenord.
Om det finns en fara med personnummer så är det
att det blir lätt att se att den där 631211-1658
som skriver databaskurser är samma person som den där
631211-1658 som har årskort på Klubb Läderhamster.
Och det kanske jag inte vill att alla ska veta.
Därför vill jag kanske inte att Klubb Läderhamster ska använda
mitt personnummer som medlemsnummer, och trycka det på medlemskortet.
"Integritet" som i "personlig integritet"
I det här avsnittet har vi pratat om "integritet"
i betydelsen "dataintegritet", som ungefär innebär
"utan inre motsägelser" eller
"stämmer med integritetsvillkoren för den här databasen".
Men som vi redan nämnt så används det svenska ordet "integritet"
ofta i en lite annorlunda betydelse,
som när man pratar om "personlig integritet".
Personlig integritet heter "privacy" på engelska,
och det betyder ungefär att uppgifter om mig,
till exempel min adress och min lön, och min religion och mitt brottsregister,
inte ska vara tillgängliga för vem som helst hur som helst.
De viktigaste begreppen
De viktigaste begreppen från det här avsnittet finns också med i
ordlistan:
integritet, dataintegritet, personlig integritet (privacy), integritetsvillkor, nyckelvillkor, referensintegritet, aktiv databas