Tilbake
6.4
SQL – avanserte spørringer og JOIN

6.4 SQL – avanserte spørringer og JOIN

Lær JOIN, GROUP BY, HAVING og subqueries for komplekse dataspørringer.

70 min
8 oppgaver
JOININNER JOINLEFT JOINGROUP BY
Du leser den tradisjonelle versjonen
Din fremgang i kapitlet
0 / 8 oppgaver

SQL – avanserte spørjingar og samankoplingar

Hittil har vi jobba med data frå éin tabell om gongen. Men den verkelege krafta i relasjonsdatabasar ligg i moglegheita til å kople saman data frå fleire tabellar. Tenk deg at du vil sjå elevnamn saman med fagnamn og karakterar – den informasjonen er fordelt over tre tabellar (elever, fag og karakterer). For å setje dette saman treng vi JOIN.

I tillegg skal vi lære å rekne ut statistikk med aggregeringsfunksjonar (telje, summere, finne gjennomsnitt), gruppere data med GROUP BY, filtrere grupper med HAVING, og bruke underspørjingar for å løyse komplekse dataspørsmål.

Desse teknikkane er det som gjer SQL til eit kraftig analyseverktøy. Med dei kan du svare på spørsmål som «Kva er gjennomsnittskarakteren per fag?», «Kva for elevar har karakter over gjennomsnittet?» og «Kva for klasse har flest elevar med toppkarakter?».

Ein JOIN koplar rader frå to tabellar basert på ein felles kolonne. Den vanlegaste forma er INNER JOIN, som returnerer berre rader der det finst ein match i begge tabellane.

Syntaks for INNER JOIN

SELECT kolonne1, kolonne2, ...
FROM tabell1
INNER JOIN tabell2 ON tabell1.kolonne = tabell2.kolonne;

Eksempel: Elever med klassenavn

I staden for å sjå berre klasse_id (som er eit tal), vil vi sjå klassenamnet:

SELECT elever.fornavn, elever.etternavn, klasser.klassenavn
FROM elever
INNER JOIN klasser ON elever.klasse_id = klasser.klasse_id;

Resultat:

fornavnetternavnklassenavn
EmmaHansen10A
OliverJohansen10A
SofieBerg10A
NoraOlsen10B
JakobLarsen10B
NoahAndreassen10B
LiamDahl10C
EllaNilsen10C

Tabellaliasar


For å gjere spørjingane kortare kan vi gi tabellane aliasar:
SELECT e.fornavn, e.etternavn, k.klassenavn
FROM elever e
INNER JOIN klasser k ON e.klasse_id = k.klasse_id;
Her er e eit alias for elever og k eit alias for klasser.

✏️Eksempel: JOIN over tre tabellar

La oss kople elever, karakterer og fag for å sjå ei komplett karakteroversikt med namn:

SELECT
    e.fornavn,
    e.etternavn,
    f.fagnavn,
    k.karakter,
    k.termin
FROM karakterer k
INNER JOIN elever e ON k.elev_id = e.elev_id
INNER JOIN fag f ON k.fag_id = f.fag_id
ORDER BY e.etternavn, f.fagnavn;

Resultat (utdrag):

fornavnetternavnfagnavnkaraktertermin
NoahAndreassenMatematikk3H2024
NoahAndreassenNorsk2H2024
SofieBergMatematikk5H2024
SofieBergNaturfag4H2024
SofieBergNorsk5H2024
...............

Her koplar vi tre tabellar: karakterer med elever (via elevid), og karakterer med fag (via fagid). Resultatet gir oss lesbare namn i staden for ID-nummer.

Typar JOIN

Det finst fleire typar JOIN:

INNER JOIN – Returnerer berre rader der det finst match i begge tabellane. Rader utan match blir utelatne.

LEFT JOIN (LEFT OUTER JOIN) – Returnerer alle rader frå venstre tabell, og matchande rader frå høgre tabell. Dersom ingen match finst, blir høgre side fylt med NULL.

