Databasernas grunder

vår 2026

1. Inledning

Vad är en databas?

En databas (database) är en samling data som är lagrad på en dator och som man kan söka i samt ändra. Några exempel på databaser är:

Det finns numera en mängd olika databaser. De flesta av oss använder dagligen flera tjänster som är kopplade till databaser.

Utmaningar med databaser

Det finns flera utmaningar som hör ihop med den tekniska implementeringen av databaser, till exempel:

Databashanterare

En databashanterare (ett databashanteringssystem) sköter innehållet i databasen och erbjuder de funktioner som användaren behöver för att kunna söka fram och ändra data. Observera att ordet databas också ofta används synonymt med databashanterare/databashanteringssystem.

De flesta databaser som används i dag bygger på relationsmodellen och SQL-språket, vars teoretiska grund utvecklades på 1970-talet. Under denna kurs bekantar vi oss med databasen SQLite som lämpar sig väl för att lära sig SQL. Exempel på andra SQL-databaser är MySQL och PostgreSQL.

Termen NoSQL syftar på databaser som inte bygger på relationsmodellen och SQL-språket. MongoDB och Redis är exempel på sådana databaser. NoSQL-databaser behandlas dock inte i någon större utsträckning under denna kurs.

Gör-det-själv-databas

Innan vi bekantar oss med befintliga databashanterare är det bra att fundera på vilket behov sådana system fyller. Varför skulle vi inte helt enkelt kunna implementera en egen databas, till exempel genom att lagra databasens innehåll i en textfil i lämpligt format?

Exempel

Vi vill i databasen spara data om kursdeltagarnas uppgiftslösningar. När en kursdeltagare skickar in en lösning sparas studerandenumret, uppgiftsnumret, tidpunkten för inskickandet av lösningen och lösningens poäng i databasen.

Ett enkelt sätt att implementera databasen är att skapa en textfil där varje rad motsvarar en inlämning. Varje gång en studerande skickar in en lösning läggs en ny rad till i filen. I praktiken kunde vi spara databasen som ett CSV-format i filen database.csv på följande sätt:

012121212;1;2020-05-03 12:50:32;100
012341234;1;2020-05-03 14:02:12;20
012121212;2;2020-05-04 14:05:50;70
012121212;3;2020-05-04 14:43:12;0
012341234;2;2020-05-04 10:15:23;0
012341234;2;2020-05-04 16:40:39;0
013371337;1;2020-05-06 18:11:13;0
012341234;2;2020-05-07 10:02:15;100

I en CSV-fil skiljer ett visst avgränsningstecken fälten åt på varje rad. I det här fallet används semikolon ; som avgränsare. Till exempel visar filens första rad att studerande 012121212 har lämnat in en lösning för uppgift 1 och fått 100 poäng för lösningen.

Om vi nu exempelvis vill föra statistik där vi ser hur många inlämningar varje studerande har gjort, kan vi göra det i Python på följande sätt:

stats = {}

for line in open("database.csv"):
    student_id = line.split(";")[0]

    if student_id not in stats:
        stats[student_id] = 0

    stats[student_id] += 1

print(stats)

Koden ger följande resultat:

{'012121212': 3, '012341234': 4, '013371337': 1}

Detta betyder att studerande 012121212 har skickat in tre lösningar, studerande 012341234 har skickat in fyra lösningar och studerande 013371337 har skickat in en lösning.

Eventuella problemsituationer

I princip fungerar en sådan här databas som använder en CVS-fil. Problem kan dock uppstå:

Effektivitet

När datamängden växer kan det dröja att söka data i en CSV-fil. Det beror på att vi i de flesta situationer måste gå igenom hela filen från början till slut för att hitta det vi söker efter.

Om vi exempelvis vill ta reda på hur många poäng studerande 012341234 har fått för uppgift 2, måste vi gå igenom alla rader i filen för att hitta de rätta raderna. De rader som hör till en viss studerande kan ligga på flera olika ställen i filen och vi vet inte på förhand var de finns.

Att gå igenom filen är inget problem om den är liten. Det går snabbt att gå igenom en fil på till exempel hundra rader. Att gå igenom alla rader i en större fil för att få fram viss data är däremot tidskrävande.

Samtidig åtkomst av data

Vad händer om två studerande skickar in en lösning samtidigt? I det fallet borde två rader med data läggas till i slutet av filen på följande sätt:

012341234;3;2020-05-07 15:42:02;0
013371337;7;2020-05-07 15:42:02;0

Resultatet kan dock bli följande:

012341234;3;2020013371337;7;2020-05-07 15:42:02;0
-05-07 15:42:02;0

En sådan situation kan uppstå när två processer som körs samtidigt ändrar filens innehåll. Den första processen lägger först till följande i slutet av filen: 012341234;3;2020, varpå den andra processen lägger till följande mitt i filen: 013371337;7;2020-05-07 15:42:02;0. Slutligen lägger den första processen till följande: -05-07 15:42:02;0. Detta leder till att filen tappar sin struktur.

När data läggs till en fil är det inte självklart att det lyckas som önskat om någon annan försöker lägga till data samtidigt. Här har filsystemet, mängden data och sättet på hur filen hanteras betydelse.

