Exercices SQL corrigés : EXISTS et NOT EXISTS

Exercices SQL corrigés : EXISTS et NOT EXISTS

Mis à jour le

EXISTS est vrai si la sous-requête renvoie au moins une ligne ; NOT EXISTS, si elle n’en renvoie aucune. Ces 15 exercices vont du niveau 5 au niveau 8 ; ils utilisent la syntaxe de SQLite, le moteur SQL de SpeedQL.

Conseil : NOT EXISTS est plus sûr que NOT IN quand la sous-requête peut contenir des NULL.

Relire la fiche « EXISTS » de l’aide-mémoire

Exercice 1 · niveau 5

Affiche le nom des employés qui ont au moins un subordonné direct. Utilise EXISTS.

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), EXISTS.

Structure de la requête :

SELECT 
FROM  
WHERE EXISTS (SELECT  FROM   WHERE  = )
Voir la correction
SELECT name
FROM staff m
WHERE EXISTS (SELECT 1 FROM staff e WHERE e.manager_id = m.id);

Résultat attendu (5 lignes) :

name
Alice
Bob
Claire
David
Emma

Exercice 2 · niveau 5

Affiche le nom des employés qui n'ont aucun subordonné. Utilise NOT EXISTS.

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), EXISTS.

Structure de la requête :

SELECT 
FROM  
WHERE NOT EXISTS (SELECT  FROM   WHERE  = )
Voir la correction
SELECT name
FROM staff m
WHERE NOT EXISTS (SELECT 1 FROM staff e WHERE e.manager_id = m.id);

Résultat attendu (5 lignes) :

name
Farid
Gaelle
Hugo
Iris
Jules

Exercice 3 · niveau 5

Affiche le nom des clients qui ont passé au moins une commande de plus de 100. Utilise EXISTS.

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), EXISTS.

Structure de la requête :

SELECT 
FROM  
WHERE EXISTS (SELECT  FROM   WHERE  =  AND  > )
Voir la correction
SELECT name
FROM customers c
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id AND o.amount > 100);

Résultat attendu (2 lignes) :

name
Alice
Bruno

Exercice 4 · niveau 5

Affiche le nom des acteurs qui n'ont joué dans aucun film. Utilise NOT EXISTS.

Table actors (7 lignes)
idnamebirth_year
1Ava Stone1985
2Ben Cole1978
3Chloe Park1990
4Dario Vega1982
5Emi Sato1995
6Felix Grant1970
7Gus Hale1988
Table casting (14 lignes)
movie_idactor_idrole
11lead
12support
23lead
26support
34lead
45lead
41support
52lead
55support
75lead
76support
86lead
93lead
94support
Voir l’indice

Notions à utiliser : WHERE (filtres), EXISTS.

Structure de la requête :

SELECT 
FROM  
WHERE NOT EXISTS (SELECT  FROM   WHERE  = )
Voir la correction
SELECT name
FROM actors a
WHERE NOT EXISTS (SELECT 1 FROM casting c WHERE c.actor_id = a.id);

Résultat attendu (1 ligne) :

name
Gus Hale

Exercice 5 · niveau 5

Affiche le titre des livres qui n'ont jamais été empruntés. Utilise NOT EXISTS.

Table books (10 lignes)
idtitleauthor_idgenrepagesyear
1Cold River1Novel3202011
2Salt Roads2Travel2102016
3The Glass Hive3Sci-Fi4122019
4Winter Ledger1Crime2882014
5Desert Letters2Novel3562020
6Small Engines4Sci-Fi1982022
7Harbor Lights5Novel4452008
8Night Garden3Crime3012017
9Paper Birds5Poetry962012
10Open Maps4Travel2402021
Table loans (12 lignes)
idbook_idmember_idloan_datereturn_date
1112025-01-052025-01-19
2322025-01-102025-02-02
3512025-02-012025-02-10
4732025-02-03NULL
5342025-02-152025-03-01
6222025-03-022025-03-30
7852025-03-05NULL
8132025-03-102025-03-18
9612025-03-202025-04-15
10352025-04-01NULL
11942025-04-052025-04-12
121022025-04-082025-04-20
Voir l’indice

Notions à utiliser : WHERE (filtres), EXISTS.

Structure de la requête :

SELECT 
FROM  
WHERE NOT EXISTS (SELECT  FROM   WHERE  = )
Voir la correction
SELECT title
FROM books b
WHERE NOT EXISTS (SELECT 1 FROM loans l WHERE l.book_id = b.id);

Résultat attendu (1 ligne) :