RIGHT JOIN (RIGHT OUTER JOIN) – Returnerer alle rader frå høgre tabell, og matchande rader frå venstre. Motsett av LEFT JOIN. (Merk: RIGHT JOIN blir ikkje støtta i SQLite, men kan simulerast ved å byte tabellrekkjefølgja med LEFT JOIN.)

FULL OUTER JOIN – Returnerer alle rader frå begge tabellane, med NULL der det ikkje er match. (Blir heller ikkje støtta direkte i SQLite.)

✏️Eksempel: LEFT JOIN – alle elevar, også utan karakterar

INNER JOIN viser berre elevar som har karakterar. Med LEFT JOIN får vi med alle elevar, også dei som manglar karakterar:

SELECT e.fornavn, e.etternavn, f.fagnavn, k.karakter
FROM elever e
LEFT JOIN karakterer k ON e.elev_id = k.elev_id
LEFT JOIN fag f ON k.fag_id = f.fag_id
ORDER BY e.etternavn;

Dersom ein elev ikkje har nokon karakterar, blir NULL vist i kolonnane fagnavn og karakter:

fornavnetternavnfagnavnkarakter
EmmaHansenMatematikk5
EmmaHansenNorsk4
EmmaHansenNaturfag5
MajaStrandNULLNULL

Her ser vi at Maja Strand (som vi sette inn tidlegare) ikkje har nokon karakterar enno. Med INNER JOIN ville Maja vore usynleg i resultatet.
Tommelfingerregel: Bruk INNER JOIN når du berre vil ha rader som matchar i begge tabellar. Bruk LEFT JOIN når du vil behalde alle rader frå den «venstre» tabellen, uansett om dei har matchande rader i den andre.

Aggregeringsfunksjonar reknar ut éin verdi basert på mange rader. Dei vanlegaste er:

FunksjonSkildringEksempel
COUNT()Tel talet på raderCOUNT(*) eller COUNT(kolonne)
SUM()Summerer verdiarSUM(karakter)
AVG()Reknar ut gjennomsnittAVG(karakter)
MIN()Finn lågaste verdiMIN(karakter)
MAX()Finn høgaste verdiMAX(karakter)

Eksempel


-- Antall elever i databasen
SELECT COUNT(*) AS antall_elever FROM elever;
Resultat: 8
-- Gjennomsnittskarakter for alle
SELECT AVG(karakter) AS gjennomsnitt FROM karakterer;
Resultat: 4.0 (ca.)
-- Høyeste og laveste karakter
SELECT MAX(karakter) AS hoyeste, MIN(karakter) AS laveste FROM karakterer;

Resultat: hoyeste = 6, laveste = 2

-- Antall karakterer registrert
SELECT COUNT(*) AS antall_karakterer FROM karakterer;
Legg merke til AS – det gir kolonnen i resultatet eit forståeleg namn (alias).

GROUP BY grupperer rader med like verdiar slik at aggregeringsfunksjonar blir rekna ut per gruppe.

Eksempel: Antall elever per klasse

SELECT klasse_id, COUNT(*) AS antall_elever
FROM elever
GROUP BY klasse_id;

Resultat:

klasseidantallelever
13
23
32

Eksempel: Gjennomsnittskarakter per fag


SELECT f.fagnavn, AVG(k.karakter) AS snittkarakter
FROM karakterer k
INNER JOIN fag f ON k.fag_id = f.fag_id
GROUP BY f.fagnavn;
Resultat:
fagnavnsnittkarakter
Matematikk4.0
Naturfag4.67
Norsk3.75

Eksempel: Antall fag per elev

SELECT e.fornavn, e.etternavn, COUNT(*) AS antall_fag
FROM karakterer k
INNER JOIN elever e ON k.elev_id = e.elev_id
GROUP BY e.elev_id, e.fornavn, e.etternavn;

Resultat:

fornavnetternavnantall_fag
EmmaHansen3
OliverJohansen3
NoraOlsen3
JakobLarsen2
.........
HAVING filtrerer grupper etter at GROUP BY har gjort jobben sin. Det er til grupper det WHERE er til individuelle rader.

