Exercices SQL corrigés : les sous-requêtes

Exercices SQL corrigés : les sous-requêtes

Mis à jour le

Une sous-requête est une requête dans une requête : une valeur, une liste (IN) ou une table. Ces 15 exercices vont du niveau 2 au niveau 8 ; ils utilisent la syntaxe de SQLite, le moteur SQL de SpeedQL.

Conseil : Écris d’abord la sous-requête seule pour vérifier son résultat, puis intègre-la.

Relire la fiche « Sous-requêtes » de l’aide-mémoire

Exercice 1 · niveau 2

Affiche le nom des employés dont le salaire est supérieur au salaire moyen de tous les employés.

Table employees (8 lignes)
idnamedepartmentsalary
1AliceFinance32000
2BobIT41000
3ClaireFinance38000
4DavidHR29000
5EmmaIT50000
6FaridIT47000
7GaelleHR33000
8HugoFinance44000
Voir l’indice

Notions à utiliser : WHERE (filtres), COUNT, SUM, AVG, MIN, MAX, Sous-requêtes.

Structure de la requête :

SELECT 
FROM 
WHERE  > (SELECT AVG() FROM )
Voir la correction
SELECT name
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);

Résultat attendu (4 lignes) :

name
Bob
Emma
Farid
Hugo

Exercice 2 · niveau 3

Affiche le nom des clients qui n'ont passé aucune commande.

Table customers (4 lignes)
idnamecity
1AliceParis
2BrunoLyon
3ChloeParis
4DylanNantes
Table orders (6 lignes)
idcustomer_idamount
11120
2180
32200
4350
5375
6330
Voir l’indice

Notions à utiliser : WHERE (filtres), Sous-requêtes.

Structure de la requête :

SELECT 
FROM 
WHERE  NOT IN (SELECT  FROM )
Voir la correction
SELECT name
FROM customers
WHERE id NOT IN (SELECT customer_id FROM orders);

Résultat attendu (1 ligne) :

name
Dylan

Exercice 3 · niveau 3

Affiche le nom des employés dont le salaire est supérieur au salaire moyen de leur propre département.

Table employees (8 lignes)
idnamedepartmentsalary
1AliceFinance32000
2BobIT41000
3ClaireFinance38000
4DavidHR29000
5EmmaIT50000
6FaridIT47000
7GaelleHR33000
8HugoFinance44000
Voir l’indice

Notions à utiliser : WHERE (filtres), COUNT, SUM, AVG, MIN, MAX, Sous-requêtes.

Structure de la requête :

SELECT 
FROM  
WHERE  > (SELECT AVG() FROM  WHERE  = )
Voir la correction
SELECT name
FROM employees e
WHERE salary > (SELECT AVG(salary) FROM employees WHERE department = e.department);

Résultat attendu (4 lignes) :

name
Emma
Farid
Gaelle
Hugo

Exercice 4 · niveau 3

Affiche le code et la ville des aéroports où n'arrive aucun vol.

Table airports (7 lignes)
codecitycountry
CDGParisFrance
LYSLyonFrance
MADMadridSpain
LISLisbonPortugal
FCORomeItaly
BERBerlinGermany
NCENiceFrance
Table flights (12 lignes)
idairlineorigindestdepartsduration_minpriceseats_soldcapacity
1SkyJetCDGMAD2025-06-01 08:1012589150180
2AirNovaCDGLIS2025-06-01 11:40155120160170
3BlueWingLYSFCO2025-06-02 07:309575110150
4SkyJetMADCDG2025-06-02 18:2012095170180
5AirNovaBERCDG2025-06-03 09:05110105140160
6BlueWingNCEBER2025-06-03 13:5013014090150
7SkyJetCDGFCO2025-06-04 06:4513599175180
8AirNovaLISMAD2025-06-04 16:15756560120
9BlueWingFCOLYS2025-06-05 20:3010082130150
10SkyJetCDGBER2025-06-05 10:00105110120180
11AirNovaMADLIS2025-06-06 12:25807095120
12BlueWingLYSMAD2025-06-06 15:4011579100150
Voir l’indice

Notions à utiliser : WHERE (filtres), Sous-requêtes.

Structure de la requête :

SELECT , 
FROM 
WHERE  NOT IN (SELECT  FROM )
Voir la correction
SELECT code, city
FROM airports
WHERE code NOT IN (SELECT dest FROM flights);

Résultat attendu (1 ligne) :

codecity
NCENice

Exercice 5 · niveau 5

Affiche le nom et le département du mieux payé de chaque département (sous-requête corrélée).

