Aller au contenu principal
bddExercices corrigés

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.

Où on va

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.

Comment utiliser ce chapitre
  • 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.
  1. Identifiez les entités et leurs attributs.
  2. Identifiez les relations et leurs cardinalités.
  3. Proposez un MCD (notation textuelle).
  4. Déduisez le MLD correspondant.
Afficher la correction

Entités et attributs :

  • Auteur : idAuteur, nom, prenom
  • Livre : idLivre, titre, genre, annee
  • Lecteur : idLecteur, nom, prenom, email

Relations :

  • Auteur -(1,1)- Ecrit -(0,N)- Livre → un auteur écrit 0 à N livres, un livre a exactement 1 auteur
  • Lecteur -(0,N)- Emprunt -(0,N)- Livre → relation N-M avec attribut (dateEmprunt, dateRetour)

MCD :

Modèle conceptuel de données
Auteur
idAuteur
nom
prenom
1,N
Ecrit
1,1
Livre
idLivre
titre
genre
annee
0,N
Emprunt
dateEmprunt, dateRetour
0,N
Lecteur
idLecteur
nom
prenom
email

Rectangle : entité, identifiant souligné. Losange : relation. Les couples min,max portés sur les pattes se lisent depuis l'entité la plus proche.

MLD :

Modèle logique de données
Auteur
idAuteur
nom
prenom
Livre
idLivre
titre
genre
annee
idAuteurAuteur
Lecteur
idLecteur
nom
prenom
email
Emprunt
idLivreLivre
idLecteurLecteur
dateEmprunt
dateRetour

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).
  1. Proposez le MCD.
  2. Déduisez le MLD.
  3. Quelle est la nature de la relation Salarié-Projet ? Comment la traduit-on en MLD ?
Afficher la correction

MCD :

Modèle conceptuel de données
Departement
idDept
nomDept
localisation
1,N
Appartient
1,1
Salarie
idSalarie
nom
prenom
0,N
TravailleSur
role
0,N
Projet
idProjet
nomProjet

Rectangle : entité, identifiant souligné. Losange : relation. Les couples min,max portés sur les pattes se lisent depuis l'entité la plus proche.

MLD :

Modèle logique de données
Departement
idDept
nomDept
localisation
Salarie
idSalarie
nom
prenom
idDeptDepartement
Projet
idProjet
nomProjet
TravailleSur
idSalarieSalarie
idProjetProjet
role

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 idDept dans la table Salarie.
  • La relation Salarié-Projet est N-M : elle génère une table de liaison TravailleSur.
  • L'attribut role appartient à 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).
  1. Proposez le MCD.
  2. 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 :

Modèle logique de données
Utilisateur
idUser
nom
email
dateInscription
Video
idVideo
titre
datePublication
duree
idUserUtilisateur
Commentaire
idCom
contenu
dateCom
idUserUtilisateur
idVideoVideo
Like
idUserUtilisateur
idVideoVideo
dateLike

clé primaire clé étrangère, avec la table visée

Explication :

  • Video porte la clé idUser car un utilisateur poste une vidéo (relation 1-N).
  • Commentaire porte deux clés étrangères : vers l'auteur du commentaire et vers la vidéo concernée.
  • Like est une table de liaison N-M entre Utilisateur et Video.

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.
  1. Identifiez les entités et leurs attributs.
  2. Identifiez les relations et leurs cardinalités.
  3. Proposez le MLD.
Afficher la correction

Entités et attributs :

  • Client : idClient, nom, prenom, email, telephone
  • Chambre : idChambre, numero, type, tarifNuit
  • Reservation : idReservation, dateArrivee, dateDepart

