SQLite – Reference

En praktisk SQL-reference med forklaringer fra bunden. Fokus er på funktionalitet – ikke performanceoptimering.

Grundidé

Database, tabel, række og kolonne

En SQLite-database er typisk én fil. I databasen kan der være flere tabeller.

  • En tabel består af rækker og kolonner.
  • En række beskriver én ting, fx én elev.
  • En kolonne beskriver én egenskab, fx navn.
  • En værdi er det konkrete indhold i en celle.
CREATE TABLE Person (
    id INTEGER PRIMARY KEY,
    name TEXT,
    age INTEGER
);

Her har tabellen Person tre kolonner. En række kunne fx være (1, 'Anna', 17).

SQL i SQLite

SQL er sproget, vi bruger til at beskrive, hvilke data vi vil hente eller ændre.

SQLite understøtter det meste af almindelig SQL, men har også sine egne SQLite-specifikke detaljer. Denne reference bruger SQLite-syntaks.

Vigtigt: SQL-kommandoer afsluttes normalt med semikolon ;. Store og små bogstaver bruges normalt ikke til at skelne mellem SQL-keywords.

SELECT – hent data

SELECT bruges til at hente data fra en eller flere tabeller.

SELECT name, age
FROM Person;

Resultatet indeholder kolonnerne name og age for alle rækker.

SELECT *
FROM Person;

* betyder alle kolonner.

Alias – giv et navn et midlertidigt navn

AS kan give en kolonne eller tabel et mere praktisk navn i en forespørgsel.

SELECT name AS navn,
       age AS alder
FROM Person;

Et tabel-alias er især nyttigt, når flere tabeller bruges.

SELECT p.name
FROM Person AS p;

WHERE – vælg bestemte rækker

WHERE filtrerer rækkerne, så kun rækker der opfylder betingelsen, kommer med.

SELECT name, age
FROM Person
WHERE age >= 18;

Her vælges kun personer, hvis alder er mindst 18.

Sortering

SELECT name, age
FROM Person
ORDER BY age;

Standard er stigende orden.

SELECT name, age
FROM Person
ORDER BY age DESC;

ASC = stigende. DESC = faldende.

Flere sorteringskriterier

SELECT name, age
FROM Person
ORDER BY age DESC, name ASC;

Først sorteres efter alder. Hvis to personer har samme alder, sorteres de efter navn.

DISTINCT – fjern dubletter

SELECT DISTINCT age
FROM Person;

Hvis mange personer har samme alder, kommer hver alder kun én gang i resultatet.

LIMIT og OFFSET

SELECT *
FROM Person
ORDER BY name
LIMIT 10;

Hent højst 10 rækker.

SELECT *
FROM Person
ORDER BY name
LIMIT 10 OFFSET 20;

Spring de første 20 rækker over og hent derefter højst 10.

Kommentarer i SQL

-- Dette er en kommentar

SELECT *
FROM Person;  -- Hent alle personer

En kommentar begynder med -- og fortsætter til slutningen af linjen.

Datatyper og værdier

SQLite bruger en anden type-model end mange klassiske databasesystemer.

De fem storage classes

SQLite har fem grundlæggende storage classes:

TypeHvad bruges den til?
NULLIngen værdi / ukendt værdi.
INTEGERHeltal.
REALTal med decimaltal.
TEXTTekst.
BLOBBinære data.

SQLite bruger dynamic typing: den deklarerede type bestemmer ikke på samme måde som i mange andre databaser præcis, hvilken type værdi en kolonne altid skal indeholde.

Typiske deklarationer

CREATE TABLE Product (
    id INTEGER PRIMARY KEY,
    name TEXT,
    price REAL,
    description TEXT
);

Det er stadig en god idé at skrive meningsfulde deklarerede typer. De gør tabellen lettere at forstå og påvirker SQLite's type affinity.

INTEGER

Bruges til hele tal.

age INTEGER
quantity INTEGER
year INTEGER

En særlig SQLite-konstruktion er INTEGER PRIMARY KEY, som har en særlig forbindelse til tabellens rowid.

