Det här är ett avsnitt i en
webbkurs om databaser
som finns fritt tillgänglig på adressen
http://www.databasteknik.se/webbkursen/.
Senaste ändring:
24 juni 2006.
Av Thomas Padron-McCarthy. Copyright, alla rättigheter reserverade, osv. Skicka gärna kommentarer till webbkursen@databasteknik.se.
|
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.)
Faktaruta
om ordet "integritet":
Om man tittar på en science fiction-film, och någon säger till kaptenen på rymdskeppet att "the integrity of the hull has been broken", så vet man att det är allvarligt. Då har det gått hål i skrovet, och rymdskeppet håller inte tätt längre. Luften pyser ut och rymdmonstren smiter in. Snart går hela rymdskeppet i bitar. Det engelska ordet "integrity" betyder ungefär att saker hänger ihop på rätt sätt. Det svenska ordet "integritet" används ofta i en lite annorlunda betydelse, som när man pratar om "personlig integritet". Mer om det nedan. Men tills vidare menar vi med "integritet" samma sak som det engelska "integrity": att data i databasen hänger ihop på rätt sätt. |
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.
Faktaruta
om ordet "constraint":
Det engelska ordet "constrain" betyder "hindra" eller "begränsa", och "constraint" betyder "begränsning". En "integrity constraint" är just en begränsning på vad man tillåter för data i databasen. |
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 Anställd as proletär, Anställd as boss where proletär.Chef = boss.Nummer and proletär.Lön > boss.Lön))
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:
(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 Anställd after update as begin if (new.Lön < old.Lön) then exception bad_salary; end;
Om någon transaktion försöker ändra kolumnen Lön i tabellen Anställd 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:
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, dataintegritet, personlig integritet (privacy), integritetsvillkor, nyckelvillkor, referensintegritet, aktiv databas