Relations :

  • Client -(1,N)- Effectue -(1,1)- Reservation → un client peut avoir plusieurs réservations, une réservation appartient à un seul client
  • Chambre -(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 :

Modèle logique de données
Client
idClient
nom
prenom
email
telephone
Chambre
idChambre
numero
type
tarifNuit
Reservation
idReservation
dateArrivee
dateDepart
idClientClient
idChambreChambre

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.
  1. Identifiez les entités et relations (attention : la relation "suit" est réflexive).
  2. Proposez le MLD.
Afficher la correction

Entités :

  • Utilisateur : idUser, pseudo, email, dateInscription
  • Post : 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 :

Modèle logique de données
Utilisateur
idUser
pseudo
email
dateInscription
Post
idPost
contenu
datePublication
idUserUtilisateur
Abonnement
idSuiveurUtilisateur
idSuiviUtilisateur
dateAbonnement
LikePost
idUserUtilisateur
idPostPost

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 :

Modèle logique de données
Etudiant
idEtudiant
nom
prenom
dateNaissance
Cours
idCours
intitule
volumeHoraire
Inscription
idEtudiant
idCours
note

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 :

nomprenom
DurandClaire
MartinLucas

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 EtudiantInscriptionCours, 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
requete.sql
Résultat
>_ Prêt à exécuter…

Résultat attendu :

nomprenomnote
DurandClaire16.00
DurandClaire14.50
LefevreSophie13.00
BernardTom12.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
requete.sql
Résultat
>_ Prêt à exécuter…

Résultat attendu :

intitulenbEtudiants
Mathematiques2
Physique2
Informatique2

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
requete.sql
Résultat
>_ Prêt à exécuter…

Résultat attendu :

intitulenbInscritsmoyenneNotes
Informatique214.00
Mathematiques212.75

Explication :

  • WHERE ne peut pas filtrer sur AVG() car les agrégats ne sont pas encore calculés à ce stade.
  • HAVING s'applique après le GROUP BY et 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 :

requete.sql
Résultat
>_ Prêt à exécuter…

Solution 2, avec LEFT JOIN (recommandée) :

requete.sql
Résultat
>_ Prêt à exécuter…

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 :

  1. L'étudiant "Lefevre Sophie" a changé de nom, elle s'appelle désormais "Lefebvre". Mettez à jour la table.
  2. L'étudiant "Bernard Tom" se désinscrit de la plateforme. Supprimez-le ainsi que toutes ses inscriptions.
  3. Pourquoi n'est-il pas nécessaire de supprimer manuellement ses inscriptions dans ce cas ?
Afficher la correction
requete.sql
Résultat
>_ Prêt à exécuter…

Points importants :

  • Le WHERE dans UPDATE est indispensable, sans lui, tous les étudiants seraient renommés.
  • ON DELETE CASCADE sur la clé étrangère idEtudiant dans Inscription garantit 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 :

  1. Ajoutez une colonne mention de type VARCHAR(20) à la table Inscription, avec la valeur par défaut 'Passable'.
  2. Ajoutez une contrainte UNIQUE sur la colonne email de la table Etudiant (en supposant qu'on ait ajouté cette colonne).
  3. Supprimez la colonne dateNaissance de la table Etudiant.
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 COLUMN ajoute une colonne avec ses contraintes (ici DEFAULT).
  • ADD CONSTRAINT nomme explicitement la contrainte pour pouvoir la supprimer plus tard avec DROP CONSTRAINT uq_email.
  • DROP COLUMN est 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 :

  1. Affichez les étudiants dont le nom commence par "D" ou "M".
  2. Affichez les cours dont le volume horaire est compris entre 25 et 40 heures.
  3. Affichez les inscriptions dont la note est NULL (non encore saisie).
Afficher la correction
requete.sql
Résultat
>_ Prêt à exécuter…

Points importants :

  • LIKE 'D%' : le % remplace n'importe quelle suite de caractères. _ remplace un seul caractère.
  • BETWEEN 25 AND 40 est inclusif : équivalent à >= 25 AND <= 40.
  • note = NULL ne fonctionne pas, il faut toujours utiliser IS NULL ou IS NOT NULL pour 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) :

  1. Les trois tables sont jointes sur leurs clés communes.
  2. On filtre les lignes où intitule = 'Mathematiques'.
  3. On ne garde que les colonnes nom et prenom.

Traduction SQL :

requete.sql
Résultat
>_ Prêt à exécuter…

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 :

requete.sql
Résultat
>_ Prêt à exécuter…

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 :

requete.sql
Résultat
>_ Prêt à exécuter…

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) :

requete.sql
Résultat
>_ Prêt à exécuter…

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 :

requete.sql
Résultat
>_ Prêt à exécuter…

Alternative avec IN :

requete.sql
Résultat
>_ Prêt à exécuter…

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 :

requete.sql
Résultat
>_ Prêt à exécuter…

Alternative avec NOT IN (compatible MySQL qui ne supporte pas EXCEPT) :

requete.sql
Résultat
>_ Prêt à exécuter…

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 :

requete.sql
Résultat
>_ Prêt à exécuter…
Afficher la correction
Arbre algébrique
         π_{nom, prenom}
                |
    σ_{dateNaissance < '2002-01-01'}
                |
           Etudiant