REAL

Bruges til flydende tal.

height REAL
temperature REAL
price REAL

Fx kan en temperatur gemmes som 21.5.

TEXT

Bruges til tekst.

name TEXT
email TEXT
city TEXT

Tekstværdier skrives normalt med apostroffer:

SELECT *
FROM Person
WHERE city = 'Aars';

BLOB

BLOB bruges til rå binære data, fx bytes. I mange almindelige elevprojekter vil tekst, heltal og reelle tal være langt mere almindelige.

Boolean – sand/falsk

SQLite har ikke en separat Boolean-storage class. TRUE og FALSE behandles normalt som henholdsvis 1 og 0.

CREATE TABLE Student (
    name TEXT,
    active INTEGER
);

INSERT INTO Student (name, active)
VALUES ('Anna', 1);

Det er almindeligt at bruge 0 = falsk og 1 = sand.

Dato og klokkeslæt

SQLite har ikke en separat DATE- eller DATETIME-storage class. Datoer og tidspunkter gemmes typisk som TEXT, REAL eller INTEGER, afhængigt af hvordan programmet vil arbejde med dem.

created_at TEXT
-- fx: '2026-09-28 13:20:00'

SQLite har funktioner til at arbejde med dato og tid.

NULL

NULL betyder ikke det samme som 0 eller en tom tekst. Det bruges til at angive, at der ikke er nogen værdi.

SELECT *
FROM Person
WHERE phone IS NULL;

Brug IS NULL og IS NOT NULL – ikke = NULL.

Type affinity

SQLite forbinder den deklarerede kolonnetype med en type affinity. De fem almindelige affinity-kategorier er INTEGER, TEXT, REAL, NUMERIC og BLOB.

Det er en vigtig forklaring på, hvorfor SQLite kan være mere fleksibel med typer end fx C#.

Betingelser og udtryk

De vigtigste operatorer til at beskrive, hvilke rækker vi vil arbejde med.

Sammenligninger

OperatorBetydning
=lig med
<> eller !=forskellig fra
<mindre end
<=mindre end eller lig med
>større end
>=større end eller lig med
SELECT *
FROM Product
WHERE price < 100;

AND, OR og NOT

Flere betingelser kan kombineres.

SELECT *
FROM Person
WHERE age >= 18
  AND city = 'Aars';
SELECT *
FROM Person
WHERE city = 'Aars'
   OR city = 'Farsø';

Brug parenteser, når det gør rækkefølgen tydelig:

WHERE age >= 18
  AND (city = 'Aars' OR city = 'Farsø')

BETWEEN

BETWEEN bruges til et interval. Grænserne er inkluderet.

SELECT *
FROM Product
WHERE price BETWEEN 50 AND 100;

Det svarer i praksis til at teste både nedre og øvre grænse.

IN

IN tester, om en værdi findes blandt flere mulige værdier.

SELECT *
FROM Person
WHERE city IN ('Aars', 'Farsø', 'Hobro');

Med NOT IN vendes betingelsen.

LIKE

LIKE bruges til simpel mønstergenkendelse.

MønsterBetydning
%nul eller flere vilkårlige tegn
_præcis ét vilkårligt tegn
SELECT *
FROM Person
WHERE name LIKE 'Ann%';

Her findes fx Anna og Anni.

NULL er ikke en almindelig værdi

En sammenligning med NULL giver ikke almindelig sand/falsk-logik. Derfor:

-- Forkert
WHERE phone = NULL

-- Rigtigt
WHERE phone IS NULL

CASE – hvis/så ellers

CASE kan bruges til at lave en værdi ud fra betingelser.

SELECT name,
       CASE
           WHEN age < 18 THEN 'barn'
           ELSE 'voksen'
       END AS type
FROM Person;

Beregninger i SELECT

Man kan beregne værdier direkte i en forespørgsel.

SELECT name,
       price,
       price * 1.25 AS price_with_tax
FROM Product;

Et alias gør resultatkolonnen lettere at forstå.