Table staff (10 lignes)
idnamedepartmentsalarymanager_idhired
1AliceFinance52000NULL2015-03-01
2BobIT4100012018-06-15
3ClaireFinance3800012019-01-10
4DavidHR2900012020-09-01
5EmmaIT5000022017-11-20
6FaridIT4700022021-02-14
7GaelleHR3300042022-05-30
8HugoFinance4400032016-08-08
9IrisIT4700052023-01-09
10JulesFinance4400032024-03-18
Voir l’indice

Notions à utiliser : WHERE (filtres), COUNT, SUM, AVG, MIN, MAX, Sous-requêtes.

Structure de la requête :

SELECT , 
FROM 
WHERE  = (SELECT MAX() FROM   WHERE  = )
Voir la correction
SELECT name, department
FROM staff
WHERE salary = (SELECT MAX(salary) FROM staff s WHERE s.department = staff.department);

Résultat attendu (3 lignes) :

namedepartment
AliceFinance
EmmaIT
GaelleHR

Exercice 6 · niveau 5

Affiche le titre des films dont la note dépasse la note moyenne des films de leur propre genre.

Table movies (10 lignes)
idtitlegenreyeardurationratingdirector_id
1Night TrainThriller20151187.81
2Blue HarborDrama20181027.12
3Paper Moon CityComedy2012956.45
4Silent PeakDrama20201318.23
5Last SignalSci-Fi20191427.51
6Summer KeysComedy2016885.94
7Iron GardenSci-Fi202112583
8Dust and GoldWestern20141106.82
9The Quiet HourDrama2022977.45
10Deep CurrentThriller20171056.9NULL
Voir l’indice

Notions à utiliser : WHERE (filtres), COUNT, SUM, AVG, MIN, MAX, Sous-requêtes.

Structure de la requête :

SELECT 
FROM  
WHERE  > (SELECT AVG() FROM   WHERE  = )
Voir la correction
SELECT title
FROM movies m
WHERE rating > (SELECT AVG(rating) FROM movies x WHERE x.genre = m.genre);

Résultat attendu (4 lignes) :

title
Night Train
Paper Moon City
Silent Peak
Iron Garden

Exercice 7 · niveau 5

Affiche le nom et le nombre de buts des joueurs qui ont marqué plus que la moyenne des joueurs de leur équipe.

Table players (9 lignes)
idnameteam_idpositiongoals
1Alex Moreau1FW9
2Bilal Sow1MF4
3Carl Weber2FW7
4Diego Ruiz2DF1
5Eli Novak3FW6
6Femi Adeyemi4FW11
7Goran Petrov4GK0
8Hugo Lamy5MF5
9Ivan Kral5DF2
Voir l’indice

Notions à utiliser : WHERE (filtres), COUNT, SUM, AVG, MIN, MAX, Sous-requêtes.

Structure de la requête :

SELECT , 
FROM  
WHERE  > (SELECT AVG() FROM   WHERE  = )
Voir la correction
SELECT name, goals
FROM players p
WHERE goals > (SELECT AVG(goals) FROM players x WHERE x.team_id = p.team_id);

Résultat attendu (4 lignes) :

namegoals
Alex Moreau9
Carl Weber7
Femi Adeyemi11
Hugo Lamy5

Exercice 8 · niveau 5

Affiche le titre des chansons plus longues que la durée moyenne des chansons de leur artiste.

Table songs (10 lignes)
idtitleartist_idgenreduration_sreleased
1Glass Heart1Pop2142019-04-12
2Low Tide1Pop1982021-06-01
3Rust2Rock2562008-09-30
4Wires2Rock3012015-02-14
5Sunday Market3Afrobeat2332020-11-20
6Palm Wine3Afrobeat2452022-03-03
7Alma4Latin1892013-07-07
8Brisa4Pop2052018-05-25
9Night Drive5Electro2762021-10-10
10Pulse5Electro2302023-01-15
Voir l’indice

Notions à utiliser : WHERE (filtres), COUNT, SUM, AVG, MIN, MAX, Sous-requêtes.

Structure de la requête :

SELECT 
FROM  
WHERE  > (SELECT AVG() FROM   WHERE  = )
Voir la correction
SELECT title
FROM songs s
WHERE duration_s > (SELECT AVG(duration_s) FROM songs x WHERE x.artist_id = s.artist_id);

Résultat attendu (5 lignes) :

title
Glass Heart
Wires
Palm Wine
Brisa
Night Drive

Exercice 9 · niveau 5

Affiche la ville, le jour et la température maximale des relevés plus chauds que la moyenne des maximales de leur ville.