title
Winter Ledger

Exercice 6 · niveau 5

Affiche le nom des équipes qui ont joué au moins un match nul à domicile. Utilise EXISTS.

Table teams (5 lignes)
idnamecityfounded
1Red FoxesLyon1950
2Blue OwlsParis1962
3Green BullsLille1971
4Gold HawksNantes1988
5Grey WolvesParis1990
Table matches (10 lignes)
idplayed_onhome_idaway_idhome_goalsaway_goals
12025-08-021221
22025-08-033400
32025-08-095113
42025-08-102322
52025-08-164531
62025-08-171310
72025-08-232401
82025-08-243523
92025-08-304111
102025-08-315202
Voir l’indice

Notions à utiliser : WHERE (filtres), EXISTS.

Structure de la requête :

SELECT 
FROM  
WHERE EXISTS (SELECT  FROM   WHERE  =  AND  = )
Voir la correction
SELECT name
FROM teams t
WHERE EXISTS (SELECT 1 FROM matches m WHERE m.home_id = t.id AND m.home_goals = m.away_goals);

Résultat attendu (3 lignes) :

name
Blue Owls
Green Bulls
Gold Hawks

Exercice 7 · niveau 5

Affiche le code et la ville des aéroports A d'où part au moins un vol vers un aéroport B, alors qu'un autre vol relie B à A. Utilise EXISTS.

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), EXISTS.

Structure de la requête :

SELECT , 
FROM  
WHERE EXISTS (SELECT  FROM   WHERE  =  AND EXISTS (SELECT  FROM   WHERE  =  AND  = ))
Voir la correction
SELECT code, city
FROM airports a
WHERE EXISTS (SELECT 1 FROM flights f WHERE f.origin = a.code AND EXISTS (SELECT 1 FROM flights g WHERE g.origin = f.dest AND g.dest = f.origin));

Résultat attendu (6 lignes) :

codecity
CDGParis
LYSLyon
MADMadrid
LISLisbon
FCORome
BERBerlin

Exercice 8 · niveau 5

Avec NOT EXISTS, affiche le numéro, le type et le prix des chambres qui n'ont jamais été réservées.

Table rooms (10 lignes)
idhotel_idtypeprice
11single70
21double95
32double130
42suite210
53single110
63double150
74double65
85double260
95suite480
105single190
Table bookings (11 lignes)
idroom_idguest_idcheck_incheck_out
1212025-07-012025-07-04
2522025-07-022025-07-05
3832025-07-032025-07-06
4342025-07-052025-07-12
5652025-07-062025-07-08
6122025-07-082025-07-10
7932025-07-102025-07-11
8752025-07-112025-07-15
9412025-07-142025-07-16
10642025-07-152025-07-19
11652025-07-162025-07-18
Voir l’indice

Notions à utiliser : WHERE (filtres), EXISTS.

Structure de la requête :

SELECT , , 
FROM  
WHERE NOT EXISTS (SELECT  FROM   WHERE  = )
Voir la correction
SELECT r.id, r.type, r.price
FROM rooms r
WHERE NOT EXISTS (SELECT 1 FROM bookings b WHERE b.room_id = r.id);

Résultat attendu (1 ligne) :

idtypeprice
10single190

Exercice 9 · niveau 5

Avec EXISTS, affiche le nom et la ville des clients qui ont au moins une commande annulée ('cancelled').

Table customers (6 lignes)
idnamecitysignup
1AlbaParis2024-01-10
2BorisLyon2024-02-15
3CarlaParis2024-03-01
4DenisNantes2024-05-20
5EvaLyon2024-06-30
6FabioLille2024-08-08
Table orders (8 lignes)
idcustomer_idorder_datestatus
112025-01-05shipped
222025-01-12shipped
312025-02-03paid
432025-02-10cancelled
542025-02-20shipped
652025-03-02shipped
722025-03-15paid
832025-03-28shipped
Voir l’indice

Notions à utiliser : WHERE (filtres), EXISTS.

Structure de la requête :

SELECT , 
FROM  
WHERE EXISTS (SELECT  FROM   WHERE  =  AND  = )
Voir la correction
SELECT c.name, c.city
FROM customers c
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id AND o.status = 'cancelled');

Résultat attendu (1 ligne) :

namecity
CarlaParis

Exercice 10 · niveau 5

Avec NOT EXISTS, affiche le nom et l'équipe des développeurs qui n'ont terminé aucune tâche.