Tekst-sammensætning

Operatoren || sammenkæder tekst.

SELECT first_name || ' ' || last_name AS full_name
FROM Person;

Funktioner og grupper

Beregn værdier for mange rækker på én gang.

COUNT, SUM, AVG, MIN og MAX

FunktionFormål
COUNT()tæl rækker eller værdier
SUM()læg tal sammen
AVG()beregn gennemsnit
MIN()find mindste værdi
MAX()find største værdi

SQLite har disse indbyggede aggregate functions blandt flere andre.

COUNT

SELECT COUNT(*)
FROM Person;

Tæller rækker.

SELECT COUNT(phone)
FROM Person;

Her tælles ikke-NULL værdier i phone.

SUM og AVG

SELECT SUM(price) AS total
FROM Product;
SELECT AVG(price) AS average_price
FROM Product;

MIN og MAX

SELECT MIN(price), MAX(price)
FROM Product;

Resultatet indeholder den laveste og højeste pris.

GROUP BY

GROUP BY samler rækker med samme værdi, så aggregate functions kan beregnes for hver gruppe.

SELECT city, COUNT(*) AS number_of_people
FROM Person
GROUP BY city;

Her får vi én resultatlinje pr. by.

HAVING

WHERE filtrerer rækker før grupperingen. HAVING filtrerer grupper efter grupperingen.

SELECT city, COUNT(*) AS number_of_people
FROM Person
GROUP BY city
HAVING COUNT(*) >= 5;

WHERE eller HAVING?

SELECT city, COUNT(*) AS number_of_people
FROM Person
WHERE age >= 18
GROUP BY city
HAVING COUNT(*) >= 5;

Først fjernes personer under 18. Derefter grupperes de resterende personer efter by. Til sidst beholdes kun grupper med mindst 5 personer.

DISTINCT sammen med COUNT

SELECT COUNT(DISTINCT city)
FROM Person;

Her tælles antallet af forskellige byer.

ROUND

Tal kan afrundes med ROUND().

SELECT ROUND(AVG(price), 2) AS average_price
FROM Product;

Andre nyttige simple funktioner

SELECT
    UPPER(name),
    LOWER(name),
    LENGTH(name)
FROM Person;

Funktioner kan bruges direkte i SELECT-udtryk.

Flere tabeller – JOIN

JOIN bruges, når oplysninger ligger i forskellige tabeller.

Hvorfor flere tabeller?

Forestil dig to tabeller:

Person
id | name
---+------
1  | Anna
2  | Bo

Order
id | person_id | product
---+-----------+--------
10 | 1         | Bog
11 | 2         | Mus

Order.person_id fortæller, hvilken person ordren hører til.

INNER JOIN

INNER JOIN finder rækker, hvor sammenkoblingsbetingelsen passer i begge tabeller.

SELECT Person.name, Order.product
FROM Person
INNER JOIN Order
    ON Person.id = Order.person_id;

LEFT JOIN

LEFT JOIN tager alle rækker fra tabellen til venstre med. Hvis der ikke findes en matchende række, bliver værdier fra højre side NULL.

SELECT Person.name, Order.product
FROM Person
LEFT JOIN Order
    ON Person.id = Order.person_id;

RIGHT JOIN og FULL OUTER JOIN

SQLite understøtter også RIGHT JOIN og FULL OUTER JOIN.

SELECT p.name, o.product
FROM Person AS p
RIGHT JOIN Order AS o
    ON p.id = o.person_id;
SELECT p.name, o.product
FROM Person AS p
FULL OUTER JOIN Order AS o
    ON p.id = o.person_id;

RIGHT JOIN bevarer alle rækker fra højre side. FULL OUTER JOIN bevarer rækker fra begge sider, også når der ikke findes en match.

JOIN med alias

Alias gør lange tabelnavne lettere at skrive.

SELECT p.name, o.product
FROM Person AS p
JOIN Order AS o
    ON p.id = o.person_id;

JOIN uden andet keyword betyder normalt INNER JOIN.

JOIN og WHERE

