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-typBeskrivning
INNER JOINBara rader som matchar i båda tabellerna
LEFT JOINAlla rader från vänster, NULL för icke-matchande höger
RIGHT JOINAlla rader från höger, NULL för icke-matchande vänster
FULL OUTER JOINAlla rader från båda tabellerna
CROSS JOINKartesisk 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. status med 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