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:

    1. Ontwikkelaars een eenvoudige, leesbare taal nodig hadden om gegevens te bevragen.
    2. Bedrijven consistente regels nodig hadden om relaties op te slaan.
    3. 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

    Tabel 2: orders

    iduser_idproductprice
    11Coffee Mug12.99
    21Hoodie39.99
    32Stickers4.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
    • 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_id overeenkomt met een echte id in de tabel users.
    • 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 users We voegen nieuwe rijen toe aan de tabel users.

    • (name, email) We geven alleen name en email op, niet id.

      Waarom geven we de ID niet op? Omdat de kolom id een serieel/automatisch oplopend type gebruikt (SERIAL van PostgreSQL), maakt de database ID's automatisch aan:

    • Eerste gebruiker → id = 1

    • Tweede gebruiker → id = 2

    • Derde 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');
    
    • VALUES leidt 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:

    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 hoort
      • product → naam
      • price → 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:

    1. De gebruikers eerst zijn ingevoegd.

    2. Automatisch oplopende ID's altijd opeenvolgende gehele getallen toewijzen.

    3. 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

    orders

    iduser_idproductprice
    11Coffee Mug12.99
    21Hoodie39.99
    32Stickers4.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:

    2. Gebruikers selecteren met een filter

    SELECT * FROM users
    WHERE email LIKE '%alice@example.com';
    

    Wat dit betekent:

    • LIKE is 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 op alice@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.com
    • hello.alice@example.com
    • anythingalice@example.com

    Als die bestonden.

    Resultaat:

    idnameemail
    1Alicealice@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:

    iduser_idproductprice
    11Coffee Mug12.99
    21Hoodie39.99
    32Stickers4.99
    42T-Shirt29.99
    • De nieuwe rij heeft id = 4, automatisch gegenereerd.
    • user_id = 2 koppelt 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:

    nameproductprice
    AliceCoffee Mug12.99
    AliceHoodie39.99
    BobStickers4.99
    BobT-Shirt29.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:

    nameproduct
    AliceCoffee Mug
    AliceHoodie
    BobStickers
    BobT-Shirt
    Carolinenull

    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:

    nametotal_spent
    Alice52.98
    Bob34.98
    Carolinenull

    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.