Tilbake
5.2
Avansert SQL

5.2 Avansert SQL

JOINs, subqueries, views og indekser.

70 min
7 oppgaver
JOINSubqueriesViewsIndekser
Du leser den tradisjonelle versjonen
Din fremgang i kapitlet
0 / 7 oppgaver

Avansert SQL

Du kan allerede SELECT, WHERE, ORDER BY og grunnleggende SQL. Nå skal vi ta steget videre og lære teknikker som gjør at du kan hente ut kompleks informasjon fra flere tabeller samtidig, gruppere data, lage virtuelle tabeller og optimalisere ytelsen.

I dette kapittelet lærer du:
- JOIN for å kombinere data fra flere tabeller
- GROUP BY og HAVING for å aggregere og filtrere grupper
- Subqueries (underspørringer) for komplekse spørringer
- Views for å lage gjenbrukbare "virtuelle tabeller"
- Indekser for å gjøre spørringer raskere

JOIN – å kombinere tabeller

I en normalisert database er data spredt over flere tabeller. For å hente ut meningsfull informasjon må vi ofte kombinere data fra flere tabeller. Det gjør vi med JOIN.

INNER JOIN


Returnerer bare rader der det finnes matchende verdier i begge tabeller.

-- Hent alle bøker med forfatternavnet
SELECT Bok.tittel, Forfatter.navn
FROM Bok
INNER JOIN Forfatter ON Bok.forfatterID = Forfatter.forfatterID;

LEFT JOIN (LEFT OUTER JOIN)


Returnerer alle rader fra venstre tabell, selv om det ikke finnes match i høyre tabell.

-- Hent alle forfattere, også de uten bøker
SELECT Forfatter.navn, Bok.tittel
FROM Forfatter
LEFT JOIN Bok ON Forfatter.forfatterID = Bok.forfatterID;

Forfattere uten bøker vil få NULL i bok-kolonnene.

RIGHT JOIN


Returnerer alle rader fra høyre tabell (fungerer motsatt av LEFT JOIN). Ikke støttet i SQLite, men kan løses med LEFT JOIN ved å bytte rekkefølge.

FULL OUTER JOIN


Returnerer alle rader fra begge tabeller. Heller ikke støttet i SQLite.

Flerveis JOIN


Du kan kombinere flere tabeller:

SELECT Kunde.navn, Ordre.ordreID, Produkt.produktnavn
FROM Kunde
INNER JOIN Ordre ON Kunde.kundeID = Ordre.kundeID
INNER JOIN OrdreLinjer ON Ordre.ordreID = OrdreLinjer.ordreID
INNER JOIN Produkt ON OrdreLinjer.produktID = Produkt.produktID;

Eksempel: JOIN i praksis

Gitt disse tabellene:

-- Elev (elevID, navn, klasseID)
-- Klasse (klasseID, klassenavn)
-- Karakter (karakterID, elevID, fagnavn, karakter)

Oppgave: Hent alle elever med klassenavn og deres karakterer.

SELECT
    Elev.navn AS elevnavn,
    Klasse.klassenavn,
    Karakter.fagnavn,
    Karakter.karakter
FROM Elev
INNER JOIN Klasse ON Elev.klasseID = Klasse.klasseID
LEFT JOIN Karakter ON Elev.elevID = Karakter.elevID
ORDER BY Elev.navn, Karakter.fagnavn;

Vi bruker:
- INNER JOIN mellom Elev og Klasse (alle elever må ha en klasse)
- LEFT JOIN mellom Elev og Karakter (noen elever kan mangle karakterer)

GROUP BY og aggregatfunksjoner

GROUP BY lar oss gruppere rader og bruke aggregatfunksjoner på hver gruppe.

Vanlige aggregatfunksjoner:


- COUNT() – teller antall rader
- SUM() – summerer verdier
- AVG() – beregner gjennomsnitt
- MIN() – finner minste verdi
- MAX() – finner største verdi

Grunnleggende GROUP BY:

-- Hvor mange elever i hver klasse?
SELECT klasseID, COUNT(*) AS antall_elever
FROM Elev
GROUP BY klasseID;

-- Gjennomsnittskarakter per fag
SELECT fagnavn, AVG(karakter) AS snittkarakter
FROM Karakter
GROUP BY fagnavn;

HAVING – filtrering av grupper


