SQL (Structured Query Language) är standardspråket för att arbeta med relationsdatabaser. Här går vi igenom avancerade tekniker som du använder i praktiken.
Grundläggande frågor
-- Projektion — välj kolumner
SELECT name, email FROM users;
-- Filtrera rader
SELECT * FROM orders WHERE total > 1000; -- * hämtar allt
-- Sortera
SELECT name, price FROM products ORDER BY price DESC;
-- Begränsa antal rader
SELECT * FROM logs ORDER BY created_at DESC LIMIT 10;
JOIN — sammanfoga tabeller
| JOIN-typ | Beskrivning |
|---|---|
INNER JOIN | Bara rader som matchar i båda tabellerna |
LEFT JOIN | Alla rader från vänster, NULL för icke-matchande höger |
RIGHT JOIN | Alla rader från höger, NULL för icke-matchande vänster |
FULL OUTER JOIN | Alla rader från båda tabellerna |
CROSS JOIN | Kartesisk produkt (varje rad × varje rad) |
-- Hämta orderrader med produktnamn
SELECT o.id, o.order_date, p.name, oi.quantity, oi.price
FROM orders o
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id
WHERE o.user_id = 42
ORDER BY o.order_date DESC;
Själv-join
Användbart för hierarkisk data som kategorier eller anställda.
SELECT e.name AS anstalld, m.name AS chef
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;
Aggregering och gruppering
-- Summera per grupp
SELECT customer_id, COUNT(*) AS antal_order, SUM(total) AS totalbelopp
FROM orders
GROUP BY customer_id
HAVING SUM(total) > 10000
ORDER BY totalbelopp DESC;
-- Flera aggregeringsfunktioner
SELECT
COUNT(*) AS total_rader,
AVG(price) AS medelpris,
MAX(price) AS hogsta_pris,
MIN(price) AS lagsta_pris,
SUM(quantity) AS totala_antalet
FROM order_items;
Subqueries
En subquery är en SELECT-sats inuti en annan SQL-sats.
-- Hitta produkter som kostar mer än genomsnittet
SELECT name, price FROM products
WHERE price > (SELECT AVG(price) FROM products);
-- EXISTS — finns det relaterade rader?
SELECT c.name FROM customers c
WHERE EXISTS (
SELECT 1 FROM orders o WHERE o.customer_id = c.id
);
-- Subquery i FROM (deriverad tabell)
SELECT dag, total
FROM (
SELECT DATE(created_at) AS dag, SUM(total) AS total
FROM orders
GROUP BY DATE(created_at)
) daglig
WHERE total > 50000;
CTE — Common Table Expressions
CTE gör komplexa frågor mer läsbara genom att bryta ned dem i namngivna delar.
-- Hierarkisk CTE — organisationsstruktur
WITH RECURSIVE org_tree AS (
-- Anchor: toppnivå
SELECT id, name, manager_id, 1 AS level
FROM employees
WHERE manager_id IS NULL
UNION ALL
-- Rekursiv del: barn
SELECT e.id, e.name, e.manager_id, ot.level + 1
FROM employees e
JOIN org_tree ot ON e.manager_id = ot.id
)
SELECT * FROM org_tree ORDER BY level, name;
-- CTE för stegvis beräkning
WITH
aktuella_order AS (
SELECT * FROM orders WHERE status = 'paid'
),
order_summering AS (
SELECT customer_id, COUNT(*) AS antal, SUM(total) AS summa
FROM aktuella_order
GROUP BY customer_id
)
SELECT c.name, os.antal, os.summa
FROM customers c
JOIN order_summering os ON c.id = os.customer_id
ORDER BY os.summa DESC;
Window Functions
Window functions utför beräkningar över en fönsterram av rader utan att gruppera dem.
-- Rownummer per partition
SELECT
name, department, salary,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rang
FROM employees;
-- Löpande summa
SELECT
order_date, total,
SUM(total) OVER (ORDER BY order_date) AS lopande_summa
FROM orders;
-- Jämför med föregående rad
SELECT
order_date, total,
LAG(total) OVER (ORDER BY order_date) AS foregaende,
total - LAG(total) OVER (ORDER BY order_date) AS skillnad
FROM orders;
Indexering
Index gör sökningar snabbare men saktar ned insättning och uppdatering.
-- Enkelt index
CREATE INDEX idx_users_email ON users(email);
-- Sammansatt index (kolumnordningen spelar roll)
CREATE INDEX idx_orders_user_status ON orders(user_id, status);
-- Unikt index
CREATE UNIQUE INDEX idx_unique_ssn ON employees(ssn);
-- Delvis index (PostgreSQL)
CREATE INDEX idx_active_users ON users(email) WHERE is_active = true;
-- EXPLAIN — se om index används
EXPLAIN SELECT * FROM users WHERE email = 'test@bytebase.se';
När skapa index?
- Kolumner som används i
WHERE,JOIN,ORDER BY - Kolumner med hög kardinalitet (många unika värden)
- Inte på kolumner som sällan anropas eller har få unika värden (t.ex.
statusmed bara 3 värden)
Transaktioner
BEGIN;
UPDATE accounts SET balance = balance - 500 WHERE id = 1;
UPDATE accounts SET balance = balance + 500 WHERE id = 2;
COMMIT;
-- Vid fel: ROLLBACK istället för COMMIT
Av Victor Hernandez från Bytebase.se