Exercices corrigés
Objectifs du Chapitre
Construire un MCD puis un MLD à partir d'un énoncé en langage courant.
Traduire une relation N-M en table de liaison, et placer les attributs de relation au bon endroit.
Écrire les requêtes de création, d'insertion et d'interrogation correspondantes.
Passer d'une requête SQL à une expression d'algèbre relationnelle, et inversement.
Lire et construire un arbre algébrique, en poussant les sélections au plus près des feuilles.
Traiter les requêtes imbriquées : sous-requête scalaire, IN, EXISTS, et le piège de NOT IN avec des NULL.
Le module en conditions réelles. Les exercices reprennent, dans l'ordre, tout ce que les chapitres précédents ont introduit : modélisation, SQL, algèbre relationnelle, arbres, requêtes imbriquées. Chaque corrigé est replié : l'ouvrir avant d'avoir cherché ne t'apprend rien, et l'épreuve, elle, ne se replie pas.
- Lisez l'énoncé et essayez de répondre par vous-même avant d'ouvrir la correction.
- Les corrections sont progressives : commencez par identifier les entités et relations, puis passez au SQL.
- Si vous bloquez, relisez les chapitres correspondants avant de consulter la correction.
Partie 1, Modélisation : MCD et MLD
Ces exercices couvrent la modélisation conceptuelle (MCD) et logique (MLD). Pour chaque énoncé, identifiez d'abord les entités, puis les relations entre elles et leur cardinalité.
Exercice 1, Bibliothèque
Une bibliothèque gère des livres, des auteurs et des emprunts.
- Un auteur peut avoir écrit plusieurs livres, et un livre a un seul auteur principal.
- Un lecteur peut emprunter plusieurs livres, et un livre peut être emprunté par plusieurs lecteurs (à des dates différentes).
- Pour chaque emprunt, on enregistre la date d'emprunt et la date de retour prévue.
- Identifiez les entités et leurs attributs.
- Identifiez les relations et leurs cardinalités.
- Proposez un MCD (notation textuelle).
- Déduisez le MLD correspondant.
Afficher la correction
Entités et attributs :
Auteur: idAuteur, nom, prenomLivre: idLivre, titre, genre, anneeLecteur: idLecteur, nom, prenom, email
Relations :
Auteur-(1,1)- Ecrit -(0,N)-Livre→ un auteur écrit 0 à N livres, un livre a exactement 1 auteurLecteur-(0,N)- Emprunt -(0,N)-Livre→ relation N-M avec attribut (dateEmprunt, dateRetour)
MCD :
Rectangle : entité, identifiant souligné. Losange : relation. Les couples min,max portés sur les pattes se lisent depuis l'entité la plus proche.
MLD :
clé primaire clé étrangère, avec la table visée
Pourquoi Emprunt a une clé primaire composite ? Un même livre peut être emprunté plusieurs fois (par des lecteurs différents). La clé (idLivre, idLecteur) n'est pas suffisante si on veut autoriser plusieurs emprunts du même livre par le même lecteur. Dans ce cas, on ajouterait dateEmprunt à la clé primaire.
Exercice 2, Entreprise et projets
Une entreprise souhaite modéliser ses salariés, départements et projets.
- Chaque salarié appartient à un seul département.
- Un salarié peut travailler sur plusieurs projets.
- Un projet peut impliquer plusieurs salariés.
- On veut aussi connaître le rôle de chaque salarié sur un projet (ex : développeur, chef de projet).
- Proposez le MCD.
- Déduisez le MLD.
- Quelle est la nature de la relation Salarié-Projet ? Comment la traduit-on en MLD ?
Afficher la correction
MCD :
Rectangle : entité, identifiant souligné. Losange : relation. Les couples min,max portés sur les pattes se lisent depuis l'entité la plus proche.
MLD :
clé primaire clé étrangère, avec la table visée
Explication :
- La relation Département-Salarié est 1-N : elle se traduit par une clé étrangère
idDeptdans la tableSalarie. - La relation Salarié-Projet est N-M : elle génère une table de liaison
TravailleSur. - L'attribut
roleappartient à la relation (pas à une entité) : il va dans la table de liaison.
Exercice 3, Plateforme vidéo
Modélise une plateforme de vidéos en ligne avec les règles suivantes :
- Un utilisateur peut poster plusieurs vidéos.
- Un utilisateur peut commenter plusieurs vidéos.
- Chaque commentaire est lié à une vidéo et à un utilisateur.
- Une vidéo peut recevoir plusieurs commentaires.
- On veut aussi pouvoir enregistrer les likes (un utilisateur peut liker plusieurs vidéos).
- Proposez le MCD.
- Déduisez le MLD.
Afficher la correction
MCD :
[Utilisateur] --(1,N)-- <Poste> --(1,1)-- [Video]
| |
<Like> <Commente>
| |
(0,N) (1,N)
|
[Commentaire]
idCom
contenu
dateCom
MLD :
clé primaire clé étrangère, avec la table visée
Explication :
Videoporte la cléidUsercar un utilisateur poste une vidéo (relation 1-N).Commentaireporte deux clés étrangères : vers l'auteur du commentaire et vers la vidéo concernée.Likeest une table de liaison N-M entreUtilisateuretVideo.
Exercice 4, Hôtel
Un hôtel souhaite gérer ses chambres, ses clients et ses réservations.
- Un client peut effectuer plusieurs réservations.
- Une réservation porte sur une seule chambre.
- Une chambre a un type (simple, double, suite) et un tarif par nuit.
- On enregistre pour chaque réservation la date d'arrivée et la date de départ.
- Identifiez les entités et leurs attributs.
- Identifiez les relations et leurs cardinalités.
- Proposez le MLD.
Afficher la correction
Entités et attributs :
Client: idClient, nom, prenom, email, telephoneChambre: idChambre, numero, type, tarifNuitReservation: idReservation, dateArrivee, dateDepart
Relations :
Client-(1,N)- Effectue -(1,1)-Reservation→ un client peut avoir plusieurs réservations, une réservation appartient à un seul clientChambre-(1,N)- Concerne -(1,1)-Reservation→ une chambre peut être réservée plusieurs fois (à des dates différentes), une réservation porte sur une chambre
MLD :
clé primaire clé étrangère, avec la table visée
Pourquoi Reservation est une entité à part et non une table de liaison ? Parce que la relation Client-Chambre n'est pas directe : une réservation a ses propres attributs (dateArrivee, dateDepart, idReservation) et une identité propre. Elle se traduit par une entité, pas seulement une table de liaison.
Exercice 5, Réseau social
Un réseau social permet à des utilisateurs de se suivre mutuellement et de publier des posts.
- Un utilisateur peut suivre plusieurs autres utilisateurs (relation d'abonnement).
- Un utilisateur peut publier plusieurs posts.
- Un post peut recevoir des likes d'autres utilisateurs.
- On veut savoir qui suit qui et depuis quand.
- Identifiez les entités et relations (attention : la relation "suit" est réflexive).
- Proposez le MLD.
Afficher la correction
Entités :
Utilisateur: idUser, pseudo, email, dateInscriptionPost: idPost, contenu, datePublication
Relations :
- Suit :
Utilisateur-(0,N)- suit -(0,N)-Utilisateur→ relation réflexive N-M (un utilisateur peut en suivre plusieurs, et être suivi par plusieurs) - Publie :
Utilisateur-(1,N)- publie -(1,1)-Post→ relation 1-N - Like :
Utilisateur-(0,N)- like -(0,N)-Post→ relation N-M
MLD :
clé primaire clé étrangère, avec la table visée
Point clé, relation réflexive : La table Abonnement référence deux fois Utilisateur avec deux noms différents (idSuiveur = celui qui suit, idSuivi = celui qui est suivi). Les deux colonnes pointent vers la même table mais ont des rôles distincts.
Partie 2, SQL : Création, insertion, interrogation
Ces exercices s'appuient sur le schéma suivant, créé en exercice 6.
Schéma de référence :
clé primaire clé étrangère, avec la table visée
Données de test :
Etudiant : Durand Claire (id=1), Martin Lucas (id=2), Lefevre Sophie (id=3), Bernard Tom (id=4)
Cours : Mathematiques (id=1, 30h), Physique (id=2, 24h), Informatique (id=3, 36h)
Inscription : (1,1,14.5) (1,3,16.0) (2,1,11.0) (2,2,9.5) (3,2,13.0) (4,3,12.0)
Exercice 6, Mise en place du schéma
Créez les tables, insérez des données de test, puis affichez les étudiants inscrits au cours "Mathématiques".
Afficher la correction
-- Création des tables
CREATE TABLE Etudiant (
idEtudiant INT PRIMARY KEY AUTO_INCREMENT,
nom VARCHAR(50) NOT NULL,
prenom VARCHAR(50) NOT NULL,
dateNaissance DATE
);
CREATE TABLE Cours (
idCours INT PRIMARY KEY AUTO_INCREMENT,
intitule VARCHAR(100) NOT NULL,
volumeHoraire INT CHECK (volumeHoraire > 0)
);
CREATE TABLE Inscription (
idEtudiant INT,
idCours INT,
note DECIMAL(4,2),
PRIMARY KEY (idEtudiant, idCours),
FOREIGN KEY (idEtudiant) REFERENCES Etudiant(idEtudiant) ON DELETE CASCADE,
FOREIGN KEY (idCours) REFERENCES Cours(idCours) ON DELETE RESTRICT
);
-- Insertion des données
INSERT INTO Etudiant (nom, prenom, dateNaissance) VALUES
('Durand', 'Claire', '2001-03-25'),
('Martin', 'Lucas', '2000-07-14'),
('Lefevre', 'Sophie', '2002-11-02'),
('Bernard', 'Tom', '2001-09-10');
INSERT INTO Cours (intitule, volumeHoraire) VALUES
('Mathematiques', 30),
('Physique', 24),
('Informatique', 36);
INSERT INTO Inscription VALUES
(1, 1, 14.5),
(1, 3, 16.0),
(2, 1, 11.0),
(2, 2, 9.5),
(3, 2, 13.0),
(4, 3, 12.0);
-- Etudiants inscrits à "Mathematiques"
SELECT e.nom, e.prenom
FROM Etudiant e
JOIN Inscription i ON e.idEtudiant = i.idEtudiant
JOIN Cours c ON i.idCours = c.idCours
WHERE c.intitule = 'Mathematiques';
Résultat attendu :
| nom | prenom |
|---|---|
| Durand | Claire |
| Martin | Lucas |
Explication : On crée les tables avec leurs contraintes (ON DELETE CASCADE supprime les inscriptions si un étudiant est supprimé). Deux jointures successives permettent de relier Etudiant → Inscription → Cours, puis on filtre sur l'intitulé.
Exercice 7, Filtrage sur les notes
Affichez le nom, le prénom et la note des étudiants ayant obtenu une note supérieure ou égale à 12, triés par note décroissante.
Afficher la correction
Résultat attendu :
| nom | prenom | note |
|---|---|---|
| Durand | Claire | 16.00 |
| Durand | Claire | 14.50 |
| Lefevre | Sophie | 13.00 |
| Bernard | Tom | 12.00 |
Explication : Le WHERE filtre les lignes de la table Inscription avant le tri. Le ORDER BY ... DESC place les meilleures notes en premier.
Exercice 8, Comptage par cours
Affichez le nombre d'étudiants inscrits par cours, avec l'intitulé du cours, trié par nombre d'inscrits décroissant.
Afficher la correction
Résultat attendu :
| intitule | nbEtudiants |
|---|---|
| Mathematiques | 2 |
| Physique | 2 |
| Informatique | 2 |
Explication : Le GROUP BY c.intitule regroupe les inscriptions par cours. COUNT(*) compte les lignes de chaque groupe. Sans JOIN, on n'aurait pas accès à intitule.
Exercice 9, Agrégats et filtrage de groupes
Affichez l'intitulé et la moyenne des notes de chaque cours, mais uniquement pour les cours ayant au moins 2 inscrits et une moyenne supérieure à 11.
Afficher la correction
Résultat attendu :
| intitule | nbInscrits | moyenneNotes |
|---|---|---|
| Informatique | 2 | 14.00 |
| Mathematiques | 2 | 12.75 |
Explication :
WHEREne peut pas filtrer surAVG()car les agrégats ne sont pas encore calculés à ce stade.HAVINGs'applique après leGROUP BYet peut utiliser des fonctions d'agrégation.- Physique est exclu : sa moyenne est
(9.5 + 13.0) / 2 = 11.25, soit inférieure à 12 mais supérieure à 11, vérifiez avec vos données exactes.
Exercice 10, Cours sans inscrits
Affichez les cours auxquels aucun étudiant n'est inscrit.
Afficher la correction
Solution 1, avec NOT IN :
Solution 2, avec LEFT JOIN (recommandée) :
Explication : La LEFT JOIN conserve tous les cours, même ceux sans inscription. Pour ces derniers, i.idCours vaut NULL. Filtrer sur IS NULL isole les cours sans inscrits.
Cette approche est préférable à NOT IN car elle gère correctement les NULL et est généralement plus performante sur de grandes tables.
Exercice 11, UPDATE et DELETE
À partir du schéma de l'exercice 6 :
- L'étudiant "Lefevre Sophie" a changé de nom, elle s'appelle désormais "Lefebvre". Mettez à jour la table.
- L'étudiant "Bernard Tom" se désinscrit de la plateforme. Supprimez-le ainsi que toutes ses inscriptions.
- Pourquoi n'est-il pas nécessaire de supprimer manuellement ses inscriptions dans ce cas ?
Afficher la correction
Points importants :
- Le
WHEREdansUPDATEest indispensable, sans lui, tous les étudiants seraient renommés. ON DELETE CASCADEsur la clé étrangèreidEtudiantdansInscriptiongarantit que les inscriptions orphelines sont supprimées automatiquement lorsqu'un étudiant est supprimé.- Préférez toujours filtrer sur la clé primaire (
idEtudiant) en production pour éviter les ambiguïtés sur les noms en double.
Exercice 12, ALTER TABLE
Après avoir créé le schéma de l'exercice 6 :
- Ajoutez une colonne
mentionde typeVARCHAR(20)à la tableInscription, avec la valeur par défaut'Passable'. - Ajoutez une contrainte
UNIQUEsur la colonneemailde la tableEtudiant(en supposant qu'on ait ajouté cette colonne). - Supprimez la colonne
dateNaissancede la tableEtudiant.
Afficher la correction
-- 1. Ajout de la colonne mention dans Inscription
ALTER TABLE Inscription
ADD COLUMN mention VARCHAR(20) DEFAULT 'Passable';
-- 2. Ajout de la contrainte UNIQUE sur email
-- (on suppose que la colonne email existe déjà)
ALTER TABLE Etudiant
ADD CONSTRAINT uq_email UNIQUE (email);
-- 3. Suppression de la colonne dateNaissance
ALTER TABLE Etudiant
DROP COLUMN dateNaissance;
Points importants :
ADD COLUMNajoute une colonne avec ses contraintes (iciDEFAULT).ADD CONSTRAINTnomme explicitement la contrainte pour pouvoir la supprimer plus tard avecDROP CONSTRAINT uq_email.DROP COLUMNest irréversible, les données de cette colonne sont perdues. Toujours sauvegarder avant.
Exercice 13, LIKE, BETWEEN et IS NULL
À partir du schéma de référence :
- Affichez les étudiants dont le nom commence par "D" ou "M".
- Affichez les cours dont le volume horaire est compris entre 25 et 40 heures.
- Affichez les inscriptions dont la note est
NULL(non encore saisie).
Afficher la correction
Points importants :
LIKE 'D%': le%remplace n'importe quelle suite de caractères._remplace un seul caractère.BETWEEN 25 AND 40est inclusif : équivalent à>= 25 AND <= 40.note = NULLne fonctionne pas, il faut toujours utiliserIS NULLouIS NOT NULLpour les valeurs nulles.
Partie 3, Algèbre relationnelle
Exprimez les requêtes suivantes en utilisant les opérateurs σ (sélection), π (projection), ⨝ (jointure), ∪ (union), − (différence), ÷ (division).
Exercice 14, Projection + sélection
Affichez les noms et prénoms des étudiants inscrits au cours "Mathematiques".
Afficher la correction
π_{nom, prenom}(
σ_{intitule = 'Mathematiques'}(
Etudiant ⨝_{Etudiant.idEtudiant = Inscription.idEtudiant}
Inscription ⨝_{Inscription.idCours = Cours.idCours}
Cours
)
)
Lecture de l'expression (intérieur → extérieur) :
- Les trois tables sont jointes sur leurs clés communes.
- On filtre les lignes où
intitule = 'Mathematiques'. - On ne garde que les colonnes
nometprenom.
Traduction SQL :
Exercice 15, Sélection sur une table liée
Affichez les intitulés des cours suivis par l'étudiant dont le nom est "Durand".
Afficher la correction
π_{intitule}(
σ_{nom = 'Durand'}(
Etudiant ⨝ Inscription ⨝ Cours
)
)
Note : Si plusieurs étudiants ont le nom "Durand", tous leurs cours seront affichés. En pratique, on préférerait filtrer sur idEtudiant pour lever l'ambiguïté.
Traduction SQL :
Exercice 16, Sélection avec condition numérique
Affichez les noms et prénoms des étudiants ayant obtenu une note strictement inférieure à 10.
Afficher la correction
π_{nom, prenom}(
σ_{note < 10}(
Etudiant ⨝_{Etudiant.idEtudiant = Inscription.idEtudiant} Inscription
)
)
Ici, il n'est pas nécessaire de joindre Cours car on ne filtre pas sur un attribut du cours.
Traduction SQL :
Exercice 17, Division relationnelle
Affichez les noms et prénoms des étudiants inscrits à tous les cours disponibles.
Afficher la correction
π_{nom, prenom}(
Etudiant ⨝ (
π_{idEtudiant, idCours}(Inscription) ÷ π_{idCours}(Cours)
)
)
Explication de la division :
π_{idEtudiant, idCours}(Inscription) ÷ π_{idCours}(Cours) retourne les idEtudiant pour lesquels il existe une inscription pour chaque idCours de Cours.
Traduction SQL (avec NOT EXISTS) :
On cherche les étudiants pour lesquels il n'existe pas de cours auquel ils ne seraient pas inscrits.
Exercice 18, Union
Affichez les identifiants des étudiants inscrits soit au cours "Mathematiques" soit au cours "Physique" (ou les deux).
Afficher la correction
π_{idEtudiant}(σ_{intitule = 'Mathematiques'}(Inscription ⨝ Cours))
∪
π_{idEtudiant}(σ_{intitule = 'Physique'}(Inscription ⨝ Cours))
Propriété de l'union : L'union supprime les doublons. Un étudiant inscrit aux deux cours n'apparaîtra qu'une fois dans le résultat.
Traduction SQL :
Alternative avec IN :
Exercice 19, Différence
Affichez les identifiants des étudiants inscrits au cours "Mathematiques" mais pas au cours "Physique".
Afficher la correction
π_{idEtudiant}(σ_{intitule = 'Mathematiques'}(Inscription ⨝ Cours))
−
π_{idEtudiant}(σ_{intitule = 'Physique'}(Inscription ⨝ Cours))
Lecture : On prend l'ensemble des étudiants en Maths, et on lui soustrait l'ensemble des étudiants en Physique. Seuls ceux qui ne sont que en Maths restent.
Traduction SQL :
Alternative avec NOT IN (compatible MySQL qui ne supporte pas EXCEPT) :
Partie 4, Arbres algébriques
Représentez chaque requête SQL sous forme d'arbre algébrique. Rappel : les feuilles sont les tables, les nœuds internes sont les opérations, et la racine est le résultat final. On lit l'arbre de bas en haut.
Exercice 20, Arbre simple (sélection + projection)
Représentez la requête suivante sous forme d'arbre :
Afficher la correction
π_{nom, prenom}
|
σ_{dateNaissance < '2002-01-01'}
|
EtudiantSe lit de bas en haut : les feuilles sont les tables, chaque nœud une opération appliquée au résultat du niveau inférieur.
Lecture (bas → haut) :
Etudiant, on part de la table entière.σ_{dateNaissance < '2002-01-01'}, on filtre les étudiants nés avant 2002.π_{nom, prenom}, on ne garde que les colonnes nom et prénom.
Pourquoi ce sens ? La sélection avant la projection est obligatoire ici : si on projetait d'abord, la colonne dateNaissance disparaîtrait et la sélection serait impossible.
Exercice 21, Arbre avec LIKE
Représentez en arbre :
Afficher la correction
π_{nom}
|
σ_{nom LIKE 'M%'}
|
EtudiantSe lit de bas en haut : les feuilles sont les tables, chaque nœud une opération appliquée au résultat du niveau inférieur.
Note : LIKE 'M%' en algèbre relationnelle s'écrit avec la même notation de sélection. La condition reste une condition sur les attributs de la relation.
Exercice 22, Arbre avec BETWEEN
Représentez en arbre :
Afficher la correction
π_{intitule}
|
σ_{volumeHoraire >= 20 ∧ volumeHoraire <= 40}
|
CoursSe lit de bas en haut : les feuilles sont les tables, chaque nœud une opération appliquée au résultat du niveau inférieur.
Équivalence : BETWEEN 20 AND 40 est équivalent à >= 20 AND <= 40. En algèbre relationnelle, on utilise le symbole ∧ (et logique) pour combiner les conditions dans σ.
Exercice 23, Arbre avec jointure et optimisation
Représentez en arbre la requête suivante, puis proposez une version optimisée :
Afficher la correction
Arbre non optimisé :
π_{nom, intitule}
|
σ_{intitule = 'Informatique'}
|
⨝ (Inscription.idCours = Cours.idCours)
/ \
⨝ (Etudiant.idEtudiant = Inscription.idEtudiant) Cours
/ \
Etudiant InscriptionSe lit de bas en haut : les feuilles sont les tables, chaque nœud une opération appliquée au résultat du niveau inférieur.
Arbre optimisé (sélection poussée vers les feuilles) :
π_{nom, intitule}
|
⨝ (Inscription.idCours = Cours.idCours)
/ \
⨝ (Etudiant.idEtudiant = Inscription.idEtudiant) σ_{intitule = 'Informatique'}
/ \ |
Etudiant Inscription CoursSe lit de bas en haut : les feuilles sont les tables, chaque nœud une opération appliquée au résultat du niveau inférieur.
Pourquoi l'optimisation est meilleure ? En filtrant Cours sur intitule = 'Informatique' avant la jointure, on réduit la taille de la table Cours à 1 ligne. Les jointures suivantes portent sur un ensemble beaucoup plus petit, ce qui accélère l'exécution.
Exercice 24, Arbre avec agrégation
Représentez en arbre la requête suivante :
Afficher la correction
π_{intitule, nbInscrits}
|
σ_{COUNT(*) >= 2} <-- HAVING
|
γ_{intitule; COUNT(*)} <-- GROUP BY + agrégat
|
⨝ (Cours.idCours = Inscription.idCours)
/ \
Cours InscriptionSe lit de bas en haut : les feuilles sont les tables, chaque nœud une opération appliquée au résultat du niveau inférieur.
Notation : On utilise γ (gamma) pour représenter l'opération d'agrégation (GROUP BY + fonction d'agrégation). La syntaxe est γ_{colonnes de groupe ; agrégats}.
Ordre de lecture (bas → haut) :
- Jointure
Cours×Inscriptionsur les clés communes. - Agrégation par
intituleavec calcul duCOUNT(*). - Filtrage des groupes (
HAVING) : on ne garde que ceux avec au moins 2 inscrits. - Projection sur les colonnes utiles.
Partie 5, Requêtes complexes et imbriquées
Exercice 25, Jointures multiples
Affichez les noms et prénoms des employés travaillant sur un projet d'un client situé à Paris.
Schéma :
clé primaire clé étrangère, avec la table visée
Afficher la correction
Explication : On remonte la chaîne de jointures depuis l'employé jusqu'au client via la table de liaison TravailleSur. Le DISTINCT évite les doublons : un employé peut travailler sur plusieurs projets parisiens mais ne doit apparaître qu'une fois.
Alternative avec sous-requête :
Exercice 26, Sous-requêtes imbriquées
Affichez les intitulés des cours suivis par au moins un étudiant né après le 31 décembre 2001.
Afficher la correction
Lecture de l'intérieur vers l'extérieur :
- La sous-requête la plus interne sélectionne les
idEtudiantdes étudiants nés après 2001. - La sous-requête intermédiaire trouve les
idCoursauxquels ces étudiants sont inscrits. - La requête externe récupère les intitulés de ces cours.
Attention : Cette requête retourne les cours qui ont AU MOINS UN inscrit né après 2001. Pour retourner les cours suivis UNIQUEMENT par des étudiants nés après 2001 (sans aucun étudiant né avant), la requête serait plus complexe.
Exercice 27, Double négation (tous les cours)
Affichez les noms et prénoms des étudiants inscrits à tous les cours disponibles.
Afficher la correction
Solution avec NOT EXISTS (portable, gère les NULL) :
Solution avec EXCEPT (PostgreSQL, SQL Server) :
Logique de la double négation :
"Un étudiant est inscrit à tous les cours" équivaut à : "Il n'existe aucun cours auquel cet étudiant n'est pas inscrit"
Cette formulation en double négation (NOT EXISTS + cours NOT IN inscriptions) est la traduction directe de la division relationnelle en SQL.
Exercice 28, Analyse statistique
À partir du schéma Etudiant/Cours/Inscription, répondez aux questions suivantes en une seule requête pour chacune :
- Quelle est la note maximale par cours, avec le nom du cours ?
- Quels sont les cours dont la moyenne est inférieure à la moyenne générale de toutes les notes ?
- Quel étudiant a la meilleure note parmi tous les inscrits ? (Affichez son nom, prénom et note.)
Afficher la correction
1. Note maximale par cours :
2. Cours dont la moyenne est inférieure à la moyenne générale :
La sous-requête (SELECT AVG(note) FROM Inscription) calcule la moyenne sur toutes les inscriptions. Le HAVING compare chaque groupe à cette valeur globale.
3. Étudiant avec la meilleure note :
Si plusieurs étudiants ont la même note maximale, tous seront affichés.
Exercice 29, Sous-requête corrélée
Pour chaque cours, affichez l'étudiant ayant obtenu la meilleure note dans ce cours (nom, prénom, intitulé du cours, note).
Afficher la correction
Pourquoi c'est une sous-requête corrélée ? La sous-requête SELECT MAX(i2.note) ... WHERE i2.idCours = i.idCours fait référence à i.idCours de la requête externe. Elle est réévaluée pour chaque ligne de la requête principale. C'est plus lent qu'une sous-requête simple, mais nécessaire ici.
Résultat attendu :
| nom | prenom | intitule | note |
|---|---|---|---|
| Durand | Claire | Informatique | 16.00 |
| Durand | Claire | Mathematiques | 14.50 |
| Lefevre | Sophie | Physique | 13.00 |
Alternative avec WITH (CTE) pour la lisibilité :
Exercice 30, EXISTS vs IN
À partir du schéma de référence, affichez les étudiants qui ont au moins une inscription avec une note inférieure à 10. Comparez les approches avec IN, EXISTS et jointure.
Afficher la correction
Solution 1, avec IN :
Solution 2, avec EXISTS :
Solution 3, avec JOIN + DISTINCT :
Comparaison des trois approches :
| Approche | Avantages | Inconvénients |
|---|---|---|
IN | Lisible, intuitif | Peut être lent si la sous-requête retourne beaucoup de lignes ; problèmes avec NULL |
EXISTS | Optimisé (s'arrête au 1er résultat) | Syntaxe plus verbeuse |
JOIN | Performant, flexible | Nécessite DISTINCT pour éviter les doublons |
Règle pratique : Préférez EXISTS quand vous voulez juste vérifier l'existence, JOIN quand vous avez besoin de colonnes de la table jointe.