SELECT p.name, o.product
FROM Person AS p
JOIN Order AS o
    ON p.id = o.person_id
WHERE p.city = 'Aars';

ON beskriver, hvordan tabellerne kobles sammen. WHERE filtrerer resultatet.

Flere JOINs

En forespørgsel kan koble flere tabeller sammen.

SELECT p.name, o.product, c.name AS category
FROM Person AS p
JOIN Order AS o
    ON p.id = o.person_id
JOIN Category AS c
    ON o.category_id = c.id;

SELF JOIN

En tabel kan kobles sammen med sig selv. Det er nyttigt, hvis en tabel fx indeholder medarbejdere og deres ledere.

SELECT e.name AS employee,
       m.name AS manager
FROM Employee AS e
LEFT JOIN Employee AS m
    ON e.manager_id = m.id;

CROSS JOIN

CROSS JOIN kombinerer hver række fra den første tabel med hver række fra den anden.

SELECT *
FROM Color
CROSS JOIN Size;

Hvis den første tabel har 3 rækker og den anden 4, bliver resultatet \(3\cdot4=12\) kombinationer.

Opret og ændr tabeller

DDL – SQL til at beskrive database-strukturen.

CREATE TABLE

CREATE TABLE Person (
    id INTEGER PRIMARY KEY,
    name TEXT,
    age INTEGER
);

CREATE TABLE opretter en ny tabel med de angivne kolonner og regler.

IF NOT EXISTS

CREATE TABLE IF NOT EXISTS Person (
    id INTEGER PRIMARY KEY,
    name TEXT
);

Hvis tabellen allerede findes, giver kommandoen ikke en fejl på grund af den eksisterende tabel.

PRIMARY KEY

En PRIMARY KEY identificerer en række entydigt.

CREATE TABLE Person (
    id INTEGER PRIMARY KEY,
    name TEXT
);

I SQLite har INTEGER PRIMARY KEY en særlig betydning: kolonnen fungerer som alias for rowid.

AUTOINCREMENT

Et INTEGER PRIMARY KEY får automatisk nye id'er, når id'et udelades fra INSERT. SQLite har også keywordet AUTOINCREMENT, men det ændrer reglerne for, hvordan nye rowid-værdier vælges. Det er derfor ikke nødvendigt bare for at få automatisk tildelte id'er.

CREATE TABLE Person (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT
);

NOT NULL

NOT NULL betyder, at kolonnen ikke må indeholde NULL.

CREATE TABLE Person (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL
);

UNIQUE

UNIQUE kræver, at værdier eller kombinationer af værdier ikke gentages.

CREATE TABLE Person (
    id INTEGER PRIMARY KEY,
    email TEXT UNIQUE
);

CHECK

CHECK kan sikre, at en betingelse er opfyldt.

CREATE TABLE Product (
    id INTEGER PRIMARY KEY,
    price REAL CHECK (price >= 0)
);

DEFAULT

DEFAULT angiver en værdi, der bruges, når INSERT ikke selv angiver en værdi.

CREATE TABLE Product (
    id INTEGER PRIMARY KEY,
    price REAL DEFAULT 0
);

FOREIGN KEY

En FOREIGN KEY bruges til at beskrive en relation til en anden tabel.

CREATE TABLE Person (
    id INTEGER PRIMARY KEY,
    name TEXT
);

CREATE TABLE Order (
    id INTEGER PRIMARY KEY,
    person_id INTEGER,
    FOREIGN KEY (person_id)
        REFERENCES Person(id)
);

Her peger person_id på en person.

Sammensat PRIMARY KEY

En primary key kan bestå af flere kolonner.

CREATE TABLE Enrollment (
    student_id INTEGER,
    course_id INTEGER,
    PRIMARY KEY (student_id, course_id)
);

Kombinationen af student_id og course_id skal være unik.

ALTER TABLE

SQLite understøtter et begrænset udvalg af ændringer på eksisterende tabeller: omdøbning af tabel, omdøbning af kolonne, tilføjelse af kolonne og sletning af kolonne.