WHERE filtrerer rader før gruppering. HAVING filtrerer grupper etter gruppering.

-- Fag med snittkarakter over 4.0
SELECT fagnavn, AVG(karakter) AS snitt
FROM Karakter
GROUP BY fagnavn
HAVING AVG(karakter) > 4.0;

Rekkefølge i SQL-spørringer:


1. FROM (velg tabell)
2. WHERE (filtrer rader)
3. GROUP BY (grupper rader)
4. HAVING (filtrer grupper)
5. SELECT (velg kolonner)
6. ORDER BY (sorter resultat)

Eksempel: GROUP BY med JOIN

La oss kombinere JOIN og GROUP BY:

-- Hvor mange bøker har hver forfatter skrevet?
SELECT
    Forfatter.navn,
    COUNT(Bok.ISBN) AS antall_bøker
FROM Forfatter
LEFT JOIN Bok ON Forfatter.forfatterID = Bok.forfatterID
GROUP BY Forfatter.forfatterID, Forfatter.navn
ORDER BY antall_bøker DESC;

Tips:
- Bruk LEFT JOIN hvis du vil inkludere forfattere uten bøker (de får COUNT = 0)
- GROUP BY må inkludere alle kolonner fra SELECT som ikke er aggregatfunksjoner
- ORDER BY kommer alltid til slutt

Subqueries (underspørringer)

En subquery er en SQL-spørring inne i en annen SQL-spørring. De brukes ofte når du trenger resultatet av én spørring som input til en annen.

Subquery i WHERE:

-- Finn elever som har bedre snittkarakter enn gjennomsnittet
SELECT navn, AVG(karakter) AS snitt
FROM Elev
INNER JOIN Karakter ON Elev.elevID = Karakter.elevID
GROUP BY Elev.elevID, Elev.navn
HAVING AVG(karakter) > (
    SELECT AVG(karakter) FROM Karakter
);

Subquery i FROM:

-- Bruk resultatet av en spørring som en "tabell"
SELECT klassenavn, gjennomsnitt
FROM (
    SELECT
        Klasse.klassenavn,
        AVG(Karakter.karakter) AS gjennomsnitt
    FROM Elev
    JOIN Klasse ON Elev.klasseID = Klasse.klasseID
    JOIN Karakter ON Elev.elevID = Karakter.elevID
    GROUP BY Klasse.klasseID, Klasse.klassenavn
) AS klasseresultater
WHERE gjennomsnitt > 4.0;

IN og NOT IN med subquery:

-- Finn elever som har fått 6 i minst ett fag
SELECT DISTINCT navn
FROM Elev
WHERE elevID IN (
    SELECT elevID FROM Karakter WHERE karakter = 6
);

Views – virtuelle tabeller

En view er en lagret SQL-spørring som oppfører seg som en tabell. Den lagrer ikke data, men gir deg en "ferdig" spørring du kan gjenbruke.

Opprette en view:

CREATE VIEW ElevOversikt AS
SELECT
    Elev.navn AS elevnavn,
    Klasse.klassenavn,
    AVG(Karakter.karakter) AS snittkarakter
FROM Elev
INNER JOIN Klasse ON Elev.klasseID = Klasse.klasseID
LEFT JOIN Karakter ON Elev.elevID = Karakter.elevID
GROUP BY Elev.elevID, Elev.navn, Klasse.klassenavn;

Bruke en view:

-- Nå kan du bruke ElevOversikt som en vanlig tabell:
SELECT * FROM ElevOversikt WHERE snittkarakter > 4.5;

Fordeler med views:


- Forenkler komplekse spørringer
- Gjenbrukbar kode
- Skjuler kompleksitet for sluttbrukere
- Kan brukes for sikkerhet (gi tilgang til view i stedet for rå-tabell)

Slette en view:

DROP VIEW ElevOversikt;

Indekser – optimalisering

En indeks er en datastruktur som gjør det raskere å søke etter data i en tabell. Det fungerer som stikkordregisteret bak i en bok.

Når bruke indekser?


- På kolonner som ofte brukes i WHERE
- På kolonner som ofte brukes i JOIN
- På primærnøkler (opprettes automatisk)
- På fremmednøkler

Opprette en indeks:

-- Indeks på kolonnen "navn" i Elev-tabellen
CREATE INDEX idx_elev_navn ON Elev(navn);

