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:

StudentIdFirstNameLastNameEmail
1AnnaSvenssonanna@example.com
2BengtLarssonbengt@example.com
3CeciliaNilssoncecilia@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 NULL
  • NOT NULL – kolumnen måste ha ett värde
  • UNIQUE – 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

DatatypAnvändningExempel
INTEGER / BIGINTID-nummer, räknareStudentId, Quantity
DECIMAL(12,2)Pengar, exakta talPrice, TotalAmount
NVARCHAR(50)Text med UnicodeFirstName, Email
TEXTLång textDescription, Notes
BIT / BOOLEANJa/Nej-flaggorIsActive, HasPaid
DATEDatumBirthDate, OrderDate
DATETIMEDatum + tidCreatedAt, 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:

DatabasKodexempel
SQL ServerStudentId INT IDENTITY(1,1) PRIMARY KEY
MySQLStudentId INT AUTO_INCREMENT PRIMARY KEY
PostgreSQLStudentId SERIAL PRIMARY KEY
SQLiteStudentId 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:

StudentIdFirstName
1Anna
2Bengt

Ä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:

StudentIdFirstName
1Anna

Efter:

StudentIdFirstNamePhoneNumber
1AnnaNULL

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:

  1. Testa i dev-miljö först
  2. Säkerhetskopiera produktionsdata
  3. Kör ändringar under lågtrafik
  4. 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):

OrderIdCustomerNameCustomerEmailProductNamePrice
1Annaanna@example.comLaptop8999
2Annaanna@example.comMus299

Problem: Annas email upprepas. Om hon byter email måste vi uppdatera flera rader.

Normaliserat (bra):

Customers:

CustomerIdNameEmail
1Annaanna@example.com

Orders:

OrderIdCustomerIdProductNamePrice
11Laptop8999
21Mus299

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, inte Col1 eller dt)
  • Välj singular (Student) eller plural (Students) – var konsekvent
  • PascalCase för tabeller, camelCase eller snake_case för kolumner

Datatyper:

  • Använd DECIMAL för pengar (inte FLOAT – avrundningsfel!)
  • NVARCHAR för text med specialtecken (Unicode)
  • BIT/BOOLEAN för flaggor (inte INT för sant/falskt)

Constraints:

  • Alltid PRIMARY KEY på varje tabell
  • NOT NULL för obligatoriska fält
  • UNIQUE för värden som inte får upprepas (email, personnummer)
  • DEFAULT fö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:

  1. Primärnyckel – unikt ID för varje rad
  2. Rätt datatyper – INTEGER för tal, NVARCHAR för text, DECIMAL för pengar
  3. Constraints – NOT NULL, UNIQUE, DEFAULT för datakvalitet
  4. Index – på kolumner som används i WHERE, JOIN, ORDER BY
  5. Foreign keys – för att länka till andra tabeller
  6. 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.


Upp

Upp


Licens: Apache 2.0 | © 2023 Marcus Medina, Campus Mölndal. Alla rättigheter förbehållna.
Du får använda och modifiera detta verk enligt villkoren i Apache License, Version 2.0. Du får inte använda detta verk för kommersiella ändamål utan tillstånd från upphovsmannen.