ALTER TABLE Person
ADD COLUMN email TEXT;
ALTER TABLE Person
RENAME COLUMN name TO full_name;
ALTER TABLE Person
RENAME TO Customer;

DROP TABLE

DROP TABLE Person;

Tabellen og dens data fjernes.

Pas på: DROP TABLE er ikke det samme som at slette rækker. Hele tabellen fjernes.

Indsæt, ændr og slet data

DML – SQL til selve dataene.

INSERT – én række

INSERT INTO Person (name, age)
VALUES ('Anna', 17);

Kolonnerne i parentes angiver, hvilke kolonner der får værdier.

INSERT – flere rækker

INSERT INTO Person (name, age)
VALUES
    ('Anna', 17),
    ('Bo', 18),
    ('Clara', 17);

INSERT fra SELECT

Resultatet fra en forespørgsel kan indsættes i en anden tabel.

INSERT INTO Adult (name, age)
SELECT name, age
FROM Person
WHERE age >= 18;

UPDATE – ændr data

UPDATE Person
SET age = 18
WHERE id = 1;

Her ændres alderen for den person, hvis id er 1.

UPDATE flere kolonner

UPDATE Person
SET name = 'Anna Jensen',
    age = 18
WHERE id = 1;

UPDATE uden WHERE

UPDATE Product
SET price = price * 1.10;

Uden WHERE påvirkes alle rækker.

Kontrollér altid en UPDATE-betingelse, hvis kun nogle rækker skal ændres.

DELETE – slet rækker

DELETE FROM Person
WHERE id = 1;

Rækken med id = 1 slettes.

DELETE uden WHERE

DELETE FROM Person;

Alle rækker i tabellen slettes, men selve tabellen bliver.

Det er en anden handling end DROP TABLE.

INSERT og automatisk id

Med INTEGER PRIMARY KEY kan SQLite selv vælge et id, hvis kolonnen udelades fra INSERT.

CREATE TABLE Person (
    id INTEGER PRIMARY KEY,
    name TEXT
);

INSERT INTO Person (name)
VALUES ('Anna');

RETURNING

SQLite kan returnere de rækker, som en ændring påvirker.

INSERT INTO Person (name)
VALUES ('Anna')
RETURNING id, name;

Det kan være praktisk, når programmet skal have det nye id tilbage.

Resultater fra flere forespørgsler

UNION, INTERSECT og EXCEPT kombinerer resultatsæt.

UNION

UNION lægger resultatrækkerne fra to forespørgsler sammen og fjerner dubletter.

SELECT name FROM Student
UNION
SELECT name FROM Teacher;

UNION ALL

UNION ALL beholder også dubletter.

SELECT name FROM Student
UNION ALL
SELECT name FROM Teacher;

INTERSECT

INTERSECT giver de rækker, som findes i begge resultatsæt.

SELECT name FROM Student
INTERSECT
SELECT name FROM Teacher;

EXCEPT

EXCEPT giver rækker, der findes i det første resultat, men ikke i det andet.

SELECT name FROM Student
EXCEPT
SELECT name FROM Teacher;

Krav til resultaterne

Ved disse operationer skal de to SELECT-dele passe sammen med hensyn til antal resultatkolonner og deres indbyrdes placering.

Subquery – forespørgsel inde i en forespørgsel

En subquery er en SELECT inde i en anden SQL-kommando.

SELECT name
FROM Product
WHERE price > (
    SELECT AVG(price)
    FROM Product
);

Her findes produkter, der er dyrere end gennemsnitsprisen.

IN med subquery

SELECT name
FROM Person
WHERE id IN (
    SELECT person_id
    FROM Order
);

Find personer, som har mindst én ordre.

EXISTS

EXISTS tester, om en subquery returnerer mindst én række.

SELECT p.name
FROM Person AS p
WHERE EXISTS (
    SELECT 1
    FROM Order AS o
    WHERE o.person_id = p.id
);

WITH – navngiv et midlertidigt resultatsæt

WITH kan gøre større forespørgsler lettere at læse.

