Bliv ven med SQL SELECT ved at løse rigtige mini-opgaver
Det korte svar er at du lærer SQL SELECT hurtigst ved at skrive mange små queries på det samme dataset. Men det længere svar er mere interessant.
I stedet for endnu en tør teori-tekst får du her et lille, fast dataset og 12 opgaver, der følges ad som en slags træningsbane. Du kan kopiere tabellerne, køre dem i din egen database og faktisk se, hvad der sker.
Mini-dataset til alle øvelserne
Vi arbejder med tre tabeller, der ligner noget fra en rigtig webshop: customers, products og orders. Du skal ikke gætte på feltnavne, alt står her.
Start med at oprette tabellerne:
CREATE TABLE customers (
customer_id INT PRIMARY KEY,
name VARCHAR(100),
city VARCHAR(100),
email VARCHAR(100)
);
CREATE TABLE products (
product_id INT PRIMARY KEY,
name VARCHAR(100),
category VARCHAR(50),
price DECIMAL(10,2)
);
CREATE TABLE orders (
order_id INT PRIMARY KEY,
customer_id INT,
product_id INT,
quantity INT,
order_date DATE,
FOREIGN KEY (customer_id) REFERENCES customers(customer_id),
FOREIGN KEY (product_id) REFERENCES products(product_id)
);
Og fyld dem med lidt testdata:
INSERT INTO customers (customer_id, name, city, email) VALUES
(1, 'Anna Andersen', 'København', 'anna@example.com'),
(2, 'Bo Bjerregaard', 'Aarhus', 'bo@example.com'),
(3, 'Carla Christensen','Odense', 'carla@example.com'),
(4, 'David Dahl', 'København', NULL),
(5, 'Emma Eskesen', 'Aalborg', 'emma@example.com');
INSERT INTO products (product_id, name, category, price) VALUES
(1, 'USB-kabel 1m', 'Elektronik', 49.00),
(2, 'Trådløs mus', 'Elektronik', 199.00),
(3, 'Kontorstol', 'Møbler', 1299.00),
(4, 'Hæve-sænkebord', 'Møbler', 2999.00),
(5, 'Keramik kaffekop', 'Køkken', 89.00),
(6, 'Espresso brygger 6-kop','Køkken', 349.00);
INSERT INTO orders (order_id, customer_id, product_id, quantity, order_date) VALUES
(1, 1, 2, 1, '2024-01-10'),
(2, 1, 5, 4, '2024-01-11'),
(3, 2, 3, 1, '2024-01-12'),
(4, 3, 1, 2, '2024-01-13'),
(5, 3, 6, 1, '2024-01-13'),
(6, 4, 4, 1, '2024-01-14'),
(7, 4, 2, 1, '2024-01-15'),
(8, 5, 5, 2, '2024-01-16');
Hvis du mangler et sted at køre SQL, kan du kigge på online værktøjer som SQLite i browseren eller installere et lille databaseværktøj lokalt. På codingclass.dk finder du også andre intro-artikler, hvis du først vil have styr på helt basic SQL.
Grundregler for SELECT, FROM og alias
Vi holder syntaksen simpel. De fleste af dine queries vil starte nogenlunde sådan her:
SELECT kolonner
FROM tabel
[WHERE betingelse]
[ORDER BY kolonne];
Alias er bare shortcuts på kolonnenavne eller tabeller. Det gør især joins lettere at læse.
SELECT c.name AS customer_name, o.order_id
FROM customers AS c
JOIN orders AS o ON o.customer_id = c.customer_id;
Alias med AS er mest til læsbarhed. Databasen er ligeglad, men din hjerne bliver glad.
De 12 opgaver du skal løse
Strukturen er den samme hver gang: opgaveformulering, hvad der cirka skal komme ud, løsning og forklaring. Prøv selv først, og scroll så ned til løsningen. Ja, virkelig.
Filtrering med WHERE og LIKE
Opgave 1: Alle kunder i København
Opgave: Hent navn og email på alle kunder, der bor i København.
Forventet resultat (rækker):
- Anna Andersen, anna@example.com
- David Dahl, NULL
Løsning:
SELECT name, email
FROM customers
WHERE city = 'København';
Hvorfor det virker: WHERE city = 'København' filtrerer rækkerne, så kun dem med præcis den værdi bliver vist. Bemærk at David også kommer med, selvom email er NULL, fordi betingelsen kun gælder på city.
Opgave 2: Find kunder med email på example.com
Opgave: Hent navn og email på kunder, hvor email-slutningen er @example.com. Ignorer rækker uden email.
Forventet resultat (rækker): Anna, Bo, Carla, Emma.
Løsning:
SELECT name, email
FROM customers
WHERE email LIKE '%@example.com';
Hvorfor det virker: LIKE med procenttegn (%) betyder “vilkårlige tegn før dette”. Vi filtrerer altså kun på slutningen. David er ude, fordi hans email er NULL, og NULL LIKE '%@example.com' bliver aldrig sandt.
Sortering og begrænsning (ORDER BY, LIMIT)
Opgave 3: Produkter sorteret efter pris
Opgave: Vis alle produkter med navn og pris, sorteret fra billigst til dyrest.
Forventet top og bund:
- Første række: USB-kabel 1m, 49.00
- Sidste række: Hæve-sænkebord, 2999.00
Løsning:
SELECT name, price
FROM products
ORDER BY price ASC;
Hvorfor det virker: ORDER BY price ASC sorterer stigende. Mange databaser bruger ASC som default, så du kan også skrive ORDER BY price. Men jeg anbefaler at være eksplicit, så din fremtidige hjerne ikke er i tvivl.
Opgave 4: De 3 dyreste produkter
Opgave: Vis navn og pris på de 3 dyreste produkter.
Forventet resultat (rækker): Hæve-sænkebord, Kontorstol, Espresso brygger 6-kop.
Løsning:
SELECT name, price
FROM products
ORDER BY price DESC
LIMIT 3;
Hvorfor det virker: Vi vender sorteringen med DESC (descending) og bruger LIMIT 3 til kun at tage de tre første. Det er præcis den kombination, man bruger hele tiden til “top 10”-lister.
Aggregation og GROUP BY
Opgave 5: Antal ordrer i alt
Opgave: Tæl hvor mange ordrer der er i orders-tabellen.
Forventet resultat: 8.
Løsning:
SELECT COUNT(*) AS total_orders
FROM orders;
Hvorfor det virker: COUNT(*) tæller rækker. Aliaset AS total_orders giver kolonnen et mere læsbart navn i resultatet. Det er småting, men det gør output mere brugbart, især når man bygger dashboards.
Opgave 6: Antal ordrer per kunde
Opgave: Find hvor mange ordrer hver kunde har lagt. Vis kundens id og antal ordrer.
Forventet resultat (rækkeskæring):
- customer_id 1: 2 ordrer
- customer_id 2: 1 ordre
- customer_id 3: 2 ordrer
- customer_id 4: 2 ordrer
- customer_id 5: 1 ordre
Løsning:
SELECT customer_id, COUNT(*) AS order_count
FROM orders
GROUP BY customer_id;
Hvorfor det virker: GROUP BY customer_id samler alle rækker med samme customer_id i en gruppe, og COUNT(*) kører så én gang pr. gruppe. Husk reglen: alle kolonner i SELECT, som ikke er aggregatfunktioner, skal stå i GROUP BY.
Opgave 7: Total solgt mængde per produkt
Opgave: Find hvor mange enheder der i alt er solgt af hvert produkt. Vis produkt-id og total mængde.
Forventet eksempel: product_id 5 har 6 enheder (4 i ordre 2 og 2 i ordre 8).
Løsning:
SELECT product_id, SUM(quantity) AS total_quantity
FROM orders
GROUP BY product_id;
Hvorfor det virker: SUM(quantity) lægger alle quantity-værdierne sammen inden for hver produkt-gruppe. Her begynder SQL for alvor at ligne noget, man bruger til rigtige rapporter.
JOINS (INNER og LEFT)
Opgave 8: Ordrer med kundenavn
Opgave: Vis alle ordrer med ordre-id, dato og kundens navn.
Forventet eksempelrækkke: order_id 1, 2024-01-10, Anna Andersen.
Løsning:
SELECT o.order_id, o.order_date, c.name AS customer_name
FROM orders AS o
INNER JOIN customers AS c
ON o.customer_id = c.customer_id;
Hvorfor det virker: INNER JOIN betyder “kun rækker, hvor der er match i begge tabeller”. Vi binder tabellerne sammen på den fælles nøgle customer_id. Aliasene o og c gør det mere overskueligt.
Opgave 9: Ordrer med produktnavn og pris
Opgave: Vis ordre-id, produktnavn, antal og produktets pris.
Forventet eksempelrækkke: order_id 2, Keramik kaffekop, quantity 4, price 89.00.
Løsning:
SELECT o.order_id,
p.name AS product_name,
o.quantity,
p.price
FROM orders AS o
INNER JOIN products AS p
ON o.product_id = p.product_id;
Hvorfor det virker: Samme mønster som før, bare med products-tabellen. Hvis du kan skrive én join, kan du skrive 90 % af dem, du møder i hverdagen. Resten er variationer.
Opgave 10: Alle kunder, også dem uden ordrer (LEFT JOIN)
Opgave: Vis alle kunder med deres ordre-id, hvis de har nogen. Kunder uden ordrer skal stadig vises.
Forventet nøglepoint: Alle 5 kunder vises. Kunder uden ordrer har NULL i order_id.
Løsning:
SELECT c.customer_id,
c.name,
o.order_id
FROM customers AS c
LEFT JOIN orders AS o
ON c.customer_id = o.customer_id
ORDER BY c.customer_id, o.order_id;
Hvorfor det virker: LEFT JOIN betyder: alle rækker fra venstre tabel (customers), og match fra højre tabel hvis der findes nogen, ellers NULL. Det er standarden hvis du vil finde “hvem har ikke købt endnu”.
Business-spørgsmål på simple data
Opgave 11: Samlet omsætning per kunde
Opgave: Beregn, hvor meget hver kunde har købt for i alt. Brug pris gange antal. Vis kundens navn og total beløb.
Hint: Du skal bruge både orders og products, plus SUM() og GROUP BY.
Løsning:
SELECT c.name AS customer_name,
SUM(o.quantity * p.price) AS total_amount
FROM orders AS o
JOIN customers AS c
ON o.customer_id = c.customer_id
JOIN products AS p
ON o.product_id = p.product_id
GROUP BY c.name
ORDER BY total_amount DESC;
Hvorfor det virker: Vi joiner alle tre tabeller, så hver ordre-række får både kunde og produktpris. o.quantity * p.price giver ordrelinjens beløb, og SUM() lægger det sammen per kunde. Sortering gør det let at se, hvem der er “bedste” kunde.
Opgave 12: Mest solgte kategori
Opgave: Find hvilken produktkategori der er solgt flest enheder af. Vis kategori og total mængde, sorteret så den mest solgte står øverst.
Løsning:
SELECT p.category,
SUM(o.quantity) AS total_quantity
FROM orders AS o
JOIN products AS p
ON o.product_id = p.product_id
GROUP BY p.category
ORDER BY total_quantity DESC;
Hvorfor det virker: Vi kobler ordrer til produkter for at kende kategorien, grupperer på kategori og summerer antal. Det er præcis sådan, rapporter om “hvilken type varer trækker mest” bliver bygget.
Typiske fejl når du starter med SQL SELECT
Hvis du sidder og får mærkelige resultater, er du ikke alene. Der er nogle klassiske faldgruber, som nærmest alle ryger i.
NULL opfører sig ikke som du tror
NULL er “ingen værdi”, ikke en tom tekst. Så = NULL virker ikke.
-- Forkert
SELECT * FROM customers WHERE email = NULL;
-- Rigtigt
SELECT * FROM customers WHERE email IS NULL;
Det samme gælder det modsatte: brug IS NOT NULL, ikke != NULL. Hvis noget føles mystisk, så tjek altid om kolonnen indeholder NULL-værdier.
Dubletter når du joiner
Hvis du rammer for brede joins, kan antallet af rækker eksplodere. Det betyder som regel at din join-betingelse ikke er specifik nok.
-- Mistænkeligt: ingen ON-betingelse
SELECT *
FROM orders AS o
JOIN products AS p;
Det her laver et såkaldt cartesisk produkt: hver ordre kombineres med alle produkter. Du vil næsten altid have en ON-betingelse på en nøgle, fx o.product_id = p.product_id. Hvis antallet af rækker pludselig er meget større end forventet, er det et rødt flag.
GROUP BY-fejl og uklare regler
Mange databaser kræver at alle ikke-aggregatkolonner i SELECT står i GROUP BY. Hvis du glemmer det, får du enten en fejl eller et underligt resultat.
-- Typisk fejl i nogle databaser
SELECT customer_id, order_date, COUNT(*)
FROM orders
GROUP BY customer_id;
Her giver order_date ingen mening, fordi du har flere datoer per kunde. Vil du fx have “første ordre-dato”, så skal du bruge en funktion som MIN(order_date) i stedet.
Filtrering før og efter aggregation (WHERE vs HAVING)
En klassiker er at forsøge at bruge WHERE på et aggregat:
-- Forkert
SELECT customer_id, COUNT(*) AS order_count
FROM orders
WHERE order_count >= 2
GROUP BY customer_id;
Aggregater findes ikke endnu i WHERE-fasen. Brug HAVING til at filtrere på resultaterne efter GROUP BY:
SELECT customer_id, COUNT(*) AS order_count
FROM orders
GROUP BY customer_id
HAVING COUNT(*) >= 2;
Tom huskeregel: WHERE filtrerer rækker før grouping, HAVING filtrerer grupper efter.
Sådan kan du øve videre
Hvis du har kørt dig igennem alle 12 opgaver, har du faktisk ram på størstedelen af det, man bruger til hverdags-SQL. Nu handler det mest om volumen: flere datasæt, flere spørgsmål, flere små queries.
Lav dine egne spørgsmål til samme dataset
Et godt næste skridt er at opfinde dine egne “business”-spørgsmål til det samme lille dataset. For eksempel:
- Hvilken kunde i København har lagt flest ordrer?
- Hvad er gennemsnitsprisen på produkter i kategorien “Køkken”?
- Hvor mange ordrer er lagt efter en bestemt dato?
Hvis du vil have mere inspiration til opgaver, kan du også kigge på tutorials om SQL på Coding Class, især inden for databaser og backend.
Skift dataset, men behold mønstrene
Når du er tryg ved det lille webshop-eksempel, så byt det ud med noget andet: film, spil, bøger, kaffeopskrifter, hvad du nu synes er sjovt. Pointen er at du genbruger de samme mønstre:
SELECT ... FROM ... WHEREtil filtreringORDER BY ... LIMITtil top-listerGROUP BY+ aggregater til optællingJOINtil at binde tabeller sammen
Du kan også begynde at kombinere det med andre teknologier, fx at lave små scripts i Python, der sender SQL-queries til en database. Der ligger flere introartikler til Python på codingclass.dk, hvis du vil den vej.
Brug små daglige øvelser
Hvis du vil lære SQL hurtigt, er det langt bedre at skrive lidt hver dag end at læse én lang teoretisk bog. Tag for eksempel én type opgave ad gangen:
- Dag 1: kun simple
SELECTogWHERE - Dag 2: kun
ORDER BYogLIMIT - Dag 3: kun
GROUP BYogCOUNT/SUM - Dag 4: kun
JOIN-varianter
Det føles måske langsomt, men det sætter sig meget bedre fast end at prøve alt på én dag. Lidt ligesom at lære en ny boulder-rute: du tager et par greb ad gangen, ikke hele væggen første forsøg.
Hvis du kun gør én ting anderledes efter at have læst det her, så vælg ét lille dataset, gem det, og brug det som din faste træningsbane til SQL SELECT, indtil du kan lave dine egne spørgsmål og svar uden at kigge opskrifter af.








Send kommentar
Du skal være logget ind for at skrive en kommentar.