Oväntade situationer

Låt oss betrakta en situation där vi vill radera studerandes 012341234 inlämningar från databasen. Ett sätt på vilket detta kan göras är att först läsa in alla rader i minnet och sedan skriva tillbaka endast de rader som inte tillhör studerande 012341234.

Vad händer om strömmen går när vi endast har hunnit skriva tillbaka hälften av raderna? När vi omstartar datorn inser vi att filen bara innehåller hälften av raderna och att resten av raderna har försvunnit. Vi har nu ingen möjlighet att få tillbaka de förlorade raderna.

Vad lär vi oss utav detta exempel?

En enkel textfil är i sig inte ett dåligt sätt att lagra data på, men den lämpar sig inte för alla användningsändamål. Vi behöver därför separata databashanterare som vi kommer att bekanta oss med under denna kurs.

Utvecklarna av databashanterare har noggrant övervägt hur ett system bör utformas för att ge effektiv åtkomst till data, undvika problem när flera användare samtidigt använder systemet och säkerställa att data inte går förlorad i oväntade situationer. När vi använder en databashanterare behöver vi alltså inte själva ta hand om allt detta.

Relationsmodellen och SQL-språket

Under denna kurs bekantar vi oss med databaser genom relationsmodellen och SQL-språket. De grundläggande principerna är:

  1. All data lagras i tabeller som består av rader och raderna kan hänvisa till varandra.
  2. Databasanvändaren hanterar data med hjälp av SQL-språket, vilket döljer detaljerna om hur databasen fungerar internt.

Databasens struktur

En databas består av tabeller (table) som har fasta kolumner (column). Data lagras i tabellerna som rader (row). Varje rad har ett specifikt värde i varje kolumn. Varje tabell innehåller samlad data gällande ett visst ämne.

Nedan finns ett exempel på en databas som kunde användas för en nätbutik. Tabellerna Products, Customers och Purchases innehåller data om produkterna, kunderna och innehållet i deras köpkorgar.

id name price
1 rädisa 7
2 morot 5
3 rova 4
4 kålrot 8
5 selleri 4
id name
1 Uolevi
2 Maija
3 Aapeli
customer_id product_id
1 2
1 5
2 1
2 4
2 5

Exempelvis innehåller tabellen Products kolumnerna id, name och price. Tabellen innehåller för tillfället fem rader med data som beskriver de produkter som finns i databasen.

I tabellerna Products och Customers har varje rad ett unikt ID-nummer som kan användas för att referera till raden. Detta är ett vanligt sätt att utforma databaser på. Tack vare detta kan vi i tabellen Purchases, med hjälp av ID-nummeren, visa vilka produkter respektive kund har valt. I detta exempel innehåller Uolevis köpkorg en morot och en selleri. Maijas köpkorg innehåller en rädisa, en kålrot och en selleri. Aapelis köpkorg är tom.

SQL-språket

SQL (Structured Query Language) är ett etablerat sätt att hantera innehållet i en databas. Språket innehåller kommandon som för databasanvändaren (till exempel en programmerare som arbetar med databasen) möjliggör att lägga till, hämta, ändra och ta bort data.

SQL-kommandon består av nyckelord (som SELECT och WHERE), tabellnamn, kolumnnamn och andra värden. Till exempel kommandot

SELECT price FROM Products WHERE name = 'rädisa';

hämtar priset på rädisan från databasens produkter. Kommandot avslutas med ett semikolon ;. Vi kan fritt använda mellanslag och radbrytningar. Vi kan också skriva kommandot på flera rader, till exempel på följande sätt:

SELECT price
FROM Products
WHERE name = 'rädisa';
SELECT
  price
FROM
  Products
WHERE
  name = 'rädisa';

Vi bekantar oss med SQL-språket mer ingående i kapitlen 2–4.

SQL-språket utvecklades på 1970-talet och det bär på många drag från programmering från förr i tiden som kännetecknas av bland annat att:

Det finns standarder för SQL-språket som försöker ge en gemensam grund för språket. I praktiken fungerar dock varje databassystems SQL-implementation på sitt eget sätt. Under kursen fokuserar vi på de egenskaper i SQL som finns tillgängliga i de flesta databaser.

Databasens interna funktion

Databashanterarens uppgift är att hantera de SQL-kommandon som användaren anger. Till exempel, om användaren ger ett kommando som hämtar data från databasen ska databashanteraren hitta ett effektivt sätt att bearbeta kommandot och returnera resultaten till användaren så snabbt som möjligt.

Det fina med SQL-språket är att användaren bara behöver beskriva vilken data som ska hämtas ur databasen, varefter databassystemet sköter om resten av arbetet med att hämta uppgifterna från databasen. Detta är smidigt eftersom användaren inte behöver veta något om databasens interna funktioner, utan kan lita på databashanteraren.

Det är svårt att implementera ett databassystem eftersom systemet måste kunna hantera SQL-kommandon, säkerställa att allt fungerar korrekt när flera användare är aktiva och kunna hantera oväntade situationer. Under kursen bekantar vi oss med databaser främst ur användarens perspektiv och går inte desto djupare in på databasernas interna funktioner.