En praktisk SQL-reference med forklaringer fra bunden. Fokus er på funktionalitet – ikke performanceoptimering.
En SQLite-database er typisk én fil. I databasen kan der være flere tabeller.
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 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.
;. Store og små bogstaver bruges normalt ikke til at skelne mellem SQL-keywords.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.
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 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.
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.
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.
SELECT DISTINCT age
FROM Person;
Hvis mange personer har samme alder, kommer hver alder kun én gang i resultatet.
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.
-- Dette er en kommentar
SELECT *
FROM Person; -- Hent alle personer
En kommentar begynder med -- og fortsætter til slutningen af linjen.
SQLite bruger en anden type-model end mange klassiske databasesystemer.
SQLite har fem grundlæggende storage classes:
| Type | Hvad bruges den til? |
|---|---|
NULL | Ingen værdi / ukendt værdi. |
INTEGER | Heltal. |
REAL | Tal med decimaltal. |
TEXT | Tekst. |
BLOB | Binæ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.
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.
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.
Bruges til flydende tal.
height REAL
temperature REAL
price REAL
Fx kan en temperatur gemmes som 21.5.
Bruges til tekst.
name TEXT
email TEXT
city TEXT
Tekstværdier skrives normalt med apostroffer:
SELECT *
FROM Person
WHERE city = 'Aars';
BLOB bruges til rå binære data, fx bytes. I mange almindelige elevprojekter vil tekst, heltal og reelle tal være langt mere almindelige.
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.
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 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.
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#.
De vigtigste operatorer til at beskrive, hvilke rækker vi vil arbejde med.
| Operator | Betydning |
|---|---|
= | 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;
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 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 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 bruges til simpel mønstergenkendelse.
| Mønster | Betydning |
|---|---|
% | 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.
En sammenligning med NULL giver ikke almindelig sand/falsk-logik. Derfor:
-- Forkert
WHERE phone = NULL
-- Rigtigt
WHERE phone IS NULL
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;
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å.
Operatoren || sammenkæder tekst.
SELECT first_name || ' ' || last_name AS full_name
FROM Person;
Beregn værdier for mange rækker på én gang.
| Funktion | Formå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.
SELECT COUNT(*)
FROM Person;
Tæller rækker.
SELECT COUNT(phone)
FROM Person;
Her tælles ikke-NULL værdier i phone.
SELECT SUM(price) AS total
FROM Product;
SELECT AVG(price) AS average_price
FROM Product;
SELECT MIN(price), MAX(price)
FROM Product;
Resultatet indeholder den laveste og højeste pris.
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.
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;
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.
SELECT COUNT(DISTINCT city)
FROM Person;
Her tælles antallet af forskellige byer.
Tal kan afrundes med ROUND().
SELECT ROUND(AVG(price), 2) AS average_price
FROM Product;
SELECT
UPPER(name),
LOWER(name),
LENGTH(name)
FROM Person;
Funktioner kan bruges direkte i SELECT-udtryk.
JOIN bruges, når oplysninger ligger i forskellige 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 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 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;
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.
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.
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.
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;
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 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.
DDL – SQL til at beskrive database-strukturen.
CREATE TABLE Person (
id INTEGER PRIMARY KEY,
name TEXT,
age INTEGER
);
CREATE TABLE opretter en ny tabel med de angivne kolonner og regler.
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.
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.
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 betyder, at kolonnen ikke må indeholde NULL.
CREATE TABLE Person (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
UNIQUE kræver, at værdier eller kombinationer af værdier ikke gentages.
CREATE TABLE Person (
id INTEGER PRIMARY KEY,
email TEXT UNIQUE
);
CHECK kan sikre, at en betingelse er opfyldt.
CREATE TABLE Product (
id INTEGER PRIMARY KEY,
price REAL CHECK (price >= 0)
);
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
);
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.
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.
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 Person;
Tabellen og dens data fjernes.
DROP TABLE er ikke det samme som at slette rækker. Hele tabellen fjernes.DML – SQL til selve dataene.
INSERT INTO Person (name, age)
VALUES ('Anna', 17);
Kolonnerne i parentes angiver, hvilke kolonner der får værdier.
INSERT INTO Person (name, age)
VALUES
('Anna', 17),
('Bo', 18),
('Clara', 17);
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 Person
SET age = 18
WHERE id = 1;
Her ændres alderen for den person, hvis id er 1.
UPDATE Person
SET name = 'Anna Jensen',
age = 18
WHERE id = 1;
UPDATE Product
SET price = price * 1.10;
Uden WHERE påvirkes alle rækker.
UPDATE-betingelse, hvis kun nogle rækker skal ændres.DELETE FROM Person
WHERE id = 1;
Rækken med id = 1 slettes.
DELETE FROM Person;
Alle rækker i tabellen slettes, men selve tabellen bliver.
DROP TABLE.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');
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.
UNION, INTERSECT og EXCEPT kombinerer resultatsæt.
UNION lægger resultatrækkerne fra to forespørgsler sammen og fjerner dubletter.
SELECT name FROM Student
UNION
SELECT name FROM Teacher;
UNION ALL beholder også dubletter.
SELECT name FROM Student
UNION ALL
SELECT name FROM Teacher;
INTERSECT giver de rækker, som findes i begge resultatsæt.
SELECT name FROM Student
INTERSECT
SELECT name FROM Teacher;
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;
Ved disse operationer skal de to SELECT-dele passe sammen med hensyn til antal resultatkolonner og deres indbyrdes placering.
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.
SELECT name
FROM Person
WHERE id IN (
SELECT person_id
FROM Order
);
Find personer, som har mindst én ordre.
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 kan gøre større forespørgsler lettere at læse.
WITH Adults AS (
SELECT *
FROM Person
WHERE age >= 18
)
SELECT name
FROM Adults;
Nogle praktiske funktioner, som stadig handler om funktionalitet.
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 AdultPersons;
Fjerner viewet.
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;
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.
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.
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.
.tables
Viser tabeller i SQLite CLI.
.schema Person
Viser den SQL-definition, som SQLite CLI har for tabellen.
| Kommando | Hovedidé |
|---|---|
SELECT | hent data |
INSERT | indsæt data |
UPDATE | ændr data |
DELETE | slet rækker |
CREATE TABLE | opret tabel |
ALTER TABLE | ændr tabelstruktur |
DROP TABLE | slet tabel |
CREATE VIEW | gem en forespørgsel som view |
SQL-områderne i overblik og den typiske forespørgsel.
| Område | Typiske kommandoer |
|---|---|
| Hent data | SELECT |
| Filtrering | WHERE, IN, LIKE, BETWEEN |
| Sortering | ORDER BY |
| Gruppering | GROUP BY, HAVING |
| Tabeller | CREATE TABLE, ALTER TABLE, DROP TABLE |
| Data | INSERT, UPDATE, DELETE |
| Relationer | JOIN, FOREIGN KEY |
| Resultater | UNION, INTERSECT, EXCEPT |
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.
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.
NULL, INTEGER, REAL, TEXT og BLOB.INTEGER PRIMARY KEY har en særlig funktion i SQLite.ALTER TABLE har færre muligheder end i nogle større databasesystemer.