Exercices SQL corrigés : les CTE (WITH)
Mis à jour le
WITH donne un nom à une requête intermédiaire, qu’on réutilise ensuite comme une table. Ces 15 exercices vont du niveau 5 au niveau 8 ; ils utilisent la syntaxe de SQLite, le moteur SQL de SpeedQL.
Conseil : Plusieurs CTE se suivent, séparées par des virgules, avant le SELECT final.
Relire la fiche « CTE (WITH) » de l’aide-mémoire
Exercice 1
Avec une CTE (WITH), calcule le salaire moyen de chaque département, puis affiche les départements dont la moyenne dépasse 45000.
| id | name | department | salary | manager_id | hired |
|---|---|---|---|---|---|
| 1 | Alice | Finance | 52000 | NULL | 2015-03-01 |
| 2 | Bob | IT | 41000 | 1 | 2018-06-15 |
| 3 | Claire | Finance | 38000 | 1 | 2019-01-10 |
| 4 | David | HR | 29000 | 1 | 2020-09-01 |
| 5 | Emma | IT | 50000 | 2 | 2017-11-20 |
| 6 | Farid | IT | 47000 | 2 | 2021-02-14 |
| 7 | Gaelle | HR | 33000 | 4 | 2022-05-30 |
| 8 | Hugo | Finance | 44000 | 3 | 2016-08-08 |
| 9 | Iris | IT | 47000 | 5 | 2023-01-09 |
| 10 | Jules | Finance | 44000 | 3 | 2024-03-18 |
Voir l’indice
Notions à utiliser : WHERE (filtres), COUNT, SUM, AVG, MIN, MAX, GROUP BY, CTE (WITH).
Structure de la requête :
WITH … AS (
SELECT …, AVG(…) AS …
FROM …
GROUP BY …)
SELECT …
FROM …
WHERE … > …Voir la correction
WITH avg_dept AS (
SELECT department, AVG(salary) AS a
FROM staff
GROUP BY department)
SELECT department
FROM avg_dept
WHERE a > 45000;Résultat attendu (1 ligne) :
| department |
|---|
| IT |
Exercice 2
Avec une CTE (WITH), calcule le total des salaires par département, puis affiche le département dont le total est le plus élevé.
| id | name | department | salary | manager_id | hired |
|---|---|---|---|---|---|
| 1 | Alice | Finance | 52000 | NULL | 2015-03-01 |
| 2 | Bob | IT | 41000 | 1 | 2018-06-15 |
| 3 | Claire | Finance | 38000 | 1 | 2019-01-10 |
| 4 | David | HR | 29000 | 1 | 2020-09-01 |
| 5 | Emma | IT | 50000 | 2 | 2017-11-20 |
| 6 | Farid | IT | 47000 | 2 | 2021-02-14 |
| 7 | Gaelle | HR | 33000 | 4 | 2022-05-30 |
| 8 | Hugo | Finance | 44000 | 3 | 2016-08-08 |
| 9 | Iris | IT | 47000 | 5 | 2023-01-09 |
| 10 | Jules | Finance | 44000 | 3 | 2024-03-18 |
Voir l’indice
Notions à utiliser : ORDER BY, LIMIT, COUNT, SUM, AVG, MIN, MAX, GROUP BY, CTE (WITH).
Structure de la requête :
WITH … AS (
SELECT …, SUM(…) AS …
FROM …
GROUP BY …)
SELECT …
FROM …
ORDER BY … DESC
LIMIT …Voir la correction
WITH t AS (
SELECT department, SUM(salary) AS s
FROM staff
GROUP BY department)
SELECT department
FROM t
ORDER BY s DESC
LIMIT 1;Résultat attendu (1 ligne) :
| department |
|---|
| IT |
Exercice 3
Avec une CTE (WITH), compte les emprunts de chaque membre, puis affiche le nom des membres qui ont emprunté plus que la moyenne (moyenne calculée sur les membres qui ont au moins un emprunt).
| id | name | city | joined |
|---|---|---|---|
| 1 | Lena | Lyon | 2022-01-15 |
| 2 | Marc | Paris | 2021-06-03 |
| 3 | Nadia | Lyon | 2023-03-20 |
| 4 | Oscar | Lille | 2020-11-11 |
| 5 | Paula | Paris | 2024-02-01 |
| 6 | Quentin | Nantes | 2023-09-09 |
| id | book_id | member_id | loan_date | return_date |
|---|---|---|---|---|
| 1 | 1 | 1 | 2025-01-05 | 2025-01-19 |
| 2 | 3 | 2 | 2025-01-10 | 2025-02-02 |
| 3 | 5 | 1 | 2025-02-01 | 2025-02-10 |
| 4 | 7 | 3 | 2025-02-03 | NULL |
| 5 | 3 | 4 | 2025-02-15 | 2025-03-01 |
| 6 | 2 | 2 | 2025-03-02 | 2025-03-30 |
| 7 | 8 | 5 | 2025-03-05 | NULL |
| 8 | 1 | 3 | 2025-03-10 | 2025-03-18 |
| 9 | 6 | 1 | 2025-03-20 | 2025-04-15 |
| 10 | 3 | 5 | 2025-04-01 | NULL |
| 11 | 9 | 4 | 2025-04-05 | 2025-04-12 |
| 12 | 10 | 2 | 2025-04-08 | 2025-04-20 |
Voir l’indice
Notions à utiliser : WHERE (filtres), COUNT, SUM, AVG, MIN, MAX, GROUP BY, JOIN, Sous-requêtes, CTE (WITH).
Structure de la requête :
WITH … AS (
SELECT …, COUNT(*) AS …
FROM …
GROUP BY …)
SELECT …
FROM …
JOIN … … ON … = …
WHERE … > (SELECT AVG(…) FROM …)Voir la correction
WITH c AS (
SELECT member_id, COUNT(*) AS n
FROM loans
GROUP BY member_id)
SELECT m.name
FROM c
JOIN members m ON m.id = c.member_id
WHERE c.n > (SELECT AVG(n) FROM c);Résultat attendu (2 lignes) :
| name |
|---|
| Lena |
| Marc |
Exercice 4
Avec une CTE (WITH), calcule le nombre total de buts de chaque match, puis affiche la date des matchs dont le total dépasse la moyenne des totaux.
| id | played_on | home_id | away_id | home_goals | away_goals |
|---|---|---|---|---|---|
| 1 | 2025-08-02 | 1 | 2 | 2 | 1 |
| 2 | 2025-08-03 | 3 | 4 | 0 | 0 |
| 3 | 2025-08-09 | 5 | 1 | 1 | 3 |
| 4 | 2025-08-10 | 2 | 3 | 2 | 2 |
| 5 | 2025-08-16 | 4 | 5 | 3 | 1 |
| 6 | 2025-08-17 | 1 | 3 | 1 | 0 |
| 7 | 2025-08-23 | 2 | 4 | 0 | 1 |
| 8 | 2025-08-24 | 3 | 5 | 2 | 3 |
| 9 | 2025-08-30 | 4 | 1 | 1 | 1 |
| 10 | 2025-08-31 | 5 | 2 | 0 | 2 |
Voir l’indice
Notions à utiliser : WHERE (filtres), COUNT, SUM, AVG, MIN, MAX, Sous-requêtes, CTE (WITH).
Structure de la requête :
WITH … AS (
SELECT …, … + … AS …
FROM …)
SELECT …
FROM …
WHERE … > (SELECT AVG(…) FROM …)Voir la correction
WITH t AS (
SELECT played_on, home_goals + away_goals AS g
FROM matches)
SELECT played_on
FROM t
WHERE g > (SELECT AVG(g) FROM t);Résultat attendu (5 lignes) :
| played_on |
|---|
| 2025-08-02 |
| 2025-08-09 |
| 2025-08-10 |
| 2025-08-16 |
| 2025-08-24 |
Exercice 5
Avec une CTE (WITH), calcule la température moyenne de chaque relevé ((max + min) / 2), puis affiche la ville, le jour et cette moyenne pour les relevés où elle dépasse 22.
| id | city | day | temp_max | temp_min | rain_mm |
|---|---|---|---|---|---|
| 1 | Paris | 2025-07-01 | 24 | 15 | 0 |
| 2 | Paris | 2025-07-02 | 27 | 17 | 0 |
| 3 | Paris | 2025-07-03 | 22 | 16 | 4.5 |
| 4 | Paris | 2025-07-04 | 19 | 14 | 12 |
| 5 | Paris | 2025-07-05 | 23 | 13 | 0 |
| 6 | Lyon | 2025-07-01 | 26 | 16 | 0 |
| 7 | Lyon | 2025-07-02 | 29 | 18 | 0 |
| 8 | Lyon | 2025-07-03 | 31 | 19 | 0 |
| 9 | Lyon | 2025-07-04 | 24 | 17 | 8.5 |
| 10 | Lyon | 2025-07-05 | 22 | 15 | 3 |
| 11 | Marseille | 2025-07-01 | 30 | 21 | 0 |
| 12 | Marseille | 2025-07-02 | 32 | 22 | 0 |
| 13 | Marseille | 2025-07-03 | 33 | 23 | 0 |
| 14 | Marseille | 2025-07-04 | 29 | 21 | 0 |
| 15 | Marseille | 2025-07-05 | 28 | 20 | 0 |
Voir l’indice
Notions à utiliser : WHERE (filtres), CTE (WITH).
Structure de la requête :
WITH … AS (
SELECT …, …, (… + …) / … AS …
FROM …)
SELECT …, …, …
FROM …
WHERE … > …Voir la correction
WITH m AS (
SELECT city, day, (temp_max + temp_min) / 2.0 AS t
FROM readings)
SELECT city, day, t
FROM m
WHERE t > 22;Résultat attendu (7 lignes) :
| city | day | t |
|---|---|---|
| Lyon | 2025-07-02 | 23.5 |
| Lyon | 2025-07-03 | 25 |
| Marseille | 2025-07-01 | 25.5 |
| Marseille | 2025-07-02 | 27 |
| Marseille | 2025-07-03 | 28 |
| Marseille | 2025-07-04 | 25 |
| Marseille | 2025-07-05 | 24 |
Exercice 6
Avec une CTE (WITH), calcule le montant de chaque réservation (nombre de nuits × prix de la chambre), puis affiche le numéro et le montant des réservations de plus de 500.
| id | hotel_id | type | price |
|---|---|---|---|
| 1 | 1 | single | 70 |
| 2 | 1 | double | 95 |
| 3 | 2 | double | 130 |
| 4 | 2 | suite | 210 |
| 5 | 3 | single | 110 |
| 6 | 3 | double | 150 |
| 7 | 4 | double | 65 |
| 8 | 5 | double | 260 |
| 9 | 5 | suite | 480 |
| 10 | 5 | single | 190 |
| id | room_id | guest_id | check_in | check_out |
|---|---|---|---|---|
| 1 | 2 | 1 | 2025-07-01 | 2025-07-04 |
| 2 | 5 | 2 | 2025-07-02 | 2025-07-05 |
| 3 | 8 | 3 | 2025-07-03 | 2025-07-06 |
| 4 | 3 | 4 | 2025-07-05 | 2025-07-12 |
| 5 | 6 | 5 | 2025-07-06 | 2025-07-08 |
| 6 | 1 | 2 | 2025-07-08 | 2025-07-10 |
| 7 | 9 | 3 | 2025-07-10 | 2025-07-11 |
| 8 | 7 | 5 | 2025-07-11 | 2025-07-15 |
| 9 | 4 | 1 | 2025-07-14 | 2025-07-16 |
| 10 | 6 | 4 | 2025-07-15 | 2025-07-19 |
| 11 | 6 | 5 | 2025-07-16 | 2025-07-18 |
Voir l’indice
Notions à utiliser : WHERE (filtres), JOIN, Texte et dates, CTE (WITH).
Structure de la requête :
WITH … AS (
SELECT …, (JULIANDAY(…) - JULIANDAY(…)) * … AS …
FROM … …
JOIN … … ON … = …)
SELECT …, CAST(… AS INTEGER)
FROM …
WHERE … > …Voir la correction
WITH a AS (
SELECT b.id, (julianday(b.check_out) - julianday(b.check_in)) * r.price AS amount
FROM bookings b
JOIN rooms r ON r.id = b.room_id)
SELECT id, CAST(amount AS INTEGER)
FROM a
WHERE amount > 500;Résultat attendu (3 lignes) :
| id | CAST(amount AS INTEGER) |
|---|---|
| 3 | 780 |
| 4 | 910 |
| 10 | 600 |
Exercice 7
Avec une CTE (WITH), calcule le coût de chaque projet (somme des heures × taux du développeur), puis affiche le nom et le coût des projets dont le coût dépasse 15 % du budget.
| id | name | client | budget | deadline |
|---|---|---|---|---|
| 1 | Atlas | Acme | 20000 | 2025-06-30 |
| 2 | Beacon | Bolt | 12000 | 2025-05-15 |
| 3 | Comet | Acme | 8000 | 2025-04-30 |
| 4 | Delta | Cyan | 15000 | 2025-07-31 |
| id | project_id | dev_id | title | hours | status | done_on |
|---|---|---|---|---|---|---|
| 1 | 1 | 1 | Landing page | 12 | done | 2025-03-10 |
| 2 | 1 | 3 | Data model | 20 | done | 2025-03-20 |
| 3 | 1 | 2 | Login | 8 | doing | NULL |
| 4 | 2 | 4 | ETL job | 16 | done | 2025-04-02 |
| 5 | 2 | 5 | CI pipeline | 6 | done | 2025-03-15 |
| 6 | 3 | 1 | Dashboard | 14 | todo | NULL |
| 7 | 3 | 3 | Report | 10 | done | 2025-04-25 |
| 8 | 4 | 2 | API | 18 | doing | NULL |
| 9 | 4 | 5 | Monitoring | 9 | todo | NULL |
| 10 | 4 | 4 | Forecast | 22 | done | 2025-05-05 |
| id | name | team | rate |
|---|---|---|---|
| 1 | Ana | Web | 55 |
| 2 | Bo | Web | 48 |
| 3 | Cleo | Data | 62 |
| 4 | Dan | Data | 58 |
| 5 | Eve | Ops | 50 |
| 6 | Finn | Ops | 45 |
Voir l’indice
Notions à utiliser : WHERE (filtres), COUNT, SUM, AVG, MIN, MAX, GROUP BY, JOIN, CTE (WITH).
Structure de la requête :
WITH … AS (
SELECT …, SUM(… * …) AS …
FROM … …
JOIN … … ON … = …
GROUP BY …)
SELECT …, …
FROM … …
JOIN … ON … = …
WHERE … > … * …Voir la correction
WITH c AS (
SELECT t.project_id, SUM(t.hours * d.rate) AS cost
FROM tasks t
JOIN devs d ON d.id = t.dev_id
GROUP BY t.project_id)
SELECT p.name, c.cost
FROM projects p
JOIN c ON c.project_id = p.id
WHERE c.cost > 0.15 * p.budget;Résultat attendu (2 lignes) :
| name | cost |
|---|---|
| Comet | 1390 |
| Delta | 2590 |
Exercice 8
Avec une CTE (WITH), calcule pour chaque titulaire le salaire reçu en janvier et en février 2025, puis affiche le nom et les deux montants des titulaires dont le salaire de février dépasse celui de janvier.
| id | owner | city | opened | kind |
|---|---|---|---|---|
| 1 | Alice | Paris | 2021-03-01 | current |
| 2 | Alice | Paris | 2022-06-15 | savings |
| 3 | Bruno | Lyon | 2020-09-10 | current |
| 4 | Chloe | Lyon | 2023-01-20 | current |
| 5 | David | Nice | 2019-11-05 | savings |
| 6 | Emma | Nice | 2024-04-01 | current |
| id | account_id | made_on | amount | label |
|---|---|---|---|---|
| 1 | 1 | 2025-01-02 | 2500 | salary |
| 2 | 1 | 2025-01-05 | -60 | groceries |
| 3 | 1 | 2025-01-12 | -800 | rent |
| 4 | 2 | 2025-01-15 | 500 | transfer |
| 5 | 3 | 2025-01-03 | 1900 | salary |
| 6 | 3 | 2025-01-20 | -120 | groceries |
| 7 | 3 | 2025-02-01 | -950 | rent |
| 8 | 4 | 2025-01-25 | 2100 | salary |
| 9 | 4 | 2025-02-03 | -45 | restaurant |
| 10 | 1 | 2025-02-02 | 2600 | salary |
| 11 | 1 | 2025-02-06 | -75 | groceries |
| 12 | 5 | 2025-02-10 | 30 | interest |
| 13 | 2 | 2025-02-15 | 500 | transfer |
| 14 | 4 | 2025-02-18 | -600 | rent |
Voir l’indice
Notions à utiliser : WHERE (filtres), COUNT, SUM, AVG, MIN, MAX, GROUP BY, JOIN, CASE, Texte et dates, CTE (WITH).
Structure de la requête :
WITH … AS (
SELECT …, SUM(CASE WHEN … LIKE … THEN … ELSE … END) AS …, SUM(CASE WHEN … LIKE … THEN … ELSE … END) AS …
FROM … …
JOIN … … ON … = …
WHERE … = …
GROUP BY …)
SELECT …, …, …
FROM …
WHERE … > …Voir la correction
WITH s AS (
SELECT a.owner, SUM(CASE WHEN t.made_on LIKE '2025-01%' THEN t.amount ELSE 0 END) AS jan, SUM(CASE WHEN t.made_on LIKE '2025-02%' THEN t.amount ELSE 0 END) AS feb
FROM accounts a
JOIN transactions t ON t.account_id = a.id
WHERE t.label = 'salary'
GROUP BY a.owner)
SELECT owner, jan, feb
FROM s
WHERE feb > jan;Résultat attendu (1 ligne) :
| owner | jan | feb |
|---|---|---|
| Alice | 2500 | 2600 |
Exercice 9
Pour chaque catégorie, affiche la catégorie, le nom du produit le plus vendu en quantité et cette quantité (en cas d'égalité, tous les produits ex æquo).
| id | name | category | price |
|---|---|---|---|
| 1 | Desk Lamp | Home | 35 |
| 2 | Coffee Mug | Kitchen | 12 |
| 3 | Notebook | Office | 6 |
| 4 | Office Chair | Office | 149 |
| 5 | Kettle | Kitchen | 45 |
| 6 | Cushion | Home | 22 |
| 7 | Stapler | Office | 9 |
| order_id | product_id | qty |
|---|---|---|
| 1 | 1 | 1 |
| 1 | 2 | 2 |
| 2 | 4 | 1 |
| 3 | 3 | 5 |
| 3 | 2 | 1 |
| 4 | 5 | 1 |
| 5 | 6 | 2 |
| 5 | 1 | 1 |
| 6 | 2 | 4 |
| 6 | 3 | 3 |
| 7 | 4 | 1 |
| 7 | 6 | 1 |
| 8 | 5 | 2 |
| 8 | 2 | 1 |
Voir l’indice
Notions à utiliser : WHERE (filtres), COUNT, SUM, AVG, MIN, MAX, GROUP BY, JOIN, Sous-requêtes, CTE (WITH).
Structure de la requête :
WITH … AS (
SELECT …, …, SUM(…) AS …
FROM … …
JOIN … … ON … = …
GROUP BY …)
SELECT …, …, …
FROM …
WHERE … = (SELECT MAX(…) FROM … … WHERE … = …)Voir la correction
WITH q AS (
SELECT p.category, p.name, SUM(oi.qty) AS s
FROM products p
JOIN order_items oi ON oi.product_id = p.id
GROUP BY p.id)
SELECT category, name, s
FROM q
WHERE s = (SELECT MAX(s) FROM q q2 WHERE q2.category = q.category);Résultat attendu (3 lignes) :
| category | name | s |
|---|---|---|
| Kitchen | Coffee Mug | 8 |
| Office | Notebook | 8 |
| Home | Cushion | 3 |
Exercice 10
Avec une CTE (WITH), calcule le total facturé par client, puis affiche client, ce total et une colonne size valant 'big' si le total est d'au moins 1500, sinon 'small'. Utilise CASE.
| id | client | issued | due | amount | paid_on |
|---|---|---|---|---|---|
| 1 | Acme | 2025-01-15 | 2025-02-14 | 1200 | 2025-02-10 |
| 2 | Acme | 2025-03-01 | 2025-03-16 | 800 | NULL |
| 3 | Bolt | 2025-01-20 | 2025-03-06 | 450 | 2025-03-01 |
| 4 | Bolt | 2025-02-25 | 2025-03-27 | 950 | 2025-04-02 |
| 5 | Cyan | 2025-03-05 | 2025-05-04 | 300 | NULL |
| 6 | Cyan | 2024-12-10 | 2024-12-30 | 600 | 2025-01-05 |
Voir l’indice
Notions à utiliser : COUNT, SUM, AVG, MIN, MAX, GROUP BY, CASE, CTE (WITH).
Structure de la requête :
WITH … AS (
SELECT …, SUM(…) AS …
FROM …
GROUP BY …)
SELECT …, …, CASE WHEN … >= … THEN … ELSE … END AS …
FROM …Voir la correction
WITH t AS (
SELECT client, SUM(amount) AS total
FROM invoices
GROUP BY client)
SELECT client, total, CASE WHEN total >= 1500 THEN 'big' ELSE 'small' END AS size
FROM t;Résultat attendu (3 lignes) :
| client | total | size |
|---|---|---|
| Acme | 2000 | big |
| Bolt | 1400 | small |
| Cyan | 900 | small |
Exercice 11
Avec une CTE (WITH), calcule les points de chaque équipe sur tous ses matchs, à domicile et à l'extérieur (victoire 3 points, nul 1 point, défaite 0), puis affiche le nom de l'équipe et son total de points, du plus grand total au plus petit (en cas d'égalité, par ordre alphabétique du nom).
| id | name | city | founded |
|---|---|---|---|
| 1 | Red Foxes | Lyon | 1950 |
| 2 | Blue Owls | Paris | 1962 |
| 3 | Green Bulls | Lille | 1971 |
| 4 | Gold Hawks | Nantes | 1988 |
| 5 | Grey Wolves | Paris | 1990 |
| id | played_on | home_id | away_id | home_goals | away_goals |
|---|---|---|---|---|---|
| 1 | 2025-08-02 | 1 | 2 | 2 | 1 |
| 2 | 2025-08-03 | 3 | 4 | 0 | 0 |
| 3 | 2025-08-09 | 5 | 1 | 1 | 3 |
| 4 | 2025-08-10 | 2 | 3 | 2 | 2 |
| 5 | 2025-08-16 | 4 | 5 | 3 | 1 |
| 6 | 2025-08-17 | 1 | 3 | 1 | 0 |
| 7 | 2025-08-23 | 2 | 4 | 0 | 1 |
| 8 | 2025-08-24 | 3 | 5 | 2 | 3 |
| 9 | 2025-08-30 | 4 | 1 | 1 | 1 |
| 10 | 2025-08-31 | 5 | 2 | 0 | 2 |
Voir l’indice
Notions à utiliser : ORDER BY, LIMIT, COUNT, SUM, AVG, MIN, MAX, GROUP BY, JOIN, CASE, UNION, INTERSECT, EXCEPT, CTE (WITH).
Structure de la requête :
WITH … AS (
SELECT … AS …, CASE WHEN … > … THEN … WHEN … = … THEN … ELSE … END AS …
FROM …
UNION ALL SELECT …, CASE WHEN … > … THEN … WHEN … = … THEN … ELSE … END
FROM …)
SELECT …, SUM(…)
FROM …
JOIN … … ON … = …
GROUP BY …
ORDER BY SUM(…) DESC, …Voir la correction
WITH r AS (
SELECT home_id AS team, CASE WHEN home_goals > away_goals THEN 3 WHEN home_goals = away_goals THEN 1 ELSE 0 END AS pts
FROM matches
UNION ALL SELECT away_id, CASE WHEN away_goals > home_goals THEN 3 WHEN away_goals = home_goals THEN 1 ELSE 0 END
FROM matches)
SELECT t.name, SUM(r.pts)
FROM r
JOIN teams t ON t.id = r.team
GROUP BY t.id
ORDER BY SUM(r.pts) DESC, t.name;Résultat attendu (5 lignes, dans cet ordre) :
| name | SUM(r.pts) |
|---|---|
| Red Foxes | 10 |
| Gold Hawks | 8 |
| Blue Owls | 4 |
| Grey Wolves | 3 |
| Green Bulls | 2 |
Exercice 12
Avec une CTE (WITH), calcule le taux de remplissage de chaque vol (places vendues / capacité, en %), puis affiche pour chaque compagnie son taux moyen arrondi à 1 décimale et le nombre de ses vols remplis à plus de 80 %.
| id | airline | origin | dest | departs | duration_min | price | seats_sold | capacity |
|---|---|---|---|---|---|---|---|---|
| 1 | SkyJet | CDG | MAD | 2025-06-01 08:10 | 125 | 89 | 150 | 180 |
| 2 | AirNova | CDG | LIS | 2025-06-01 11:40 | 155 | 120 | 160 | 170 |
| 3 | BlueWing | LYS | FCO | 2025-06-02 07:30 | 95 | 75 | 110 | 150 |
| 4 | SkyJet | MAD | CDG | 2025-06-02 18:20 | 120 | 95 | 170 | 180 |
| 5 | AirNova | BER | CDG | 2025-06-03 09:05 | 110 | 105 | 140 | 160 |
| 6 | BlueWing | NCE | BER | 2025-06-03 13:50 | 130 | 140 | 90 | 150 |
| 7 | SkyJet | CDG | FCO | 2025-06-04 06:45 | 135 | 99 | 175 | 180 |
| 8 | AirNova | LIS | MAD | 2025-06-04 16:15 | 75 | 65 | 60 | 120 |
| 9 | BlueWing | FCO | LYS | 2025-06-05 20:30 | 100 | 82 | 130 | 150 |
| 10 | SkyJet | CDG | BER | 2025-06-05 10:00 | 105 | 110 | 120 | 180 |
| 11 | AirNova | MAD | LIS | 2025-06-06 12:25 | 80 | 70 | 95 | 120 |
| 12 | BlueWing | LYS | MAD | 2025-06-06 15:40 | 115 | 79 | 100 | 150 |
Voir l’indice
Notions à utiliser : COUNT, SUM, AVG, MIN, MAX, GROUP BY, CASE, CTE (WITH).
Structure de la requête :
WITH … AS (
SELECT …, … * … / … AS …
FROM …)
SELECT …, ROUND(AVG(…), …), SUM(CASE WHEN … > … THEN … ELSE … END)
FROM …
GROUP BY …Voir la correction
WITH r AS (
SELECT airline, 100.0 * seats_sold / capacity AS t
FROM flights)
SELECT airline, ROUND(AVG(t), 1), SUM(CASE WHEN t > 80 THEN 1 ELSE 0 END)
FROM r
GROUP BY airline;Résultat attendu (3 lignes) :
| airline | ROUND(AVG(t), 1) | SUM(CASE WHEN t > 80 THEN 1 ELSE 0 END) |
|---|---|---|
| AirNova | 77.7 | 2 |
| BlueWing | 71.7 | 1 |
| SkyJet | 85.4 | 3 |
Exercice 13
Avec une CTE (WITH), calcule pour chaque jour la moyenne des températures maximales de toutes les villes, puis affiche le jour, la ville et l'écart (arrondi à 1 décimale) des relevés qui dépassent cette moyenne de plus de 3 degrés.
| id | city | day | temp_max | temp_min | rain_mm |
|---|---|---|---|---|---|
| 1 | Paris | 2025-07-01 | 24 | 15 | 0 |
| 2 | Paris | 2025-07-02 | 27 | 17 | 0 |
| 3 | Paris | 2025-07-03 | 22 | 16 | 4.5 |
| 4 | Paris | 2025-07-04 | 19 | 14 | 12 |
| 5 | Paris | 2025-07-05 | 23 | 13 | 0 |
| 6 | Lyon | 2025-07-01 | 26 | 16 | 0 |
| 7 | Lyon | 2025-07-02 | 29 | 18 | 0 |
| 8 | Lyon | 2025-07-03 | 31 | 19 | 0 |
| 9 | Lyon | 2025-07-04 | 24 | 17 | 8.5 |
| 10 | Lyon | 2025-07-05 | 22 | 15 | 3 |
| 11 | Marseille | 2025-07-01 | 30 | 21 | 0 |
| 12 | Marseille | 2025-07-02 | 32 | 22 | 0 |
| 13 | Marseille | 2025-07-03 | 33 | 23 | 0 |
| 14 | Marseille | 2025-07-04 | 29 | 21 | 0 |
| 15 | Marseille | 2025-07-05 | 28 | 20 | 0 |
Voir l’indice
Notions à utiliser : WHERE (filtres), COUNT, SUM, AVG, MIN, MAX, GROUP BY, JOIN, CTE (WITH).
Structure de la requête :
WITH … AS (
SELECT …, AVG(…) AS …
FROM …
GROUP BY …)
SELECT …, …, ROUND(… - …, …)
FROM … …
JOIN … ON … = …
WHERE … - … > …Voir la correction
WITH a AS (
SELECT day, AVG(temp_max) AS m
FROM readings
GROUP BY day)
SELECT r.day, r.city, ROUND(r.temp_max - a.m, 1)
FROM readings r
JOIN a ON a.day = r.day
WHERE r.temp_max - a.m > 3;Résultat attendu (4 lignes) :
| day | city | ROUND(r.temp_max - a.m, 1) |
|---|---|---|
| 2025-07-01 | Marseille | 3.3 |
| 2025-07-03 | Marseille | 4.3 |
| 2025-07-04 | Marseille | 5 |
| 2025-07-05 | Marseille | 3.7 |
Exercice 14
Pour chaque projet, affiche son nom, son budget, son coût (heures × taux, 0 sans tâche), le budget restant et la part du budget consommée en % arrondie à 1 décimale, de la plus forte part à la plus faible (en cas d'égalité, par nom).
| id | name | client | budget | deadline |
|---|---|---|---|---|
| 1 | Atlas | Acme | 20000 | 2025-06-30 |
| 2 | Beacon | Bolt | 12000 | 2025-05-15 |
| 3 | Comet | Acme | 8000 | 2025-04-30 |
| 4 | Delta | Cyan | 15000 | 2025-07-31 |
| id | project_id | dev_id | title | hours | status | done_on |
|---|---|---|---|---|---|---|
| 1 | 1 | 1 | Landing page | 12 | done | 2025-03-10 |
| 2 | 1 | 3 | Data model | 20 | done | 2025-03-20 |
| 3 | 1 | 2 | Login | 8 | doing | NULL |
| 4 | 2 | 4 | ETL job | 16 | done | 2025-04-02 |
| 5 | 2 | 5 | CI pipeline | 6 | done | 2025-03-15 |
| 6 | 3 | 1 | Dashboard | 14 | todo | NULL |
| 7 | 3 | 3 | Report | 10 | done | 2025-04-25 |
| 8 | 4 | 2 | API | 18 | doing | NULL |
| 9 | 4 | 5 | Monitoring | 9 | todo | NULL |
| 10 | 4 | 4 | Forecast | 22 | done | 2025-05-05 |
| id | name | team | rate |
|---|---|---|---|
| 1 | Ana | Web | 55 |
| 2 | Bo | Web | 48 |
| 3 | Cleo | Data | 62 |
| 4 | Dan | Data | 58 |
| 5 | Eve | Ops | 50 |
| 6 | Finn | Ops | 45 |
Voir l’indice
Notions à utiliser : ORDER BY, LIMIT, COUNT, SUM, AVG, MIN, MAX, GROUP BY, JOIN, NULL, COALESCE, CTE (WITH).
Structure de la requête :
WITH … AS (
SELECT …, …, …, COALESCE(SUM(… * …), …) AS …
FROM … …
LEFT JOIN … … ON … = …
LEFT JOIN … … ON … = …
GROUP BY …)
SELECT …, …, …, … - …, ROUND(… * … / …, …)
FROM …
ORDER BY … * … / … DESC, …Voir la correction
WITH c AS (
SELECT p.id, p.name, p.budget, COALESCE(SUM(t.hours * d.rate), 0) AS cost
FROM projects p
LEFT JOIN tasks t ON t.project_id = p.id
LEFT JOIN devs d ON d.id = t.dev_id
GROUP BY p.id)
SELECT name, budget, cost, budget - cost, ROUND(100.0 * cost / budget, 1)
FROM c
ORDER BY 1.0 * cost / budget DESC, name;Résultat attendu (4 lignes, dans cet ordre) :
| name | budget | cost | budget - cost | ROUND(100.0 * cost / budget, 1) |
|---|---|---|---|---|
| Comet | 8000 | 1390 | 6610 | 17.4 |
| Delta | 15000 | 2590 | 12410 | 17.3 |
| Atlas | 20000 | 2284 | 17716 | 11.4 |
| Beacon | 12000 | 1228 | 10772 | 10.2 |
Exercice 15
Affiche le nom des titulaires dont les dépenses dépassent 30 % de leurs revenus (tous comptes confondus), avec ce ratio en % arrondi à 1 décimale.
| id | owner | city | opened | kind |
|---|---|---|---|---|
| 1 | Alice | Paris | 2021-03-01 | current |
| 2 | Alice | Paris | 2022-06-15 | savings |
| 3 | Bruno | Lyon | 2020-09-10 | current |
| 4 | Chloe | Lyon | 2023-01-20 | current |
| 5 | David | Nice | 2019-11-05 | savings |
| 6 | Emma | Nice | 2024-04-01 | current |
| id | account_id | made_on | amount | label |
|---|---|---|---|---|
| 1 | 1 | 2025-01-02 | 2500 | salary |
| 2 | 1 | 2025-01-05 | -60 | groceries |
| 3 | 1 | 2025-01-12 | -800 | rent |
| 4 | 2 | 2025-01-15 | 500 | transfer |
| 5 | 3 | 2025-01-03 | 1900 | salary |
| 6 | 3 | 2025-01-20 | -120 | groceries |
| 7 | 3 | 2025-02-01 | -950 | rent |
| 8 | 4 | 2025-01-25 | 2100 | salary |
| 9 | 4 | 2025-02-03 | -45 | restaurant |
| 10 | 1 | 2025-02-02 | 2600 | salary |
| 11 | 1 | 2025-02-06 | -75 | groceries |
| 12 | 5 | 2025-02-10 | 30 | interest |
| 13 | 2 | 2025-02-15 | 500 | transfer |
| 14 | 4 | 2025-02-18 | -600 | rent |
Voir l’indice
Notions à utiliser : WHERE (filtres), COUNT, SUM, AVG, MIN, MAX, GROUP BY, JOIN, CASE, CTE (WITH).
Structure de la requête :
WITH … AS (
SELECT …, SUM(CASE WHEN … > … THEN … ELSE … END) AS …, -SUM(CASE WHEN … < … THEN … ELSE … END) AS …
FROM … …
JOIN … … ON … = …
GROUP BY …)
SELECT …, ROUND(… * … / …, …)
FROM …
WHERE … > … AND … > … * …Voir la correction
WITH s AS (
SELECT a.owner, SUM(CASE WHEN t.amount > 0 THEN t.amount ELSE 0 END) AS income, -SUM(CASE WHEN t.amount < 0 THEN t.amount ELSE 0 END) AS spent
FROM accounts a
JOIN transactions t ON t.account_id = a.id
GROUP BY a.owner)
SELECT owner, ROUND(100.0 * spent / income, 1)
FROM s
WHERE income > 0 AND spent > 0.3 * income;Résultat attendu (2 lignes) :
| owner | ROUND(100.0 * spent / income, 1) |
|---|---|
| Bruno | 56.3 |
| Chloe | 30.7 |
S’entraîner avec correction automatique
Dans SpeedQL, tu écris ta requête et elle est vérifiée tout de suite, sur ces tables puis sur un jeu de données caché.