WITH Adults AS (
    SELECT *
    FROM Person
    WHERE age >= 18
)
SELECT name
FROM Adults;

Views, transaktioner og SQLite-værktøjer

Nogle praktiske funktioner, som stadig handler om funktionalitet.

VIEW – gem en forespørgsel som en navngiven visning

CREATE VIEW AdultPersons AS
SELECT id, name, age
FROM Person
WHERE age >= 18;

En view kan derefter bruges næsten som en tabel i en SELECT.

SELECT *
FROM AdultPersons;

DROP VIEW

DROP VIEW AdultPersons;

Fjerner viewet.

Transaktioner

En transaction samler flere ændringer, så de kan behandles som én samlet operation.

BEGIN TRANSACTION;

UPDATE Account
SET balance = balance - 100
WHERE id = 1;

UPDATE Account
SET balance = balance + 100
WHERE id = 2;

COMMIT;

ROLLBACK

Hvis ændringerne ikke skal gemmes, kan en aktiv transaktion rulles tilbage.

BEGIN TRANSACTION;

UPDATE Product
SET price = price * 1.10;

ROLLBACK;

Ændringen annulleres i stedet for at blive gemt.

PRAGMA foreign_keys

SQLite kan håndhæve foreign-key relationer. Det aktiveres normalt eksplicit for en databaseforbindelse.

PRAGMA foreign_keys = ON;

Dette er SQLite-specifikt og er ikke almindelig SQL.

SQLite CLI – nyttige kommandoer

Hvis du bruger SQLite's kommandolinjeværktøj, findes der kommandoer, som ikke er SQL.

.tables
.schema Person
.headers on
.mode column

De begynder typisk med punktum. De hører til SQLite CLI og ikke SQL-sproget.

Se tabeller

.tables

Viser tabeller i SQLite CLI.

Se tabeldefinition

.schema Person

Viser den SQL-definition, som SQLite CLI har for tabellen.

Husk forskellen

KommandoHovedidé
SELECThent data
INSERTindsæt data
UPDATEændr data
DELETEslet rækker
CREATE TABLEopret tabel
ALTER TABLEændr tabelstruktur
DROP TABLEslet tabel
CREATE VIEWgem en forespørgsel som view

Hurtig oversigt

SQL-områderne i overblik og den typiske forespørgsel.

SQL-kategorier

OmrådeTypiske kommandoer
Hent dataSELECT
FiltreringWHERE, IN, LIKE, BETWEEN
SorteringORDER BY
GrupperingGROUP BY, HAVING
TabellerCREATE TABLE, ALTER TABLE, DROP TABLE
DataINSERT, UPDATE, DELETE
RelationerJOIN, FOREIGN KEY
ResultaterUNION, INTERSECT, EXCEPT

Den typiske SELECT-rækkefølge

En almindelig forespørgsel kan læses sådan:

SELECT ...
FROM ...
JOIN ...
WHERE ...
GROUP BY ...
HAVING ...
ORDER BY ...
LIMIT ...;

Ikke alle dele skal være med. En simpel forespørgsel kan fx kun bestå af SELECT og FROM.

Et samlet eksempel

SELECT p.city,
       COUNT(*) AS adults,
       ROUND(AVG(p.age), 1) AS average_age
FROM Person AS p
WHERE p.age >= 18
GROUP BY p.city
HAVING COUNT(*) >= 3
ORDER BY adults DESC;

Læs den som: find voksne → grupper efter by → beregn antal og gennemsnitsalder → behold kun byer med mindst 3 voksne → sortér efter antal.

SQLite-specifikke pointer

  • SQLite er en database-motor, ikke en server, der skal køre separat.
  • En database kan normalt ligge i én fil.
  • SQLite har fem storage classes: NULL, INTEGER, REAL, TEXT og BLOB.
  • SQLite bruger dynamic typing og type affinity.
  • INTEGER PRIMARY KEY har en særlig funktion i SQLite.
  • ALTER TABLE har færre muligheder end i nogle større databasesystemer.