Table devs (6 lignes)
idnameteamrate
1AnaWeb55
2BoWeb48
3CleoData62
4DanData58
5EveOps50
6FinnOps45
Table tasks (10 lignes)
idproject_iddev_idtitlehoursstatusdone_on
111Landing page12done2025-03-10
213Data model20done2025-03-20
312Login8doingNULL
424ETL job16done2025-04-02
525CI pipeline6done2025-03-15
631Dashboard14todoNULL
733Report10done2025-04-25
842API18doingNULL
945Monitoring9todoNULL
1044Forecast22done2025-05-05
Voir l’indice

Notions à utiliser : WHERE (filtres), EXISTS.

Structure de la requête :

SELECT , 
FROM  
WHERE NOT EXISTS (SELECT  FROM   WHERE  =  AND  = )
Voir la correction
SELECT d.name, d.team
FROM devs d
WHERE NOT EXISTS (SELECT 1 FROM tasks t WHERE t.dev_id = d.id AND t.status = 'done');

Résultat attendu (2 lignes) :

nameteam
BoWeb
FinnOps

Exercice 11 · niveau 5

Avec NOT EXISTS, affiche le numéro, le titulaire et le type des comptes qui n'ont jamais payé de loyer (label 'rent').

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), EXISTS.

Structure de la requête :

SELECT , , 
FROM  
WHERE NOT EXISTS (SELECT  FROM   WHERE  =  AND  = )
Voir la correction
SELECT a.id, a.owner, a.kind
FROM accounts a
WHERE NOT EXISTS (SELECT 1 FROM transactions t WHERE t.account_id = a.id AND t.label = 'rent');

Résultat attendu (3 lignes) :

idownerkind
2Alicesavings
5Davidsavings
6Emmacurrent

Exercice 12 · niveau 8

Affiche le nom des auteurs dont tous les livres ont été empruntés au moins une fois (parmi les auteurs qui ont au moins un livre).

Table authors (6 lignes)
idnamecountrybirth_year
1Maya LindSweden1968
2Omar HaddadMorocco1975
3Julia BrandtGermany1981
4Tom ReyesMexico1990
5Ines DupuisFrance1959
6Ravi MenonIndia1984
Table books (10 lignes)
idtitleauthor_idgenrepagesyear
1Cold River1Novel3202011
2Salt Roads2Travel2102016
3The Glass Hive3Sci-Fi4122019
4Winter Ledger1Crime2882014
5Desert Letters2Novel3562020
6Small Engines4Sci-Fi1982022
7Harbor Lights5Novel4452008
8Night Garden3Crime3012017
9Paper Birds5Poetry962012
10Open Maps4Travel2402021
Table loans (12 lignes)
idbook_idmember_idloan_datereturn_date
1112025-01-052025-01-19
2322025-01-102025-02-02
3512025-02-012025-02-10
4732025-02-03NULL
5342025-02-152025-03-01
6222025-03-022025-03-30
7852025-03-05NULL
8132025-03-102025-03-18
9612025-03-202025-04-15
10352025-04-01NULL
11942025-04-052025-04-12
121022025-04-082025-04-20
Voir l’indice

Notions à utiliser : WHERE (filtres), EXISTS.

Structure de la requête :

SELECT 
FROM  
WHERE EXISTS (SELECT  FROM   WHERE  = ) AND NOT EXISTS (SELECT  FROM   WHERE  =  AND NOT EXISTS (SELECT  FROM   WHERE  = ))
Voir la correction
SELECT name
FROM authors a
WHERE EXISTS (SELECT 1 FROM books b WHERE b.author_id = a.id) AND NOT EXISTS (SELECT 1 FROM books b WHERE b.author_id = a.id AND NOT EXISTS (SELECT 1 FROM loans l WHERE l.book_id = b.id));

Résultat attendu (4 lignes) :

name
Omar Haddad
Julia Brandt
Tom Reyes
Ines Dupuis

Exercice 13 · niveau 8

Affiche le nom des hôtels qui ont des chambres et dont toutes les chambres ont été réservées au moins une fois.

Table hotels (5 lignes)
idnamecitystars
1Seaside InnNice3
2Alpine LodgeAnnecy4
3City LoftParis4
4Old MillBordeaux2
5Grand PalaceParis5
Table rooms (10 lignes)
idhotel_idtypeprice
11single70
21double95
32double130
42suite210
53single110
63double150
74double65
85double260
95suite480
105single190
Table bookings (11 lignes)
idroom_idguest_idcheck_incheck_out
1212025-07-012025-07-04
2522025-07-022025-07-05
3832025-07-032025-07-06
4342025-07-052025-07-12
5652025-07-062025-07-08
6122025-07-082025-07-10
7932025-07-102025-07-11
8752025-07-112025-07-15
9412025-07-142025-07-16
10642025-07-152025-07-19
11652025-07-162025-07-18
Voir l’indice