Se 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) :

  1. Etudiant, on part de la table entière.
  2. σ_{dateNaissance < '2002-01-01'}, on filtre les étudiants nés avant 2002.
  3. π_{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 :

requete.sql
Résultat
>_ Prêt à exécuter…
Afficher la correction
Arbre algébrique
      π_{nom}
         |
  σ_{nom LIKE 'M%'}
         |
      Etudiant

Se 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 :

requete.sql
Résultat
>_ Prêt à exécuter…
Afficher la correction
Arbre algébrique
      π_{intitule}
            |
  σ_{volumeHoraire >= 20 ∧ volumeHoraire <= 40}
            |
          Cours

Se 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 :

requete.sql
Résultat
>_ Prêt à exécuter…
Afficher la correction

Arbre non optimisé :

Arbre algébrique
              π_{nom, intitule}
                     |
          σ_{intitule = 'Informatique'}
                     |
         ⨝ (Inscription.idCours = Cours.idCours)
                /              \
⨝ (Etudiant.idEtudiant = Inscription.idEtudiant)   Cours
           /         \
       Etudiant   Inscription

Se 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) :

Arbre algébrique
              π_{nom, intitule}
                     |
         ⨝ (Inscription.idCours = Cours.idCours)
                /                     \
⨝ (Etudiant.idEtudiant = Inscription.idEtudiant)   σ_{intitule = 'Informatique'}
           /         \                                           |
       Etudiant   Inscription                                  Cours

Se 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 :

requete.sql
Résultat
>_ Prêt à exécuter…
Afficher la correction
Arbre algébrique
              π_{intitule, nbInscrits}
                        |
              σ_{COUNT(*) >= 2}    <-- HAVING
                        |
              γ_{intitule; COUNT(*)}   <-- GROUP BY + agrégat
                        |
         ⨝ (Cours.idCours = Inscription.idCours)
                /               \
             Cours           Inscription

Se 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) :

  1. Jointure Cours × Inscription sur les clés communes.
  2. Agrégation par intitule avec calcul du COUNT(*).
  3. Filtrage des groupes (HAVING) : on ne garde que ceux avec au moins 2 inscrits.
  4. 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 :

Modèle logique de données
Client
idClient
nom
ville
Projet
idProjet
nomProjet
idClient
Employe
idEmploye
nom
prenom
TravailleSur
idEmploye
idProjet

clé primaire clé étrangère, avec la table visée

Afficher la correction
requete.sql
Résultat
>_ Prêt à exécuter…

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 :

requete.sql
Résultat
>_ Prêt à exécuter…

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
requete.sql
Résultat
>_ Prêt à exécuter…

Lecture de l'intérieur vers l'extérieur :

  1. La sous-requête la plus interne sélectionne les idEtudiant des étudiants nés après 2001.
  2. La sous-requête intermédiaire trouve les idCours auxquels ces étudiants sont inscrits.
  3. 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) :

requete.sql
Résultat
>_ Prêt à exécuter…

Solution avec EXCEPT (PostgreSQL, SQL Server) :

requete.sql
Résultat
>_ Prêt à exécuter…

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 :

  1. Quelle est la note maximale par cours, avec le nom du cours ?
  2. Quels sont les cours dont la moyenne est inférieure à la moyenne générale de toutes les notes ?
  3. 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 :

requete.sql
Résultat
>_ Prêt à exécuter…

2. Cours dont la moyenne est inférieure à la moyenne générale :

requete.sql
Résultat
>_ Prêt à exécuter…

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 :

requete.sql
Résultat
>_ Prêt à exécuter…

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
requete.sql
Résultat
>_ Prêt à exécuter…

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 :

nomprenomintitulenote
DurandClaireInformatique16.00
DurandClaireMathematiques14.50
LefevreSophiePhysique13.00

Alternative avec WITH (CTE) pour la lisibilité :

requete.sql
Résultat
>_ Prêt à exécuter…

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 :

requete.sql
Résultat
>_ Prêt à exécuter…

Solution 2, avec EXISTS :

requete.sql
Résultat
>_ Prêt à exécuter…

Solution 3, avec JOIN + DISTINCT :

requete.sql
Résultat
>_ Prêt à exécuter…

Comparaison des trois approches :

ApprocheAvantagesInconvénients
INLisible, intuitifPeut être lent si la sous-requête retourne beaucoup de lignes ; problèmes avec NULL
EXISTSOptimisé (s'arrête au 1er résultat)Syntaxe plus verbeuse
JOINPerformant, flexibleNé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.