Table readings (15 lignes)
idcitydaytemp_maxtemp_minrain_mm
1Paris2025-07-0124150
2Paris2025-07-0227170
3Paris2025-07-0322164.5
4Paris2025-07-04191412
5Paris2025-07-0523130
6Lyon2025-07-0126160
7Lyon2025-07-0229180
8Lyon2025-07-0331190
9Lyon2025-07-0424178.5
10Lyon2025-07-0522153
11Marseille2025-07-0130210
12Marseille2025-07-0232220
13Marseille2025-07-0333230
14Marseille2025-07-0429210
15Marseille2025-07-0528200
Voir l’indice

Notions à utiliser : WHERE (filtres), COUNT, SUM, AVG, MIN, MAX, Sous-requêtes.

Structure de la requête :

SELECT , , 
FROM  
WHERE  > (SELECT AVG() FROM   WHERE  = )
Voir la correction
SELECT city, day, temp_max
FROM readings r
WHERE temp_max > (SELECT AVG(temp_max) FROM readings x WHERE x.city = r.city);

Résultat attendu (6 lignes) :

citydaytemp_max
Paris2025-07-0124
Paris2025-07-0227
Lyon2025-07-0229
Lyon2025-07-0331
Marseille2025-07-0232
Marseille2025-07-0333

Exercice 10 · niveau 5

Affiche le nom, la catégorie et le prix des produits plus chers que la moyenne des produits de leur catégorie.

Table products (7 lignes)
idnamecategoryprice
1Desk LampHome35
2Coffee MugKitchen12
3NotebookOffice6
4Office ChairOffice149
5KettleKitchen45
6CushionHome22
7StaplerOffice9
Voir l’indice

Notions à utiliser : WHERE (filtres), COUNT, SUM, AVG, MIN, MAX, Sous-requêtes.

Structure de la requête :

SELECT , , 
FROM  
WHERE  > (SELECT AVG() FROM   WHERE  = )
Voir la correction
SELECT name, category, price
FROM products p
WHERE price > (SELECT AVG(x.price) FROM products x WHERE x.category = p.category);

Résultat attendu (3 lignes) :

namecategoryprice
Desk LampHome35
Office ChairOffice149
KettleKitchen45

Exercice 11 · niveau 5

Affiche le nom, l'équipe et le taux des développeurs dont le taux dépasse la moyenne de leur équipe.

Table devs (6 lignes)
idnameteamrate
1AnaWeb55
2BoWeb48
3CleoData62
4DanData58
5EveOps50
6FinnOps45
Voir l’indice

Notions à utiliser : WHERE (filtres), COUNT, SUM, AVG, MIN, MAX, Sous-requêtes.

Structure de la requête :

SELECT , , 
FROM  
WHERE  > (SELECT AVG() FROM   WHERE  = )
Voir la correction
SELECT name, team, rate
FROM devs d
WHERE rate > (SELECT AVG(x.rate) FROM devs x WHERE x.team = d.team);

Résultat attendu (3 lignes) :

nameteamrate
AnaWeb55
CleoData62
EveOps50

Exercice 12 · niveau 5

Affiche le numéro, le compte et le montant des opérations dont le montant dépasse la moyenne des opérations de leur compte.

Table transactions (14 lignes)
idaccount_idmade_onamountlabel
112025-01-022500salary
212025-01-05-60groceries
312025-01-12-800rent
422025-01-15500transfer
532025-01-031900salary
632025-01-20-120groceries
732025-02-01-950rent
842025-01-252100salary
942025-02-03-45restaurant
1012025-02-022600salary
1112025-02-06-75groceries
1252025-02-1030interest
1322025-02-15500transfer
1442025-02-18-600rent
Voir l’indice

Notions à utiliser : WHERE (filtres), COUNT, SUM, AVG, MIN, MAX, Sous-requêtes.

Structure de la requête :

SELECT , , 
FROM  
WHERE  > (SELECT AVG() FROM   WHERE  = )
Voir la correction
SELECT id, account_id, amount
FROM transactions t
WHERE amount > (SELECT AVG(x.amount) FROM transactions x WHERE x.account_id = t.account_id);

Résultat attendu (4 lignes) :

idaccount_idamount
112500
531900
842100
1012600

Exercice 13 · niveau 7