Viktig forskjell:
- WHERE filtrerer rader før gruppering
- HAVING filtrerer grupper etter gruppering

Eksempel: Fag med gjennomsnittskarakter over 4

SELECT f.fagnavn, AVG(k.karakter) AS snittkarakter
FROM karakterer k
INNER JOIN fag f ON k.fag_id = f.fag_id
GROUP BY f.fagnavn
HAVING AVG(k.karakter) > 4;

Resultat:

fagnavnsnittkarakter
Naturfag4.67

Eksempel: Elever med mer enn 2 fag


SELECT e.fornavn, e.etternavn, COUNT(*) AS antall_fag
FROM karakterer k
INNER JOIN elever e ON k.elev_id = e.elev_id
GROUP BY e.elev_id, e.fornavn, e.etternavn
HAVING COUNT(*) > 2;

Kombinere WHERE og HAVING

-- Gjennomsnittskarakter per fag for termin H2024,
-- men bare fag der snittet er over 3.5
SELECT f.fagnavn, AVG(k.karakter) AS snittkarakter, COUNT(*) AS antall
FROM karakterer k
INNER JOIN fag f ON k.fag_id = f.fag_id
WHERE k.termin = 'H2024'
GROUP BY f.fagnavn
HAVING AVG(k.karakter) > 3.5;

Her filtrerer WHERE først ut berre H2024-karakterar, deretter grupperer GROUP BY etter fag, og til slutt filtrerer HAVING bort grupper med snitt under 3.5.

Ein subquery (underspørjing) er ei SELECT-setning som er nesta inne i ei anna spørjing. Underspørjinga blir køyrd først, og resultatet blir brukt av den ytre spørjinga.

Subquery i WHERE

-- Finn alle elever med karakter høyere enn gjennomsnittet
SELECT e.fornavn, e.etternavn, k.karakter
FROM karakterer k
INNER JOIN elever e ON k.elev_id = e.elev_id
WHERE k.karakter > (SELECT AVG(karakter) FROM karakterer);

Her reknar underspørjinga først ut gjennomsnittskarakteren, og den ytre spørjinga bruker dette resultatet for å filtrere.

Subquery med IN

-- Finn alle elever som har karakter 6 i et eller annet fag
SELECT fornavn, etternavn
FROM elever
WHERE elev_id IN (SELECT elev_id FROM karakterer WHERE karakter = 6);

Underspørjinga finn alle elev_id-ar med karakter 6, og den ytre spørjinga hentar namna til desse elevane.

Subquery i FROM (avleidde tabellar)

-- Finn elevenes gjennomsnittskarakter og vis bare de med snitt over 4
SELECT fornavn, etternavn, snitt
FROM (
    SELECT e.fornavn, e.etternavn, AVG(k.karakter) AS snitt
    FROM karakterer k
    INNER JOIN elever e ON k.elev_id = e.elev_id
    GROUP BY e.elev_id, e.fornavn, e.etternavn
) AS elevsnitt
WHERE snitt > 4;

Her lagar underspørjinga ein mellombels tabell (elevsnitt) med gjennomsnitt per elev, og den ytre spørjinga filtrerer denne.

✏️Eksempel: Komplett analyse av skuledatabasen

La oss bruke alle teknikkane vi har lært for å lage ein omfattande analyse:

1. Karakteroversikt med klassenamn og fagnamn:

SELECT kl.klassenavn, e.fornavn, e.etternavn,
       f.fagnavn, k.karakter
FROM karakterer k
INNER JOIN elever e ON k.elev_id = e.elev_id
INNER JOIN fag f ON k.fag_id = f.fag_id
INNER JOIN klasser kl ON e.klasse_id = kl.klasse_id
ORDER BY kl.klassenavn, e.etternavn, f.fagnavn;

2. Beste elev per fag:

SELECT f.fagnavn, e.fornavn, e.etternavn, k.karakter
FROM karakterer k
INNER JOIN elever e ON k.elev_id = e.elev_id
INNER JOIN fag f ON k.fag_id = f.fag_id
WHERE k.karakter = (
    SELECT MAX(k2.karakter)
    FROM karakterer k2
    WHERE k2.fag_id = k.fag_id
);