Indeks på flere kolonner:

CREATE INDEX idx_karakter_elev_fag ON Karakter(elevID, fagnavn);

Fordeler og ulemper:

Fordeler:
- Dramatisk raskere SELECT-spørringer
- Raskere JOIN-operasjoner

Ulemper:
- Tar opp ekstra plass
- Gjør INSERT, UPDATE og DELETE litt tregere (indeksen må oppdateres)

Tips: Ikke lag indeks på alt. Bare på kolonner som faktisk brukes mye i spørringer.

Slette en indeks:

DROP INDEX idx_elev_navn;
JOIN: Kombinerer rader fra to eller flere tabeller basert på en relatert kolonne.

INNER JOIN: Returnerer bare rader med matchende verdier i begge tabeller.

LEFT JOIN: Returnerer alle rader fra venstre tabell, også de uten match.

GROUP BY: Grupperer rader med like verdier i én eller flere kolonner.

HAVING: Filtrerer grupper etter aggregering (brukes med GROUP BY).

Subquery: En SQL-spørring inne i en annen spørring.

View: En lagret SQL-spørring som kan brukes som en virtuell tabell.

Indeks: En datastruktur som akselererer søk i en tabell.

📝Oppgave

Hva er forskjellen mellom INNER JOIN og LEFT JOIN?

📝Oppgave

Hva er forskjellen mellom WHERE og HAVING?

📝Oppgave

Gitt disse tabellene:

Forfatter (forfatterID, navn, land)
Bok (ISBN, tittel, forfatterID, utgivelsesår)

Skriv SQL-spørringer for:
a) Hent alle bøker med forfatterens navn og land
b) Finn antall bøker per land
c) Finn land som har utgitt mer enn 10 bøker
d) Finn forfattere som ikke har skrevet noen bøker

📝Oppgave

Gitt tabellen:

Ordre (ordreID, kundeID, produktID, antall, pris, dato)

a) Finn totalt salgsbeløp per kunde (sorter fra høyest til lavest)
b) Finn kunder som har handlet for mer enn 10 000 kr totalt
c) Finn gjennomsnittlig ordrestørrelse (antall × pris) per måned i 2024

📝Oppgave

a) Lag en VIEW kalt "KundeStatistikk" som viser kundeID, antall ordrer og totalt salgsbeløp for hver kunde.

b) Bruk denne viewen til å finne kunder som har handlet mer enn 5 ganger og brukt over 5000 kr totalt.

📝Oppgave

Når bør du opprette en indeks på en kolonne?

📝Oppgave

// --- Samleoppgaver ---

Du har en database for en online-strømmetjeneste:

Bruker (brukerID, navn, epost, registrertDato)
Film (filmID, tittel, sjanger, lengdeMinutter, utgivelsesår)
Visning (visningID, brukerID, filmID, dato, minutter_sett)

Skriv SQL for:
a) Finn de 5 mest populære filmene (flest visninger totalt)
b) Finn brukere som har sett mer enn 100 timer totalt
c) Finn gjennomsnittlig visningstid per sjanger
d) Lag en VIEW som viser hver brukers favorittsjanger (sjangeren de har sett mest)
e) Finn filmer som ingen har sett helt til slutten (minutter_sett < lengdeMinutter for alle visninger)
f) Foreslå hvilke indekser du ville opprettet på disse tabellene og forklar hvorfor

Oppsummering

I dette kapittelet har du lært:

- JOIN: kombinerer tabeller (INNER, LEFT, RIGHT, FULL).
- GROUP BY og aggregatfunksjoner: COUNT, SUM, AVG, MIN, MAX.
- HAVING: filtrerer grupper.
- Subqueries: underspoerringer i WHERE og FROM.
- Views og indekser: virtuelle tabeller og ytelsesoptimalisering.

Noekkelbegreper


BegrepForklaring
JOINKombinerer rader fra flere tabeller
ViewVirtuell tabell basert på en spørring
IndeksStruktur som gjør søk raskere

Oppgaver

Dette kapitlet er skrevet av Anthropics toppmodeller (Claude Opus og Claude Fable) og er foreløpig ikke manuelt gjennomgått — kvalitetskontrollen gjøres av uavhengige KI-agenter, og innmeldte feil rettes fortløpende. Funnet en feil? Meld fra, så retter vi den. Les mer om hvordan innholdet lages.