Pour chaque compte qui a des dépenses, affiche le numéro du compte, le titulaire, la date, le montant et le libellé de sa plus grosse dépense (en cas d'égalité, toutes les dépenses ex æquo).

Table accounts (6 lignes)
idownercityopenedkind
1AliceParis2021-03-01current
2AliceParis2022-06-15savings
3BrunoLyon2020-09-10current
4ChloeLyon2023-01-20current
5DavidNice2019-11-05savings
6EmmaNice2024-04-01current
Table transactions (14 lignes)
idaccount_idmade_onamountlabel
112025-01-022500salary
212025-01-05-60groceries
312025-01-12-800rent
422025-01-15500transfer
532025-01-031900salary
632025-01-20-120groceries
732025-02-01-950rent
842025-01-252100salary
942025-02-03-45restaurant
1012025-02-022600salary
1112025-02-06-75groceries
1252025-02-1030interest
1322025-02-15500transfer
1442025-02-18-600rent
Voir l’indice

Notions à utiliser : WHERE (filtres), COUNT, SUM, AVG, MIN, MAX, JOIN, Sous-requêtes.

Structure de la requête :

SELECT , , , , 
FROM  
JOIN   ON  = 
WHERE  <  AND  = (SELECT MIN() FROM   WHERE  = )
Voir la correction
SELECT a.id, a.owner, t.made_on, t.amount, t.label
FROM transactions t
JOIN accounts a ON a.id = t.account_id
WHERE t.amount < 0 AND t.amount = (SELECT MIN(x.amount) FROM transactions x WHERE x.account_id = t.account_id);

Résultat attendu (3 lignes) :

idownermade_onamountlabel
1Alice2025-01-12-800rent
3Bruno2025-02-01-950rent
4Chloe2025-02-18-600rent

Exercice 14 · niveau 8

Pour chaque réalisateur qui a au moins un film, affiche son nom, le titre de son film le plus long et la durée de ce film.

Table movies (10 lignes)
idtitlegenreyeardurationratingdirector_id
1Night TrainThriller20151187.81
2Blue HarborDrama20181027.12
3Paper Moon CityComedy2012956.45
4Silent PeakDrama20201318.23
5Last SignalSci-Fi20191427.51
6Summer KeysComedy2016885.94
7Iron GardenSci-Fi202112583
8Dust and GoldWestern20141106.82
9The Quiet HourDrama2022977.45
10Deep CurrentThriller20171056.9NULL
Table directors (6 lignes)
idnamecountry
1Nora EllisUK
2Paulo ReisBrazil
3Kenji MoriJapan
4Anna BergSweden
5Luc MartinFrance
6Sara DiazSpain
Voir l’indice

Notions à utiliser : WHERE (filtres), COUNT, SUM, AVG, MIN, MAX, JOIN, Sous-requêtes.

Structure de la requête :

SELECT , , 
FROM  
JOIN   ON  = 
WHERE  = (SELECT MAX() FROM   WHERE  = )
Voir la correction
SELECT d.name, m.title, m.duration
FROM directors d
JOIN movies m ON m.director_id = d.id
WHERE m.duration = (SELECT MAX(duration) FROM movies x WHERE x.director_id = d.id);

Résultat attendu (5 lignes) :

nametitleduration
Nora EllisLast Signal142
Paulo ReisDust and Gold110
Kenji MoriSilent Peak131
Anna BergSummer Keys88
Luc MartinThe Quiet Hour97

Exercice 15 · niveau 8

Affiche le nom du joueur, le nom de son équipe et son nombre de buts, pour les joueurs qui ont marqué plus de buts que chacun des joueurs des Blue Owls, du plus grand nombre de buts au plus petit.

Table players (9 lignes)
idnameteam_idpositiongoals
1Alex Moreau1FW9
2Bilal Sow1MF4
3Carl Weber2FW7
4Diego Ruiz2DF1
5Eli Novak3FW6
6Femi Adeyemi4FW11
7Goran Petrov4GK0
8Hugo Lamy5MF5
9Ivan Kral5DF2
Table teams (5 lignes)
idnamecityfounded
1Red FoxesLyon1950
2Blue OwlsParis1962
3Green BullsLille1971
4Gold HawksNantes1988
5Grey WolvesParis1990
Voir l’indice

Notions à utiliser : WHERE (filtres), ORDER BY, LIMIT, COUNT, SUM, AVG, MIN, MAX, JOIN, Sous-requêtes.

Structure de la requête :

SELECT , , 
FROM  
JOIN   ON  = 
WHERE  > (SELECT MAX() FROM   JOIN   ON  =  WHERE  = )
ORDER BY  DESC
Voir la correction
SELECT p.name, t.name, p.goals
FROM players p
JOIN teams t ON t.id = p.team_id
WHERE p.goals > (SELECT MAX(x.goals) FROM players x JOIN teams y ON y.id = x.team_id WHERE y.name = 'Blue Owls')
ORDER BY p.goals DESC;

Résultat attendu (2 lignes, dans cet ordre) :

namenamegoals
Femi AdeyemiGold Hawks11
Alex MoreauRed Foxes9

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

S’entraîner sur les 34 questions « Sous-requêtes »