Tabeller – hjärtat i databasen
Tabeller är fundamentet i relationsdatabaser. Varje tabell representerar en typ av entitet (studenter, produkter, ordrar) och består av kolumner (egenskaper) och rader (enskilda poster).
TL;DR
- Varje tabell behöver en primärnyckel för att unikt identifiera rader
- Välj rätt datatyper för varje kolumn
- Använd constraints (NOT NULL, UNIQUE, DEFAULT) för datakvalitet
- Skapa index för kolumner som ofta söks eller jointas på
- Planera relationer mellan tabeller med foreign keys
Vad är en tabell?
En tabell är som ett kalkylblad, men med strikta regler:
| StudentId | FirstName | LastName | |
|---|---|---|---|
| 1 | Anna | Svensson | anna@example.com |
| 2 | Bengt | Larsson | bengt@example.com |
| 3 | Cecilia | Nilsson | cecilia@example.com |
- Kolumner (vertikalt): definierar vilken typ av data som lagras
- Rader (horisontellt): enskilda poster/rader med faktisk data
- Primärnyckel (StudentId): unikt identifierar varje rad
Skapa en tabell
CREATE TABLE Students (
StudentId INTEGER PRIMARY KEY,
FirstName NVARCHAR(50) NOT NULL,
LastName NVARCHAR(50) NOT NULL,
Email NVARCHAR(100) UNIQUE,
EnrollmentDate DATE NOT NULL DEFAULT CURRENT_DATE,
IsActive BIT NOT NULL DEFAULT 1
);
Förklaring av constraints:
PRIMARY KEY– unikt ID för varje rad, kan inte vara NULLNOT NULL– kolumnen måste ha ett värdeUNIQUE– värdet måste vara unikt (ingen annan kan ha samma email)DEFAULT– standardvärde om inget anges vid INSERT
Vanliga datatyper och när de används
| Datatyp | Användning | Exempel |
|---|---|---|
INTEGER / BIGINT | ID-nummer, räknare | StudentId, Quantity |
DECIMAL(12,2) | Pengar, exakta tal | Price, TotalAmount |
NVARCHAR(50) | Text med Unicode | FirstName, Email |
TEXT | Lång text | Description, Notes |
BIT / BOOLEAN | Ja/Nej-flaggor | IsActive, HasPaid |
DATE | Datum | BirthDate, OrderDate |
DATETIME | Datum + tid | CreatedAt, UpdatedAt |
Se kapitlet SQL-datatyper för fullständig referens.
Identitetskolumner – auto-inkrementerande ID
Identitetskolumner genererar automatiskt nästa ID-nummer vid varje INSERT. Syntaxen varierar mellan databaser:
| Databas | Kodexempel |
|---|---|
| SQL Server | StudentId INT IDENTITY(1,1) PRIMARY KEY |
| MySQL | StudentId INT AUTO_INCREMENT PRIMARY KEY |
| PostgreSQL | StudentId SERIAL PRIMARY KEY |
| SQLite | StudentId INTEGER PRIMARY KEY AUTOINCREMENT |
Exempel:
-- SQLite
CREATE TABLE Students (
StudentId INTEGER PRIMARY KEY AUTOINCREMENT,
FirstName TEXT NOT NULL
);
-- Första INSERT får StudentId = 1
INSERT INTO Students (FirstName) VALUES ('Anna');
-- Andra INSERT får StudentId = 2 automatiskt
INSERT INTO Students (FirstName) VALUES ('Bengt');
Resulterande tabell:
| StudentId | FirstName |
|---|---|
| 1 | Anna |
| 2 | Bengt |
Ändra en befintlig tabell
ALTER TABLE låter dig modifiera tabellstrukturen efter att den skapats.
Lägg till kolumn
ALTER TABLE Students
ADD COLUMN PhoneNumber NVARCHAR(20);
Före:
| StudentId | FirstName |
|---|---|
| 1 | Anna |
Efter:
| StudentId | FirstName | PhoneNumber |
|---|---|---|
| 1 | Anna | NULL |
Nya kolumner får NULL som standardvärde för befintliga rader.
Ändra kolumn
-- SQL Server / PostgreSQL
ALTER TABLE Students
ALTER COLUMN Email NVARCHAR(150) NOT NULL;
-- MySQL
ALTER TABLE Students
MODIFY COLUMN Email VARCHAR(150) NOT NULL;
Ta bort kolumn
ALTER TABLE Students
DROP COLUMN PhoneNumber;
Varning: Data i kolumnen försvinner permanent. Gör alltid backup först!
Planera ändringar:
- Testa i dev-miljö först
- Säkerhetskopiera produktionsdata
- Kör ändringar under lågtrafik
- Verifiera att applikationen fungerar
Index – snabbare sökningar
Index fungerar som ett register i boken – istället för att läsa varje sida hittar du snabbt rätt plats.
Utan index:
SELECT * FROM Students WHERE LastName = 'Svensson';
-- Databasen måste scanna ALLA rader (slow scan)
Med index:
CREATE INDEX IX_Students_LastName ON Students(LastName);
SELECT * FROM Students WHERE LastName = 'Svensson';
-- Databasen använder index för att hoppa direkt till Svensson (index seek)
Prestandaskillnad:
- Utan index: 10 000 rader → 10 000 läsningar
- Med index: 10 000 rader → 10-50 läsningar
När ska du indexera?
Indexera kolumner som:
- Används i WHERE-villkor (
WHERE Email = ...) - Används i JOIN (
ON s.StudentId = e.StudentId) - Används i ORDER BY (
ORDER BY LastName) - Är foreign keys
Indexera INTE:
- Små tabeller (<1000 rader)
- Kolumner som sällan används i queries
- Kolumner med mycket få unika värden (t.ex. BIT/BOOLEAN)
-- Enkelt index
CREATE INDEX IX_Students_Email ON Students(Email);
-- Kombinerat index (för queries som filtrerar på båda)
CREATE INDEX IX_Students_Name ON Students(LastName, FirstName);
Kostnad av index:
- Snabbare läsningar ✅
- Långsammare INSERT/UPDATE/DELETE ❌ (index måste uppdateras)
- Mer diskutrymme ❌
Normalisering – undvik dataredundans
Normalisering är processen att organisera data för att minska upprepning.
Onormaliserat (dåligt):
| OrderId | CustomerName | CustomerEmail | ProductName | Price |
|---|---|---|---|---|
| 1 | Anna | anna@example.com | Laptop | 8999 |
| 2 | Anna | anna@example.com | Mus | 299 |
Problem: Annas email upprepas. Om hon byter email måste vi uppdatera flera rader.
Normaliserat (bra):
Customers:
| CustomerId | Name | |
|---|---|---|
| 1 | Anna | anna@example.com |
Orders:
| OrderId | CustomerId | ProductName | Price |
|---|---|---|---|
| 1 | 1 | Laptop | 8999 |
| 2 | 1 | Mus | 299 |
Fördelar:
- Annas email finns på EN plats
- Mindre diskutrymme
- Enklare att uppdatera
Grundregler:
- 1NF – varje cell innehåller ett enda värde (inga kommaseparerade listor)
- 2NF – alla kolumner beror på hela primärnyckeln
- 3NF – inga transitiva beroenden (postnummer beror på stad → egen tabell)
Relationer mellan tabeller – Foreign Keys
Foreign keys skapar relationer och säkerställer dataintegritet.
Exempel: Students och Enrollments
CREATE TABLE Students (
StudentId INTEGER PRIMARY KEY,
FirstName TEXT NOT NULL
);
CREATE TABLE Courses (
CourseId INTEGER PRIMARY KEY,
Title NVARCHAR(100) NOT NULL
);
CREATE TABLE Enrollments (
EnrollmentId INTEGER PRIMARY KEY,
StudentId INTEGER NOT NULL,
CourseId INTEGER NOT NULL,
EnrolledOn DATE NOT NULL DEFAULT CURRENT_DATE,
-- En student kan bara anmäla sig till samma kurs EN gång
UNIQUE (StudentId, CourseId),
-- Foreign keys säkerställer att StudentId och CourseId finns
FOREIGN KEY (StudentId) REFERENCES Students(StudentId),
FOREIGN KEY (CourseId) REFERENCES Courses(CourseId)
);
Vad händer vid foreign key-brott?
-- Försök infoga enrollment för student som inte finns
INSERT INTO Enrollments (StudentId, CourseId) VALUES (999, 1);
-- ERROR: FOREIGN KEY constraint failed
Fördelar med foreign keys:
- Förhindrar “orphaned” data (enrollments utan student)
- Dokumenterar relationer mellan tabeller
- Kan sätta upp CASCADE (automatisk radering av relaterad data)
Best practices
Namngivning:
- Använd beskrivande kolumnnamn (
CreatedAt, inteCol1ellerdt) - Välj singular (
Student) eller plural (Students) – var konsekvent - PascalCase för tabeller, camelCase eller snake_case för kolumner
Datatyper:
- Använd
DECIMALför pengar (inteFLOAT– avrundningsfel!) NVARCHARför text med specialtecken (Unicode)BIT/BOOLEANför flaggor (inteINTför sant/falskt)
Constraints:
- Alltid
PRIMARY KEYpå varje tabell NOT NULLför obligatoriska fältUNIQUEför värden som inte får upprepas (email, personnummer)DEFAULTför kolumner med standardvärden
Audit trail:
CREATE TABLE Orders (
OrderId INTEGER PRIMARY KEY,
Amount DECIMAL(12,2) NOT NULL,
CreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP,
UpdatedAt DATETIME DEFAULT CURRENT_TIMESTAMP
);
Lägg till CreatedAt och UpdatedAt för att spåra när rader skapas/ändras.
Sammanfattning
En väldesignad tabell har:
- Primärnyckel – unikt ID för varje rad
- Rätt datatyper – INTEGER för tal, NVARCHAR för text, DECIMAL för pengar
- Constraints – NOT NULL, UNIQUE, DEFAULT för datakvalitet
- Index – på kolumner som används i WHERE, JOIN, ORDER BY
- Foreign keys – för att länka till andra tabeller
- Normalisering – undvik att upprepa data i flera rader
Kom ihåg:
- Index = snabbare läsning, långsammare skrivning
- Foreign keys = dataintegritet, förhindrar orphaned data
- Normalisering = mindre redundans, enklare underhåll
- Testa alltid ALTER TABLE i dev-miljö först!
Dad joke
Varför slogs två tabeller? De kunde inte enas om en common column.