3. Klassestatistikk:

SELECT kl.klassenavn,
       COUNT(DISTINCT e.elev_id) AS antall_elever,
       ROUND(AVG(k.karakter), 2) AS snittkarakter,
       MIN(k.karakter) AS laveste,
       MAX(k.karakter) AS hoyeste
FROM klasser kl
INNER JOIN elever e ON kl.klasse_id = e.klasse_id
INNER JOIN karakterer k ON e.elev_id = k.elev_id
GROUP BY kl.klassenavn
ORDER BY snittkarakter DESC;

ROUND(AVG(k.karakter), 2) avrundar gjennomsnittet til 2 desimalar, og COUNT(DISTINCT e.elev_id) tel unike elevar (ikkje talet på karakterrader).

📝Oppgave 6.4.1

Kva gjer ein INNER JOIN?

📝Oppgave 6.4.2

Kva for aggregeringsfunksjon reknar ut gjennomsnittet av verdiar i ein kolonne?

📝Oppgave 6.4.3

Kva er forskjellen mellom WHERE og HAVING?

📝Oppgave 6.4.4

Skriv SQL-spørjingar for følgjande oppgåver (bruk skuledatabasen):

a) Vis fornamn, etternamn og klassenamn for alle elevar (bruk JOIN)

b) Rekn ut gjennomsnittskarakteren per elev (vis fornamn og gjennomsnitt)

c) Tel talet på elevar per klasse (vis klassenamn og tal)

📝Oppgave 6.4.5

Kva returnerer denne spørjinga?

SELECT f.fagnavn, COUNT(*) AS antall
FROM karakterer k
INNER JOIN fag f ON k.fag_id = f.fag_id
GROUP BY f.fagnavn
HAVING COUNT(*) > 5;

📝Oppgave 6.4.6

Skriv SQL-spørjingar for følgjande oppgåver:

a) Vis elevnamn, fagnamn og karakter for alle karakterar som er over gjennomsnittskarakteren (bruk subquery)

b) Finn alle elevar som har fått karakter 6 i minst eitt fag – vis namnet og faget dei fekk 6 i

c) Vis klassestatistikk: klassenamn, tal på elevar, gjennomsnittskarakter (avrunda til 1 desimal), høgaste og lågaste karakter – men berre for klassar med gjennomsnitt over 3.5

📝Oppgave 6.4.7

Kva er forskjellen mellom INNER JOIN og LEFT JOIN i denne situasjonen?

Du har 8 elevar i elevtabellen, men berre 6 av dei har karakterar i karakterer-tabellen.

-- Spørring A:
SELECT e.fornavn FROM elever e INNER JOIN karakterer k ON e.elev_id = k.elev_id;

-- Spørring B:
SELECT e.fornavn FROM elever e LEFT JOIN karakterer k ON e.elev_id = k.elev_id;

📝Oppgave 6.4.8

Du har nettbutikk-databasen frå kapittel 6.2. Skriv SQL-spørjingar for:

a) Vis alle bestillingar med kundenamn (fornamn og etternamn), bestillingsdato og totalpris – sortert etter dato

b) Finn total omsetning (sum av alle totalpris) per kunde – vis berre kundar med total omsetning over 1000 kr

c) Finn det mest bestilte produktet (produktet som har flest bestillingslinjer). Vis produktnamn og kor mange gonger det er bestilt.

Oppsummering

I dette kapittelet har du lært:

- JOIN: koplar saman data frå fleire tabellar.
- Typar JOIN: INNER, LEFT, RIGHT og FULL.
- Aggregeringsfunksjonar: COUNT, SUM, AVG, MIN og MAX.
- GROUP BY og HAVING: grupperer og filtrerer grupper.
- Subqueries: underspørjingar for komplekse spørjingar.

Nøkkelomgrep


OmgrepForklaring
JOINKoplar data frå fleire tabellar
GROUP BYGrupperer rader for aggregering
SubquerySpørjing inne i ei anna spørjing

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.