Vous pensez maîtriser les bases de données relationnelles ?
Prouvez-le avant de pouvoir accéder au NoSQL.
Tous les défis tournent sur un vrai jeu de données PostgreSQL : 50 clients, 200 000 achats, des fantômes et de la pollution.
createdb -h localhost -U postgres sqlfun psql -h localhost -U postgres -d sqlfun -f jeu-de-donnees-sql-fun.sql
Ou dans pgAdmin 4 : créer la base sqlfun, ouvrir le fichier dans l'outil de requête, puis Exécuter (F5).
Votre base contient des millions de transactions. Le directeur exige de trouver tous les clients ayant dépensé plus que la moyenne globale, mais uniquement sur le dernier trimestre.
Concevez la structure de la requête. Pas le temps pour le hasard.
🛠️ Armes requises : GROUP BY, HAVING, et une Sous-requête scalaire.
La sous-requête renvoie une seule valeur, la moyenne des totaux par client : c'est ce qui la rend scalaire.
SELECT client_id, SUM(montant) AS total
FROM achats
WHERE date_achat BETWEEN '2026-07-01' AND '2026-09-30'
GROUP BY client_id
HAVING SUM(montant) > (
SELECT AVG(total_client) FROM (
SELECT SUM(montant) AS total_client FROM achats
WHERE date_achat BETWEEN '2026-07-01' AND '2026-09-30'
GROUP BY client_id) AS totaux);💀 Mode hardcore : écrivez-la sans répéter le filtre de dates, avec un WITH totaux AS (...).
La base est corrompue. Des factures existent pour des clients qui ont été supprimés du système.
Écrivez la logique pour isoler ces enregistrements fantômes afin de les purger.
🛠️ Armes requises : LEFT JOIN couplé à une condition IS NULL sur la table de droite.
Le LEFT JOIN garde toutes les factures. Quand le client n'existe plus, ses colonnes reviennent à NULL : c'est la signature du fantôme.
SELECT factures.* FROM factures LEFT JOIN clients ON clients.id = factures.client_id WHERE clients.id IS NULL;
Dans l'arène : 4 factures fantômes, celles des clients 51, 52 et 53.
💀 Mode hardcore : purgez-les avec un DELETE ... WHERE NOT EXISTS, puis empêchez leur retour : quelle contrainte manquait à la table ?
Le marketing veut envoyer une promotion ciblée en se basant uniquement sur le tout dernier achat de chaque client dans la table `achats`.
Un simple `GROUP BY` ne suffit pas pour ramener toutes les colonnes de la ligne la plus récente.
🛠️ Arme fatale : Les Fonctions de fenêtrage ROW_NUMBER() OVER(PARTITION BY ...)
ROW_NUMBER() numérote les achats de chaque client, du plus récent au plus ancien. On garde le numéro 1, avec toutes ses colonnes.
SELECT * FROM (
SELECT achats.*,
ROW_NUMBER() OVER (PARTITION BY client_id
ORDER BY date_achat DESC, id DESC) AS rang
FROM achats) AS classement
WHERE rang = 1;💀 Mode hardcore : retirez id DESC et remplacez ROW_NUMBER() par RANK() : pourquoi certains clients ont-ils soudain deux « derniers » achats ?
La table `achats` compte 200 000 lignes. Le service client cherche les achats du client 42 à chaque appel... et l'écran gèle.
Mesurez la requête, accélérez-la, puis mesurez encore. Le gain doit être prouvé par un chiffre, pas par une impression.
🛠️ Armes requises : EXPLAIN ANALYZE et CREATE INDEX.
Sans index, la base lit tout. Avec un index, elle va droit aux bonnes lignes.
EXPLAIN ANALYZE SELECT * FROM achats WHERE client_id = 42; -- Seq Scan : 200 000 lignes lues, 196 004 jetées CREATE INDEX achats_client_id_index ON achats (client_id); EXPLAIN ANALYZE SELECT * FROM achats WHERE client_id = 42; -- Bitmap Index Scan : seulement les 4 086 bonnes lignes
Mesuré sur notre machine de test : de 80 ms à 7 ms, environ 10 fois plus rapide.
💀 Mode hardcore : ouvrez le Simulateur des cubes transitoires : quel ordre de jointure fait descendre le plus gros cube ?
Les achats de 2024 dorment dans `achats_archive`. Le comptable veut le total de tout ce que la cliente 3 a dépensé depuis toujours.
Réunissez les deux tables et faites la somme. Attention : la cliente 3 a acheté deux souris identiques le 24 décembre 2024.
🛠️ Armes requises : UNION ou UNION ALL ? Un seul des deux donne le bon montant.
UNION élimine les rangées identiques : une des deux souris disparaît. UNION ALL garde tout.
SELECT SUM(montant) FROM ( SELECT client_id, montant, date_achat FROM achats_archive WHERE client_id = 3 UNION ALL SELECT client_id, montant, date_achat FROM achats WHERE client_id = 3) AS tout;
Avec UNION ALL : 822 516,33 $. Avec UNION : 822 491,34 $. Il manque les 24,99 $ de la souris jumelle.
💀 Mode hardcore : quand UNION est-il le bon choix ? Listez les clients qui ont acheté au moins une fois, en 2024 ou après.
Ces exercices n'étaient que l'échauffement de vos neurones.
Rendez-vous dans la Salle d'entraînement pour affronter Schemaverse et le SQL Murder Mystery.
Seuls les survivants du SQL accèderont au Big Data.