// SELECT – grunnstruktur
Rekkefølgen på klausulene er fast og må følges:
SELECT kolonne(r) -- hva vil du se?
FROM tabell -- hvilken tabell?
JOIN ... -- koble inn andre tabeller
WHERE betingelse -- filtrer rader (før gruppering)
GROUP BY kolonne(r) -- grupper radene
HAVING betingelse -- filtrer grupper (etter gruppering)
ORDER BY kolonne(r) ASC/DESC -- sorter resultatet
Vanlig feil: WHERE kan ikke bruke aggregatfunksjoner (SUM, COUNT osv.). Bruk HAVING for det. WHERE filtrerer enkeltrad, HAVING filtrerer grupper.
// JOIN
JOIN brukes for å koble data fra flere tabeller basert på en felles kolonne.
INNER JOIN
Returnerer kun rader som har match i begge tabeller.
SELECT a.navn, av.avd_navn
FROM ANSATT a
INNER JOIN AVDELING av ON a.avd_id = av.avd_id;
LEFT JOIN
Returnerer alle rader fra venstre tabell, med NULL for høyre tabell der det ikke er match. Nyttig for å finne rader uten tilknytning.
-- Finn alle studier, også de uten søkere
SELECT ls.studienavn, COUNT(ss.sokernr) AS antall_søkere
FROM LÆRERSTED_STUDIUM ls
LEFT JOIN SØKER_STUD ss ON ls.studid = ss.studid
GROUP BY ls.studid, ls.studienavn;
Huskeregel: Vil du finne rader som IKKE har en kobling? Bruk LEFT JOIN og sjekk WHERE høyretabell.id IS NULL.
-- Studier uten noen søkere
SELECT ls.studienavn
FROM LÆRERSTED_STUDIUM ls
LEFT JOIN SØKER_STUD ss ON ls.studid = ss.studid
WHERE ss.studid IS NULL;
// Aggregatfunksjoner
Eksamenrelevant
Disse er de eneste aggregatfunksjonene som er pensum:
- COUNT(*) – teller alle rader inkl. NULL
- COUNT(kolonne) – teller rader der kolonnen ikke er NULL
- SUM(kolonne) – summerer verdier
- AVG(kolonne) – gjennomsnittsverdi
- MIN(kolonne) – laveste verdi
- MAX(kolonne) – høyeste verdi
-- Total inntekt og antall passeringer per stasjon
-- men kun stasjoner med over 100 passeringer
SELECT stasjon_id,
COUNT(*) AS antall_passeringer,
SUM(belop) AS total_inntekt
FROM PASSERING
GROUP BY stasjon_id
HAVING COUNT(*) > 100
ORDER BY total_inntekt DESC;
// Subqueries
En subquery er en SELECT inni en annen SELECT. Brukes typisk med IN eller NOT IN.
IN – finne rader med kobling
-- Studenter som har søkt på minst ett studie
SELECT navn FROM STUDENT
WHERE student_id IN (SELECT student_id FROM SØKNAD);
NOT IN – finne rader uten kobling
-- Studier ingen har søkt på
SELECT studienavn FROM LÆRERSTED_STUDIUM
WHERE studid NOT IN (SELECT studid FROM SØKER_STUD);
Obs: NOT IN med NULL-verdier kan gi uventede resultater. Hvis subqueryens kolonne kan inneholde NULL, bruk LEFT JOIN ... WHERE IS NULL i stedet.
// CREATE VIEW
Eksamenrelevant
En VIEW er en lagret SELECT-spørring som oppfører seg som en virtuell tabell. Den lagrer ingen data – spørringen kjøres på nytt hver gang du bruker viewen.
-- Opprett en view
CREATE VIEW AKTIVE_BOMSTASJONER AS
SELECT stasjon_id, navn, type
FROM BOMSTASJON
WHERE operativ_til IS NULL;
-- Bruk viewen akkurat som en tabell
SELECT * FROM AKTIVE_BOMSTASJONER;
-- Bruk viewen i en JOIN
SELECT ab.navn, COUNT(p.pas_id) AS antall
FROM AKTIVE_BOMSTASJONER ab
JOIN PASSERING p ON ab.stasjon_id = p.stasjon_id
GROUP BY ab.stasjon_id;
Fordeler med VIEW: forenkler komplekse spørringer, kan brukes til tilgangskontroll (gi brukere tilgang til view, ikke rå tabeller).
// DISTINCT og NULL
-- Hent unike kjøretøytyper (ingen duplikater)
SELECT DISTINCT kjøretøytype FROM KJØRETØY;
-- NULL-sjekk – bruk IS NULL / IS NOT NULL, aldri = NULL
SELECT * FROM ANSATT WHERE avd_id IS NULL;
SELECT * FROM ANSATT WHERE avd_id IS NOT NULL;
// Eksempel fra eksamen v25 – Blomster
Fra tidligere eksamen
Gitt tabellene:
BLOMST(blomst_id, navn, farge, pris)
ORDRE(ordre_id, dato, kunde_id*)
ORDRELINJE(ordre_id*, blomst_id*, antall)
-- Total omsetning per blomst, kun blomster solgt mer enn 10 ganger
SELECT b.navn, SUM(ol.antall * b.pris) AS omsetning
FROM BLOMST b
INNER JOIN ORDRELINJE ol ON b.blomst_id = ol.blomst_id
GROUP BY b.blomst_id, b.navn
HAVING SUM(ol.antall) > 10
ORDER BY omsetning DESC;
-- Blomster som aldri har blitt bestilt
SELECT navn FROM BLOMST
WHERE blomst_id NOT IN (SELECT blomst_id FROM ORDRELINJE);
🎯 Øv på SQL-spørsmål i quizen →