- Gepubliceerd op
Wat is SQL? Doel, oorsprong en gebruik begrijpen
- Auteurs
Wat is SQL? Doel, oorsprong en gebruik begrijpen
De meeste moderne applicaties, van eenvoudige blogs tot enorme sociale netwerken, slaan gegevens op en halen ze op met een taal die SQL heet.
In dit artikel leer je:
- Wat SQL is en waarom het is gemaakt
- Hoe relationele databases informatie organiseren
- Een concreet voorbeeld met twee tabellen
- Hoe je echte SQL-query's uitvoert, inclusief
JOINs - Waarom SQL na meer dan 50 jaar nog steeds de standaard is
Laten we beginnen.
Wat is SQL?
SQL (Structured Query Language) is een declaratieve programmeertaal die is gemaakt voor het beheren van relationele databases, databases die zijn opgebouwd rond tabellen, rijen en de relaties daartussen.
Met SQL kun je:
- Tabellen maken
- Gegevens invoegen en wijzigen
- Gegevens bevragen met filters
- Gerelateerde gegevens uit meerdere tabellen samenvoegen
- Beperkingen afdwingen, zoals unieke e-mailadressen of refererende sleutels
SQL wordt gebruikt in veel populaire relationele databasesystemen. Hier zijn enkele voorbeelden:
- PostgreSQL: geavanceerde opensource relationele database met sterke ondersteuning voor SQL-standaarden.
- MySQL: veel gebruikt in webapplicaties, bekend om snelheid en eenvoud.
- MariaDB: een door de community ontwikkelde afsplitsing van MySQL met verbeterde prestatiefuncties.
- SQLite: een lichtgewicht, bestandsgebaseerde database die wordt gebruikt in mobiele apps, browsers en ingebedde systemen.
- Microsoft SQL Server: commerciële database op bedrijfsniveau met krachtige tools voor Windows- en cloudomgevingen.
- Volledig beheerde SQL-clouddiensten zoals:
- Amazon RDS
- Google Cloud SQL
- PlanetScale
Deze platforms (Amazon RDS, Google Cloud SQL, PlanetScale) verzorgen back-ups, opschaling, updates en onderhoud, terwijl je de kracht van SQL behoudt.
Waarom SQL is gemaakt
In de jaren zeventig werkten onderzoekers Edgar F. Codd en Donald Chamberlin bij IBM aan een betere manier om gegevens op te slaan en op te halen. Destijds waren databases hiërarchisch of netwerkgebaseerd en was ermee werken:
- Complex
- Foutgevoelig
- Sterk gekoppeld aan fysieke opslag
- Moeilijk als je flexibel wilde bevragen
Om dit op te lossen introduceerden ze het relationele model, dat gegevens organiseert in tabellen met duidelijk gedefinieerde relaties.
SQL is gemaakt omdat:
- Ontwikkelaars een eenvoudige, leesbare taal nodig hadden om gegevens te bevragen.
- Bedrijven consistente regels nodig hadden om relaties op te slaan.
- Gegevens toegankelijk moesten zijn zonder kennis van interne opslagdetails.
Het resultaat?
SQL werd de standaard in 1986 (ANSI SQL), en vrijwel elke belangrijke database is er tegenwoordig nog steeds omheen gebouwd.
Tabellen, kolommen en relaties begrijpen
In een relationele database:
- Is een tabel als een spreadsheet (rijen + kolommen)
- Vertegenwoordigt een rij één record
- Vertegenwoordigt een kolom een eigenschap
- Identificeert een primaire sleutel elke rij uniek
- Koppelt een refererende sleutel de ene tabel aan de andere
Laten we een eenvoudig voorbeeld bekijken met gebruikers en bestellingen.
Voorbeeldschema: gebruikers en bestellingen
Tabel 1: users
| id | name | |
|---|---|---|
| 1 | Alice | alice@example.com |
| 2 | Bob | bob@example.com |
| 3 | Caroline | caroline@example.com |
Tabel 2: orders
| id | user_id | product | price |
|---|---|---|---|
| 1 | 1 | Coffee Mug | 12.99 |
| 2 | 1 | Hoodie | 39.99 |
| 3 | 2 | Stickers | 4.99 |
Relatie
- Elke bestelling hoort bij een gebruiker (
orders.user_id → users.id). - Een gebruiker kan meerdere bestellingen hebben (een-op-veelrelatie).
De tabellen maken (SQL-voorbeeld)
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL
);
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id),
product TEXT NOT NULL,
price NUMERIC(10,2) NOT NULL
);
Dit maakt twee sterk getypeerde relationele tabellen met een beperking voor een refererende sleutel. Maar wat betekent "sterk getypeerd"? In SQL-databases zoals PostgreSQL en MySQL heeft elke kolom een strikt gegevenstype:
- Tekenreeksen moeten tekenreeksen zijn
- Getallen moeten getallen zijn
- Datums moeten datums zijn
- Verkeerde typen veroorzaken fouten
Dit voorkomt fouten zoals:
- "twintig dollar" opslaan in een numerieke kolom
- 123 opslaan in een e-mailveld
- Ongeldige datums opslaan
SQLite is anders: het is flexibel met typen (“dynamische typering”), wat betekent dat het typen minder strikt afdwingt. PostgreSQL is van de belangrijkste databases het sterkst getypeerd.
Tabellen maken, regel voor regel uitgelegd
Tabel 1: users
CREATE TABLE users (
Uitleg: Dit start het maken van een nieuwe tabel met de naam users.
- Werkt in: PostgreSQL, MySQL, MariaDB, SQLite, SQL Server
- “Tabel” = gestructureerde verzameling rijen en kolommen in de database.
id SERIAL PRIMARY KEY,
Uitleg:
id → de kolomnaam.
SERIAL → automatisch oplopend geheel getal. Specifiek voor PostgreSQL.
Equivalent in andere engines:
- MySQL:
INT AUTO_INCREMENT - SQLite:
INTEGER PRIMARY KEY AUTOINCREMENT
- MySQL:
PRIMARY KEY → waarborgt uniciteit en identificeert elke rij.
name TEXT NOT NULL,
Uitleg:
- name → kolom om tekst op te slaan.
- TEXT → tekst met variabele lengte (tekenreeks).
- NOT NULL → mag niet leeg zijn.
Werkt in: PostgreSQL, MySQL, MariaDB, SQLite (maar SQL Server gebruikt VARCHAR(MAX) in plaats van TEXT).
email TEXT UNIQUE NOT NULL
Uitleg:
- email → slaat het e-mailadres van de gebruiker op.
- TEXT → teksttype.
- UNIQUE → voorkomt dubbele e-mailadressen.
- NOT NULL → verplicht veld.
Werkt in: PostgreSQL, MySQL, MariaDB, SQLite (met verschillen in de interne werking van het TEXT-type).
);
Uitleg: Sluit de tabeldefinitie af.
Tabel 2: orders
CREATE TABLE orders (
Start een nieuwe tabel met de naam orders. Werkt in alle belangrijke engines.
id SERIAL PRIMARY KEY,
Dezelfde betekenis als in de tabel users:
- Automatisch oplopende primaire sleutel
- Specifiek PostgreSQL-type (
SERIAL) - MySQL gebruikt
INT AUTO_INCREMENT - SQLite gebruikt
INTEGER PRIMARY KEY AUTOINCREMENT
user_id INTEGER NOT NULL REFERENCES users(id),
Uitleg:
- user_id → een getal dat de bestelling koppelt aan een rij in
users. - INTEGER → geheel getal.
- NOT NULL → elke bestelling moet bij een gebruiker horen.
- REFERENCES users(id) → beperking voor een refererende sleutel.
Betekenis van refererende sleutel:
- Waarborgt dat
user_idovereenkomt met een echteidin de tabelusers. - Als een gebruiker wordt verwijderd, dwingt de database de relatie af volgens de regels voor refererende sleutels.
Compatibiliteit met engines:
- PostgreSQL: Volledig ondersteund.
- MySQL/MariaDB: Werkt, maar vereist opslagengine = InnoDB.
- SQLite: Alleen ondersteund wanneer ondersteuning voor refererende sleutels is ingeschakeld (
PRAGMA foreign_keys = ON;).
product TEXT NOT NULL,
Slaat de naam van het gekochte product op.
- Het type TEXT → werkt verschillend per engine, maar slaat altijd tekenreeksen op.
- Verplicht (
NOT NULL).
price NUMERIC(10,2) NOT NULL
Uitleg:
price → de kosten van het product.
NUMERIC(10,2) → getal met vaste precisie met:
- maximaal 10 cijfers in totaal
- 2 cijfers na het decimaalteken
Dit is ideaal voor geldbedragen omdat het drijvendekommafouten voorkomt.
Compatibiliteit met engines:
- PostgreSQL: Perfecte ondersteuning.
- MySQL:
DECIMAL(10,2)is gelijkwaardig. - SQLite: Accepteert het, maar slaat intern op als TEXT, INT of REAL (dynamische typering).
);
Beëindigt de tabel.
De voorbeeldgegevens invoegen
Nu onze tabellen zijn gemaakt, voegen we de voorbeeldgegevens in die we eerder hebben getoond. We geven niet handmatig id-waarden op: de database genereert ze automatisch met het type SERIAL (automatisch oplopend).
De users-gegevens invoegen
INSERT INTO users (name, email)
VALUES
('Alice', 'alice@example.com'),
('Bob', 'bob@example.com'),
('Caroline', 'caroline@example.com');
Invoegen in users, regel voor regel uitgelegd
INSERT INTO users (name, email)
INSERT INTO usersWe voegen nieuwe rijen toe aan de tabel users.(name, email)We geven alleennameenemailop, nietid.Waarom geven we de ID niet op? Omdat de kolom
ideen serieel/automatisch oplopend type gebruikt (SERIALvan PostgreSQL), maakt de database ID's automatisch aan:Eerste gebruiker →
id = 1Tweede gebruiker →
id = 2Derde gebruiker →
id = 3
Dit is voorspelbaar in PostgreSQL, MySQL, MariaDB en SQLite (met hun respectieve mechanismen voor automatisch oplopende waarden).
VALUES
('Alice', 'alice@example.com'),
('Bob', 'bob@example.com'),
('Caroline', 'caroline@example.com');
VALUESleidt een lijst in van rijen die moeten worden ingevoegd.- Elke groep
( ... )komt overeen met één nieuwe rij. - De database kent ID's automatisch toe in de volgorde waarin deze rijen verschijnen.
De resulterende tabel users na invoeging:
| id | name | |
|---|---|---|
| 1 | Alice | alice@example.com |
| 2 | Bob | bob@example.com |
| 3 | Caroline | caroline@example.com |
Hoewel we geen ID's handmatig hebben geschreven, waarborgt het mechanisme voor automatisch oplopende waarden deze volgorde.
De orders-gegevens invoegen
Om orders in te voegen, moeten we wel user_id opgeven, omdat een bestelling bij een specifieke gebruiker moet horen.
Hoewel we de gebruikers-ID's niet handmatig hebben ingesteld, weten we toch welke ze zijn:
- Alice → ID 1
- Bob → ID 2
- Caroline → ID 3
Deze zijn automatisch gegenereerd in de volgorde van invoeging, en automatisch oplopende waarden wijzen ID's altijd opeenvolgend toe, tenzij rijen worden verwijderd.
Hier is de invoegopdracht:
INSERT INTO orders (user_id, product, price)
VALUES
(1, 'Coffee Mug', 12.99),
(1, 'Hoodie', 39.99),
(2, 'Stickers', 4.99);
Invoegen in orders, regel voor regel uitgelegd
INSERT INTO orders (user_id, product, price)
We voegen nieuwe rijen in de tabel orders in.
We geven het volgende op:
user_id→ bij welke gebruiker de bestelling hoortproduct→ naamprice→ kosten
Let opnieuw op: we geven geen id (orders.id) op, omdat die automatisch oploopt.
VALUES
(1, 'Coffee Mug', 12.99),
(1, 'Hoodie', 39.99),
(2, 'Stickers', 4.99);
(1, 'Coffee Mug', 12.99)- Deze bestelling hoort bij gebruiker 1 (Alice).
(1, 'Hoodie', 39.99)- Nog een bestelling voor Alice.
(2, 'Stickers', 4.99)- Bestelling voor gebruiker 2 (Bob).
Hoe weten we welke gebruiker welke ID heeft? Omdat:
De gebruikers eerst zijn ingevoegd.
Automatisch oplopende ID's altijd opeenvolgende gehele getallen toewijzen.
Daarom:
- Eerste ingevoegde gebruiker → ID 1
- Tweede ingevoegde gebruiker → ID 2
- Derde ingevoegde gebruiker → ID 3
Dit maakt verwijzingen naar gebruikers via ID volledig voorspelbaar.
Uiteindelijke resulterende tabellen
users
| id | name | |
|---|---|---|
| 1 | Alice | alice@example.com |
| 2 | Bob | bob@example.com |
| 3 | Caroline | caroline@example.com |
orders
| id | user_id | product | price |
|---|---|---|---|
| 1 | 1 | Coffee Mug | 12.99 |
| 2 | 1 | Hoodie | 39.99 |
| 3 | 2 | Stickers | 4.99 |
Gegevens bevragen met SQL
Hier zijn SQL-query's die we kunnen uitvoeren op de twee tabellen die we eerder hebben gedefinieerd.
1. Alle gebruikers selecteren
SELECT * FROM users;
Resultaat:
| id | name | |
|---|---|---|
| 1 | Alice | alice@example.com |
| 2 | Bob | bob@example.com |
| 3 | Caroline | caroline@example.com |
2. Gebruikers selecteren met een filter
SELECT * FROM users
WHERE email LIKE '%alice@example.com';
Wat dit betekent:
LIKEis een SQL-operator om tekst met patronen te vergelijken.%is een jokerteken dat overeenkomt met elk aantal tekens, inclusief nul tekens.
Dus:
'%alice@example.com'betekent: “Vergelijk met alle tekst die eindigt opalice@example.com.”
Dit retourneert de rij waarin het e-mailadres van de gebruiker precies dit is:
alice@example.com
Maar het zou ook overeenkomen met waarden zoals:
test-alice@example.comhello.alice@example.comanythingalice@example.com
Als die bestonden.
Resultaat:
| id | name | |
|---|---|---|
| 1 | Alice | alice@example.com |
Deze query retourneert alleen de gebruiker wiens e-mailadres overeenkomt met het filter.
3. Een nieuwe bestelling invoegen
INSERT INTO orders (user_id, product, price)
VALUES (2, 'T-Shirt', 29.99);
Resulterende tabel orders na invoeging:
| id | user_id | product | price |
|---|---|---|---|
| 1 | 1 | Coffee Mug | 12.99 |
| 2 | 1 | Hoodie | 39.99 |
| 3 | 2 | Stickers | 4.99 |
| 4 | 2 | T-Shirt | 29.99 |
- De nieuwe rij heeft
id = 4, automatisch gegenereerd.user_id = 2koppelt deze bestelling aan Bob.
JOINs: een krachtig onderdeel van SQL
Met JOINs kun je gerelateerde gegevens uit meerdere tabellen bevragen.
Laten we ze onderzoeken met onze tabellen users en orders.
Voorbeeld 1: INNER JOIN (meest gebruikt)
Haal elke bestelling op met de gebruiker die deze heeft geplaatst.
SELECT users.name, orders.product, orders.price
FROM orders
INNER JOIN users
ON orders.user_id = users.id;
Resultaat:
| name | product | price |
|---|---|---|
| Alice | Coffee Mug | 12.99 |
| Alice | Hoodie | 39.99 |
| Bob | Stickers | 4.99 |
| Bob | T-Shirt | 29.99 |
Bob verschijnt nu twee keer vanwege de nieuwe bestelling die eerder is ingevoegd:
INSERT INTO orders (user_id, product, price)
VALUES (2, 'T-Shirt', 29.99);
Voorbeeld 2: LEFT JOIN
Haal alle gebruikers op, ook degenen zonder bestellingen.
SELECT users.name, orders.product
FROM users
LEFT JOIN orders
ON users.id = orders.user_id;
Resultaat:
| name | product |
|---|---|
| Alice | Coffee Mug |
| Alice | Hoodie |
| Bob | Stickers |
| Bob | T-Shirt |
| Caroline | null |
Caroline verschijnt met null omdat ze geen bestellingen heeft.
Voorbeeld 3: aggregatiequery
Totaalbedrag dat elke gebruiker heeft uitgegeven:
SELECT users.name, SUM(orders.price) AS total_spent
FROM users
LEFT JOIN orders ON users.id = orders.user_id
GROUP BY users.name;
Resultaat:
| name | total_spent |
|---|---|
| Alice | 52.98 |
| Bob | 34.98 |
| Caroline | null |
Uitleg
- Alice: 12.99 + 39.99 = 52.98
- Bob: 4.99 + 29.99 = 34.98
- Caroline: geen bestellingen → NULL
Waar je meer kunt leren
Slotgedachten
SQL bestaat al meer dan 50 jaar, maar drijft nog steeds de belangrijkste applicaties ter wereld aan omdat het:
- Declaratief is
- Consistent is
- Voorspelbaar is
- Schaalbaar is
- Wordt ondersteund door tientallen jaren onderzoek
Zodra je begrijpt hoe tabellen en joins werken, wordt SQL een van de krachtigste tools in je programmeergereedschapskist.