Notions à utiliser : WHERE (filtres), EXISTS.

Structure de la requête :

SELECT 
FROM  
WHERE EXISTS (SELECT  FROM   WHERE  = ) AND NOT EXISTS (SELECT  FROM   WHERE  =  AND NOT EXISTS (SELECT  FROM   WHERE  = ))
Voir la correction
SELECT h.name
FROM hotels h
WHERE EXISTS (SELECT 1 FROM rooms r WHERE r.hotel_id = h.id) AND NOT EXISTS (SELECT 1 FROM rooms r WHERE r.hotel_id = h.id AND NOT EXISTS (SELECT 1 FROM bookings b WHERE b.room_id = r.id));

Résultat attendu (4 lignes) :

name
Seaside Inn
Alpine Lodge
City Loft
Old Mill

Exercice 14 · niveau 8

Affiche le nom des clients qui ont commandé tous les produits de la catégorie Kitchen (au moins une fois chacun, commandes annulées comprises).

Table customers (6 lignes)
idnamecitysignup
1AlbaParis2024-01-10
2BorisLyon2024-02-15
3CarlaParis2024-03-01
4DenisNantes2024-05-20
5EvaLyon2024-06-30
6FabioLille2024-08-08
Table orders (8 lignes)
idcustomer_idorder_datestatus
112025-01-05shipped
222025-01-12shipped
312025-02-03paid
432025-02-10cancelled
542025-02-20shipped
652025-03-02shipped
722025-03-15paid
832025-03-28shipped
Table order_items (14 lignes)
order_idproduct_idqty
111
122
241
335
321
451
562
511
624
633
741
761
852
821
Table products (7 lignes)
idnamecategoryprice
1Desk LampHome35
2Coffee MugKitchen12
3NotebookOffice6
4Office ChairOffice149
5KettleKitchen45
6CushionHome22
7StaplerOffice9
Voir l’indice

Notions à utiliser : WHERE (filtres), JOIN, EXISTS.

Structure de la requête :

SELECT 
FROM  
WHERE NOT EXISTS (SELECT  FROM   WHERE  =  AND NOT EXISTS (SELECT  FROM   JOIN   ON  =  WHERE  =  AND  = ))
Voir la correction
SELECT c.name
FROM customers c
WHERE NOT EXISTS (SELECT 1 FROM products p WHERE p.category = 'Kitchen' AND NOT EXISTS (SELECT 1 FROM orders o JOIN order_items oi ON oi.order_id = o.id WHERE o.customer_id = c.id AND oi.product_id = p.id));

Résultat attendu (1 ligne) :

name
Carla

Exercice 15 · niveau 8

Affiche le nom des développeurs qui ont au moins une tâche sur chacun des projets du client Acme.

Table devs (6 lignes)
idnameteamrate
1AnaWeb55
2BoWeb48
3CleoData62
4DanData58
5EveOps50
6FinnOps45
Table projects (4 lignes)
idnameclientbudgetdeadline
1AtlasAcme200002025-06-30
2BeaconBolt120002025-05-15
3CometAcme80002025-04-30
4DeltaCyan150002025-07-31
Table tasks (10 lignes)
idproject_iddev_idtitlehoursstatusdone_on
111Landing page12done2025-03-10
213Data model20done2025-03-20
312Login8doingNULL
424ETL job16done2025-04-02
525CI pipeline6done2025-03-15
631Dashboard14todoNULL
733Report10done2025-04-25
842API18doingNULL
945Monitoring9todoNULL
1044Forecast22done2025-05-05
Voir l’indice

Notions à utiliser : WHERE (filtres), EXISTS.

Structure de la requête :

SELECT 
FROM  
WHERE NOT EXISTS (SELECT  FROM   WHERE  =  AND NOT EXISTS (SELECT  FROM   WHERE  =  AND  = ))
Voir la correction
SELECT d.name
FROM devs d
WHERE NOT EXISTS (SELECT 1 FROM projects p WHERE p.client = 'Acme' AND NOT EXISTS (SELECT 1 FROM tasks t WHERE t.project_id = p.id AND t.dev_id = d.id));

Résultat attendu (2 lignes) :

name
Ana
Cleo

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 22 questions « EXISTS »