Aller au contenu principal

L'algèbre relationnelle et ses arbres

Ce que ce chapitre apporte 5 points
  • Expliquer pourquoi le moteur a besoin d'une algèbre entre la requête SQL et son exécution.
  • Employer les opérations de base, et prévoir ce que chacune fait aux lignes et aux colonnes.
  • Composer plusieurs opérations et lire l'arbre qui en résulte.
  • Traduire une question en français ou une requête SQL en expression algébrique, et l'inverse.
  • Réécrire un arbre en un arbre équivalent moins coûteux, et chiffrer ce qu'il épargne.

Une école range ses inscriptions dans une base, une agence ses clients, ses projets et les heures de ses employés. La scolarité veut le nom des étudiants de la classe 1A ; la direction, les employés affectés à tous les projets d'un client. Une requête SQL dit ce qu'on veut, jamais comment l'obtenir : c'est le moteur qui choisit la marche à suivre, et il raisonne dans l'algèbre relationnelle. Ses opérations, sélection, projection, jointure, division, prennent des tables et rendent une table, et l'arbre algébrique les met en ordre. Ce chapitre part des tables construites au chapitre Du MCD au modèle relationnel. Il fait lire ces arbres nœud par nœud, puis écrire des expressions que la page vérifie. Savoir les lire, c'est comprendre pourquoi deux requêtes qui donnent le même résultat ne coûtent pas le même prix.

Pourquoi une algèbre

SQL est un langage déclaratif. Une requête décrit le résultat voulu et ne dit rien de la marche à suivre. Le moteur, lui, doit bien choisir une marche à suivre, et il en existe des dizaines pour une même requête. Il lui faut donc un langage intermédiaire : assez formel pour être manipulé mécaniquement, assez proche des données pour que chaque opération soit exécutable. C'est l'algèbre relationnelle.

Ce chapitre vient avant celui des requêtes SQL, et ses figures sont pourtant accompagnées de leur traduction en SQL. Elle se lit sans connaître le langage : SELECT nom FROM Employe WHERE salaire > 2000 se dit presque comme la phrase française, « le nom des employés dont le salaire dépasse 2000 ». Le chapitre sur les requêtes SQL enseigne chacun de ces mots-clés.

Cette algèbre repose sur une seule idée, et tout le reste en découle.

La propriété de clôture. Chaque opération prend une ou deux relations et rend une relation. Le résultat d'une opération est donc lui-même un opérande valide pour la suivante.

C'est ce qui permet d'emboîter les opérations sans limite, et c'est pourquoi une requête se dessine en arbre. Les feuilles sont les tables de la base, chaque nœud une opération, la racine le résultat final.

Un mot de vocabulaire, parce qu'il revient partout. Une relation est un ensemble de lignes ayant les mêmes attributs. Le mot ensemble n'est pas décoratif : en algèbre relationnelle, une relation ne contient pas deux fois la même ligne. SQL, lui, tolère les doublons, ce qui explique le mot-clé DISTINCT. Cette différence produit une surprise à la projection, on y revient plus bas.

La base de démonstration

Presque toutes les figures de ce chapitre travaillent sur la base employée par les chapitres SQL : la même table Employe répond σ_{salaire > 2000}(Employe) ici, et répondrait SELECT * FROM Employe WHERE salaire > 2000 dans un bloc SQL, avec les mêmes lignes. Quelques figures déclarent leurs propres petites tables, deux listes de noms ou six affectations à des projets, pour isoler une opération ; elles le disent.

Cliquer sur un nœud pour voir la relation qu'il produit.

EmployeEmploye
Employe5 lignes · 6 attributs
idEmployenomprenomsalairedate_embaucheservice
1GirardNadia24502019-04-01Etudes
2LemoinePaul19802021-09-15Support
3BarbierSofia31002016-01-10Etudes
4RocheTom22002022-03-07Support
5VidalLucie27502018-11-23Commerce
La relation Employe, telle qu'elle est stockée : cinq employés, six attributs. Le nœud se clique pour afficher la table. En SQL : SELECT * FROM Employe

La base compte neuf relations, en deux parties. Côté université : des classes, des étudiants, des enseignants, des cours, et les inscriptions des étudiants aux cours avec leur note. Côté entreprise : des clients, leurs projets, des employés, et les heures que chacun passe sur chaque projet.

RelationAttributsLignes
ClasseidClasse, nomClasse, annee3
EtudiantidEtudiant, nom, prenom, dateNaissance, email, ville, idClasse, actif10
EnseignantidEnseignant, nomEns, prenomEns, specialite3
CoursidCours, intitule, volumeHoraire, coefficient, idEnseignant7
InscriptionidEtudiant, idCours, note25
ClientidClient, nom, prenom, ville4
ProjetidProjet, libelle, idClient4
EmployeidEmploye, nom, prenom, salaire, date_embauche, service5
TravailleSuridEmploye, idProjet, heures7

La partie université prolonge l'exemple du chapitre Du MCD au modèle relationnel, avec une table Classe et quelques attributs de plus. Dans Enseignant, le nom et le prénom s'appellent nomEns et prenomEns : après une jointure avec Etudiant, deux colonnes nom ne se distingueraient plus. Deux cours n'ont pas d'enseignant et deux inscriptions n'ont pas encore de note : la valeur absente s'écrit NULL, et elle servira plus bas.

Afficher le contenu des neuf tables

Classe

idClassenomClasseannee
11A1
21B1
32A2

Etudiant

idEtudiantnomprenomdateNaissanceemailvilleidClasseactif
1MartinLea2004-03-12lea.martin@ecole.frParis11
2DialloAmine2003-11-02amine.diallo@ecole.frLyon11
3DupondSophie2004-07-25sophie.dupond@ecole.frParis11
4DurandTom2003-05-30tom.durand@ecole.frNantes20
5LefebvreInes2004-01-18ines.lefebvre@ecole.frLyon21
6BernardYanis2002-09-08NULLParis21
7MoreauChi2004-02-14chi.moreau@ecole.frParis31
8MarchandHugo2003-08-21NULLBordeaux31
9NguyenElsa2004-11-30elsa.nguyen@ecole.frLyon31
10PetitSofia2002-12-05sofia.petit@ecole.frNantes10

Enseignant

idEnseignantnomEnsprenomEnsspecialite
1DurandClaireBases de donnees
2FerrandMarcAlgorithmique
3AubertSophieStatistiques

Cours

idCoursintitulevolumeHorairecoefficientidEnseignant
1Bases de donnees302.01
2Algorithmique241.52
3Mathematiques362.53
4Physique272.0NULL
5Informatique302.01
6Statistiques181.03
7Reseaux211.5NULL

Inscription

idEtudiantidCoursnote
1115.5
1317.0
1513.0
218.5
2314.0
2411.5
3111.0
3216.5
3319.0
379.5
42NULL
447.0
5118.0
5613.5
5712.0
636.0
6410.0
7214.5
7515.0
8112.5
86NULL
9316.0
9417.5
959.0
10210.5

Client

idClientnomprenomville
1DurandPaulParis
2LeroySarahLyon
3MoreauKarimParis
4FontaineEvaBordeaux

Projet

idProjetlibelleidClient
1Portail client1
2Refonte ERP2
3Application mobile3
4Audit reseau4

Employe

idEmployenomprenomsalairedate_embaucheservice
1GirardNadia24502019-04-01Etudes
2LemoinePaul19802021-09-15Support
3BarbierSofia31002016-01-10Etudes
4RocheTom22002022-03-07Support
5VidalLucie27502018-11-23Commerce

TravailleSur

idEmployeidProjetheures
11120
1340
2180
32200
3360
4435
5290

Dans les figures, chaque feuille d'un arbre se clique aussi, et affiche sa table.

Les opérations de base

Sélection (σ)

La sélection garde certaines lignes et ne touche pas aux colonnes. La condition s'écrit en indice, entre accolades. σ_{salaire > 2000}(Employe) se lit à voix haute : « les employés dont le salaire dépasse 2000 ».

Cliquer sur un nœud pour voir la relation qu'il produit.

σ salaire > 2000σ salaire > 2000EmployeEmploye
σ salaire > 20004 lignes · 6 attributs
idEmployenomprenomsalairedate_embaucheservice
1GirardNadia24502019-04-01Etudes
3BarbierSofia31002016-01-10Etudes
4RocheTom22002022-03-07Support
5VidalLucie27502018-11-23Commerce
La sélection filtre les lignes. Les six attributs sont conservés, quatre lignes sur cinq passent. En SQL : SELECT * FROM Employe WHERE salaire > 2000

Les conditions se combinent avec ∧ (et), ∨ (ou), ¬ (non). Les opérateurs de comparaison sont =, ≠, <, >, ≤, ≥.

Cliquer sur un nœud pour voir la relation qu'il produit.

σ service = 'Etudes' ∧ salair…σ service = 'Etudes' ∧ salaire < 3000EmployeEmploye
σ service = 'Etudes' ∧ salaire < 30001 ligne · 6 attributs
idEmployenomprenomsalairedate_embaucheservice
1GirardNadia24502019-04-01Etudes
Le résultat ne contient qu'une ligne, Girard, avec ses six attributs. Barbier est bien aux Etudes, mais gagne 3100 : la seconde condition l'écarte. En SQL : SELECT * FROM Employe WHERE service = 'Etudes' AND salaire < 3000
Les NULL ne se comparent pas

Une comparaison dont un des membres vaut NULL n'est jamais vraie : ni note = NULL, ni note ≠ NULL, ni note > 10. Elle n'est pas fausse non plus : elle est inconnue, et la négation d'une inconnue reste inconnue. Une sélection ne garde que les lignes où la condition est vraie. Une note absente est donc écartée par σ_{note > 10}, et aussi par σ_{¬(note > 10)}.

Elle peut en revanche rester quand une autre partie de la condition suffit. Avec ∨, un seul côté vrai rend le tout vrai : σ_{note > 10 ∨ idCours = 2}(Inscription) garde l'inscription sans note de l'étudiant 4 au cours 2.

L'algèbre d'origine ne connaît pas NULL. C'est SQL qui l'ajoute, avec cette logique à trois valeurs (vrai, faux, inconnu), et qui réserve une écriture à part pour le tester, IS NULL.

Cliquer sur un nœud pour voir la relation qu'il produit.

σ ¬ ( note > 10 )σ ¬ ( note > 10 )InscriptionInscription
σ ¬ ( note > 10 )6 lignes · 3 attributs
idEtudiantidCoursnote
218.5
379.5
447
636
6410
959

Modifier l'expression, puis « Recalculer » : l'arbre se redessine sur les mêmes tables.

Six lignes sur vingt-cinq. Dix-sept notes dépassent 10, six n'y arrivent pas, et les deux inscriptions sans note ne sont d'aucun côté : la négation d'une inconnue reste inconnue. En SQL : SELECT * FROM Inscription WHERE NOT (note > 10)
À manipuler
Cliquer sur la feuille Inscription : 25 lignes, dont deux notes affichées NULL, celle de l'étudiant 4 au cours 2 et celle de l'étudiant 8 au cours 6. Revenir au nœud σ : 6 lignes, et aucune des deux n'y figure. Dans la saisie sous l'arbre, remplacer ¬(note > 10) par note ≤ 10, puis Recalculer : toujours 6 lignes. Écrire ensuite note > 10 ∨ ¬(note > 10), une condition qui semble toujours vraie, et recalculer : 23 lignes, et non 25. Les deux notes absentes restent dehors : ni la comparaison ni sa négation ne les retient.

Projection (π)

La projection garde certaines colonnes et ne filtre aucune ligne. Du moins en apparence. π_{nom, salaire}(Employe) se lit : « le nom et le salaire de chaque employé ».

Cliquer sur un nœud pour voir la relation qu'il produit.

π nom, salaireπ nom, salaireEmployeEmploye
π nom, salaire5 lignes · 2 attributs
nomsalaire
Girard2450
Lemoine1980
Barbier3100
Roche2200
Vidal2750
La projection ne garde que deux attributs sur six. Les cinq lignes subsistent. En SQL : SELECT DISTINCT nom, salaire FROM Employe

Voici la surprise annoncée plus haut. Une relation est un ensemble : elle ne peut pas contenir deux fois la même ligne. Si la projection fait disparaître ce qui distinguait deux lignes, celles-ci se confondent et le résultat en compte une seule.

Cliquer sur un nœud pour voir la relation qu'il produit.

π serviceπ serviceEmployeEmploye
π service3 lignes · 1 attribut
service
Etudes
Support
Commerce
Cinq employés, trois services. Deux paires de lignes sont devenues identiques et ont fusionné. En SQL : SELECT DISTINCT service FROM Employe
π correspond à SELECT DISTINCT, pas à SELECT

SELECT service FROM Employe rend cinq lignes en SQL, avec Etudes et Support deux fois chacune. La projection algébrique en rend trois.

La traduction fidèle de π est donc SELECT DISTINCT. SQL s'écarte ici de l'algèbre dont il dérive, comme il le fait avec NULL, ou avec une table sans clé, qui peut contenir deux fois la même ligne. Il a deux raisons de garder les doublons. Dédoublonner coûte cher, et le moteur ne le fait que si on le lui demande. Et les doublons comptent dès qu'on additionne ou qu'on fait une moyenne : deux salaires égaux pèsent deux fois dans la masse salariale.

Vérification rapideon peut se reprendre

1.Que rend π_{ville}(Etudiant), sachant que la table contient dix étudiants répartis sur quatre villes ?

2.Quelle écriture SQL traduit fidèlement la projection algébrique ?

3.σ_{¬(note > 10)}(Inscription) retient-elle les lignes dont la note vaut NULL ?

Le dédoublonnage ne fait pas qu'alléger un résultat : il peut fausser un calcul fait dessus. Le raisonnement suivant semble tenir, et une de ses étapes ne tient pas.

Une masse salariale qui fond

Une seule étape est fausse. Désigner laquelle.

Un service compte kk employés, tous payés 2 000 € par mois. La direction veut sa masse salariale, la somme des salaires de ses employés, et la tire d'une expression d'algèbre.

Renommage (ρ)

Le renommage change le nom d'un attribut sans toucher aux données. ρ_{salaireMensuel ← salaire}(Employe) se lit : « Employe, où l'attribut salaire s'appelle désormais salaireMensuel ». Il sert quand deux relations portent le même nom d'attribut et qu'il faut les distinguer.

Cliquer sur un nœud pour voir la relation qu'il produit.

ρ salaireMensuel ← salaireρ salaireMensuel ← salaireEmployeEmploye
ρ salaireMensuel ← salaire5 lignes · 6 attributs
idEmployenomprenomsalaireMensueldate_embaucheservice
1GirardNadia24502019-04-01Etudes
2LemoinePaul19802021-09-15Support
3BarbierSofia31002016-01-10Etudes
4RocheTom22002022-03-07Support
5VidalLucie27502018-11-23Commerce
Le résultat contient les mêmes cinq lignes et les mêmes six colonnes ; seul l'en-tête de la quatrième a changé. C'est une opération sur le schéma, pas sur les données.

On peut aussi renommer une relation entière. ρ_{E2}(Employe) désigne une seconde copie d'Employe, dont les attributs s'appellent E2.nom, E2.service, et ainsi de suite. Cela sert à combiner une table avec elle-même, ce que la jointure permettra plus bas.

Produit cartésien (×)

Le produit associe chaque ligne de gauche à chaque ligne de droite. Client × Projet se lit : « chaque client apparié à chaque projet, sans aucune condition ». C'est bien le problème : le résultat compte le produit des deux effectifs.

Cliquer sur un nœud pour voir la relation qu'il produit.

××ClientClientProjetProjet
×16 lignes · 7 attributs
Client.idClientnomprenomvilleidProjetlibelleProjet.idClient
1DurandPaulParis1Portail client1
1DurandPaulParis2Refonte ERP2
1DurandPaulParis3Application mobile3
1DurandPaulParis4Audit reseau4
2LeroySarahLyon1Portail client1
2LeroySarahLyon2Refonte ERP2
2LeroySarahLyon3Application mobile3
2LeroySarahLyon4Audit reseau4
3MoreauKarimParis1Portail client1
3MoreauKarimParis2Refonte ERP2
3MoreauKarimParis3Application mobile3
3MoreauKarimParis4Audit reseau4
4FontaineEvaBordeaux1Portail client1
4FontaineEvaBordeaux2Refonte ERP2
4FontaineEvaBordeaux3Application mobile3
4FontaineEvaBordeaux4Audit reseau4
Quatre clients, quatre projets, seize lignes. Douze d'entre elles n'ont aucun sens. En SQL : SELECT * FROM Client CROSS JOIN Projet

Sur seize lignes, seules quatre associent un projet à son vrai client. Les douze autres sont du bruit. Le produit cartésien n'est presque jamais ce qu'on veut ; il est en revanche la brique dont la jointure est faite.

Jointure (⨝)

La jointure est un produit cartésien suivi d'une sélection. La condition s'écrit en indice, exactement comme pour σ. Client ⨝_{Client.idClient = Projet.idClient} Projet se lit : « chaque client apparié aux projets qui portent son identifiant ». Avec une condition quelconque, on parle de thêta-jointure ; quand la condition est une égalité, comme ici, d'équijointure.

Cliquer sur un nœud pour voir la relation qu'il produit.

⨝ Client.idClient = Projet.id…⨝ Client.idClient = Projet.idClientClientClientProjetProjet
⨝ Client.idClient = Projet.idClient4 lignes · 7 attributs
Client.idClientnomprenomvilleidProjetlibelleProjet.idClient
1DurandPaulParis1Portail client1
2LeroySarahLyon2Refonte ERP2
3MoreauKarimParis3Application mobile3
4FontaineEvaBordeaux4Audit reseau4
La même opération que ci-dessus, mais filtrée : les quatre lignes qui ont un sens. En SQL : SELECT * FROM Client JOIN Projet ON Client.idClient = Projet.idClient

Écrite sans condition, la jointure devient naturelle : elle apparie sur tous les attributs qui portent le même nom des deux côtés, et ne garde qu'un exemplaire de ces attributs.

Cliquer sur un nœud pour voir la relation qu'il produit.

⨝ naturelle⨝ naturelleClientClientProjetProjet
⨝ naturelle4 lignes · 6 attributs
idClientnomprenomvilleidProjetlibelle
1DurandPaulParis1Portail client
2LeroySarahLyon2Refonte ERP
3MoreauKarimParis3Application mobile
4FontaineEvaBordeaux4Audit reseau
Le résultat contient les quatre mêmes lignes que ci-dessus, mais six colonnes au lieu de sept : l'appariement se fait sur idClient, le seul nom commun, et idClient n'est gardé qu'une fois.
Commode, mais fragile

La jointure naturelle dépend entièrement des noms de colonnes. Ajouter demain une colonne dateCreation aux deux tables changerait silencieusement l'appariement, et donc le résultat, sans qu'aucune requête ait été modifiée. Et deux tables qui ne partagent aucun nom d'attribut n'ont rien à égaler : leur jointure naturelle garde toutes les paires, c'est un produit cartésien, et rien ne le signale. Employe ⨝ Classe rend ainsi quinze lignes, cinq employés fois trois classes.

En pratique, écrire la condition explicitement coûte une ligne et évite cette classe de surprise.

Le nombre de lignes d'un produit ne dépend que des effectifs ; celui d'une jointure dépend des valeurs qui s'apparient. Les deux se prévoient avant de cliquer.

Prévoir la taille d'un résultat

  • 1.

    Une agence compte 1 200 clients et 3 500 projets. Combien de lignes le produit Client × Projet compte-t-il ?

  • 2.

    Chaque projet a un seul client, qui existe. Combien de lignes compte la jointure sur idClient ?

  • 3.

    Combien de lignes du produit la condition d'appariement jette-t-elle ?

  • 4.

    Inscription a 3 attributs et Cours 5, avec un seul nom en commun, idCours. Combien d'attributs compte la jointure naturelle Inscription ⨝ Cours ?

La jointure peut aussi apparier une table avec elle-même, grâce au renommage d'une relation vu plus haut. Les paires d'employés qui travaillent dans le même service s'obtiennent en joignant Employe à sa copie E2 :

Cliquer sur un nœud pour voir la relation qu'il produit.

π Employe.nom, E2.nomπ Employe.nom, E2.nom⨝ Employe.service = E2.servic…⨝ Employe.service = E2.service ∧ Employe.idEmploye < E2.idEmployeEmployeEmployeρ E2ρ E2EmployeEmploye
π Employe.nom, E2.nom2 lignes · 2 attributs
Employe.nomE2.nom
GirardBarbier
LemoineRoche
Deux paires : Girard et Barbier aux Etudes, Lemoine et Roche au Support. La seconde condition écarte la paire d'un employé avec lui-même, et ne garde qu'un sens de chaque paire. En SQL : SELECT DISTINCT e.nom, e2.nom FROM Employe e JOIN Employe e2 ON e.service = e2.service AND e.idEmploye < e2.idEmploye

Opérations ensemblistes (∪, ∩, −)

L'union, l'intersection et la différence se comportent comme sur des ensembles ordinaires. Elles exigent que les deux relations aient le même schéma : le même nombre d'attributs, appariés dans l'ordre, de domaines compatibles (des nombres face à des nombres, du texte face à du texte), et qui désignent la même chose.

Les trois se lisent en une phrase chacune. Inscrits ∪ Boursiers : « les noms qui figurent dans au moins une des deux listes ». Inscrits ∩ Boursiers : « ceux qui figurent dans les deux ». Inscrits − Boursiers : « ceux qui figurent dans la première et pas dans la seconde ».

Cliquer sur un nœud pour voir la relation qu'il produit.

∪∪InscritsInscritsBoursiersBoursiers
∪4 lignes · 1 attribut
nom
Martin
Diallo
Dupond
Nguyen
Le résultat contient quatre lignes pour cinq lignes en entrée. Diallo figure dans les deux listes et n'apparaît qu'une fois.

Cliquer sur un nœud pour voir la relation qu'il produit.

∩∩InscritsInscritsBoursiersBoursiers
∩1 ligne · 1 attribut
nom
Diallo
Le résultat ne contient qu'une ligne, Diallo : le seul nom présent des deux côtés.

Cliquer sur un nœud pour voir la relation qu'il produit.

−−InscritsInscritsBoursiersBoursiers
−2 lignes · 1 attribut
nom
Martin
Dupond
Le résultat contient deux lignes, Martin et Dupond. Attention à l'ordre : Boursiers − Inscrits rendrait Nguyen, et lui seul.

Compter une union, une intersection, une différence

  • 1.

    Une association étudiante tient deux listes de même schéma : 120 adhérents et 45 bénévoles, dont 30 figurent sur les deux. Combien de lignes rend Adherents ∪ Benevoles ?

  • 2.

    Combien en rend Adherents − Benevoles ?

  • 3.

    Et Benevoles − Adherents ?

Le moteur de ces figures vérifie ce qu'il peut : le nombre d'attributs, puis, colonne par colonne, qu'un nombre ne se retrouve pas face à un texte. TravailleSur et Projet ont trois attributs chacun, mais leur union rangerait des libellés de projet parmi des identifiants : il la refuse.

Cliquer sur un nœud pour voir la relation qu'il produit.

∪∪TravailleSurTravailleSurProjetProjet
∪

Échec attendu.opérandes non compatibles : la colonne 2 contient des nombres à gauche (idProjet) et du texte à droite (libelle). Les colonnes s'apparient une à une, dans l'ordre, et chaque paire doit avoir le même domaine.

Trois attributs de chaque côté, et pourtant l'union échoue : la deuxième colonne contient des identifiants à gauche (idProjet) et des libellés à droite (libelle). Le nœud ∪ affiche le message du moteur.

Le sens, en revanche, lui échappe. Des noms de clients et des noms de villes sont tous du texte : π_{nom}(Client) ∪ π_{ville}(Client) passe sans erreur, et rend un mélange qui ne veut rien dire. Vérifier que deux colonnes parlent de la même chose reste le travail de celui qui écrit l'union.

En SQL : UNION, INTERSECT, EXCEPT, et moins de contrôles

∪ s'écrit UNION, ∩ INTERSECT et − EXCEPT. Contrairement à SELECT, ces trois opérations dédoublonnent d'elles-mêmes, comme l'algèbre ; UNION ALL est la variante qui garde les doublons. MySQL connaît INTERSECT et EXCEPT depuis sa version 8.0.31.

SQL est en revanche moins strict que l'algèbre sur le schéma. MySQL et SQLite ne vérifient que le nombre de colonnes : ils les apparient par position et gardent les noms de la première requête. SELECT * FROM TravailleSur UNION SELECT * FROM Projet s'y exécute sans un mot, et range « Refonte ERP » sous l'en-tête idProjet. PostgreSQL, lui, refuse d'apparier un entier et un texte. Dans tous les cas, le sens reste à vérifier soi-même.

La différence est l'opération qui permet d'exprimer une négation. « Les cours sans enseignant affecté » se traduit par une différence entre tous les cours et ceux qui en ont un. Ceux qui en ont un, c'est la jointure qui les trouve : elle ne garde un cours que si son enseignant existe.

Cliquer sur un nœud pour voir la relation qu'il produit.

−−π idCours, intituleπ idCours, intituleCoursCoursπ idCours, intituleπ idCours, intitule⨝ Cours.idEnseignant = Enseig…⨝ Cours.idEnseignant = Enseignant.idEnseignantCoursCoursEnseignantEnseignant
−2 lignes · 2 attributs
idCoursintitule
4Physique
7Reseaux
Le résultat contient deux lignes, Physique et Reseaux. La jointure ne garde que les cinq cours dont l'enseignant existe, puisqu'un NULL ne s'apparie à rien ; la différence rend les deux autres. En SQL : SELECT idCours, intitule FROM Cours EXCEPT SELECT Cours.idCours, intitule FROM Cours JOIN Enseignant ON Cours.idEnseignant = Enseignant.idEnseignant

idCours reste dans les deux projections, et ce n'est pas un détail. Sur intitule seul, deux cours de même intitulé, l'un avec enseignant et l'autre sans, se confondraient, et la différence ferait disparaître le second.

Division (÷)

Le besoin apparaît dès qu'une question contient « tous les » : tous les projets d'une liste, toutes les compétences exigées, tous les cours d'un programme. Une jointure répond à « au moins un », ce qui est une question différente et plus large. Les opérations déjà vues savent répondre à « tous les », mais au prix d'un détour par deux différences ; la division donne un nom et un symbole à ce détour.

La division prend deux relations : le dividende R, et le diviseur S, dont tous les attributs figurent aussi dans R. Elle rend les valeurs des autres attributs de R qui sont associées à toutes les lignes de S. Le cas le plus courant, celui de la figure, met deux attributs à gauche et un à droite. Affectation ÷ Tous se lit : « les employés qui travaillent sur tous les projets listés dans Tous ».

Six affectations d'un côté, deux projets exigés de l'autre.

Cliquer sur un nœud pour voir la relation qu'il produit.

÷÷AffectationAffectationTousTous
÷2 lignes · 1 attribut
employe
Nadia
Sofia
Le résultat contient deux lignes et une seule colonne, employe : Nadia et Sofia. Cliquer sur les deux feuilles pour retrouver les données du calcul détaillé ci-dessous.

Le calcul se fait employé par employé. Pour chacun, rassembler ses projets, puis vérifier que les deux projets exigés y figurent tous les deux. C'est le tableau intermédiaire que la notation cache.

employéses projetsPortail ?Mobile ?retenu ?
NadiaPortail, Mobileouiouioui
PaulPortailouinonnon
SofiaPortail, Mobile, ERPouiouioui

Trois employés examinés, deux retenus. Deux enseignements se lisent dans ce tableau. Paul est écarté par une seule case à « non » : il suffit qu'un seul élément du diviseur manque. Sofia est retenue malgré ERP, qui n'est pas dans le diviseur : la division exige au moins les projets listés, pas exactement ceux-là.

Un diviseur vide ne disqualifie personne : il ne manque rien à aucun candidat, et tous sont retenus. Une question en « tous les » sur une liste qui peut être vide demande donc un contrôle à part.

Noter enfin que la colonne projet a disparu du résultat, qui ne porte plus que employe. C'est la règle : la division retire les attributs du diviseur et ne garde que les autres.

La division n'ajoute pourtant rien à ce que l'algèbre sait déjà faire : elle se récrit avec π, × et −. On fabrique tous les couples qui devraient exister, chaque employé avec chaque projet exigé ; on retire ceux qui existent vraiment ; il reste les couples manquants, et l'employé d'un couple manquant n'est pas retenu.

Cliquer sur un nœud pour voir la relation qu'il produit.

−−π employeπ employeAffectationAffectationπ employeπ employe−−××π employeπ employeAffectationAffectationTousTousAffectationAffectation
−2 lignes · 1 attribut
employe
Nadia
Sofia
La même division, sans le symbole ÷. Le nœud − du bas, celui des couples manquants, n'en contient qu'un : Paul et Mobile. Paul est retiré, Nadia et Sofia restent.
À manipuler
Cliquer sur le nœud × : six couples attendus, chaque employé avec chaque projet exigé. Cliquer sur le nœud − qui le surmonte : il ne reste que Paul et Mobile, le seul couple manquant. Le nœud π employe du dessus garde Paul, que la racine retire des trois employés : restent Nadia et Sofia, les deux lignes de la figure précédente.

C'est l'opération que personne ne retient, parce qu'elle est la seule à ne pas avoir de mot-clé SQL. On l'y écrit justement par ce détour, devenu une double négation : les employés pour lesquels il n'existe pas de projet de la liste auquel ils ne participent pas. Cette formulation lourde est ce que la division résume en un symbole.

Compter les couples manquants est le cœur du calcul, et c'est là qu'un raisonnement rapide dérape.

Des couples manquants qui disparaissent

Une seule étape est fausse. Désigner laquelle.

Dans la figure de la division, trois employés et deux projets exigés. Affectation contient 5 couples sur ces deux projets, et mm couples des mêmes employés sur d'autres projets, comme Sofia sur ERP. Il s'agit de compter les couples manquants, ceux qui écartent un employé.

La division, comptée à la main

  • 1.

    Une plateforme de formation compte 40 apprentis inscrits à au moins un module, et une certification exige 3 modules précis. Combien de couples (apprenti, module exigé) la division doit-elle vérifier ?

  • 2.

    100 de ces couples figurent parmi les inscriptions. Combien de couples manquent ?

  • 3.

    Combien d'apprentis la division écarte-t-elle au plus ?

  • 4.

    Et au moins, sachant qu'un apprenti n'a que 3 couples attendus, donc au plus 3 couples manquants ?

Vérification rapideon peut se reprendre

1.Le moteur accepte π_{nom}(Client) ∪ π_{ville}(Client) sans erreur. Que manque-t-il à cette union pour avoir un sens ?

2.EmployeCompetence ÷ Competence rend :

3.Aucun client n'habite Nantes. Que rend π_{idEmploye, idProjet}(TravailleSur) divisé par les projets des clients nantais ?

Le tableau récapitulatif

OpérationNotationEffet sur les lignesEffet sur les colonnes
Sélectionσ_{condition}(R)en garde certainesinchangées
Projectionπ_{attributs}(R)dédoublonnen'en garde que certaines
Renommageρ_{b ← a}(R), ρ_{R2}(R)inchangéesen renomme une, ou renomme la relation
ProduitR × Smultiplie les effectifsconcatène les deux schémas
JointureR ⨝_{condition} Sapparieconcatène les deux schémas (la naturelle ne garde qu'une fois les attributs communs)
UnionR ∪ Srassemble, dédoublonneschéma commun exigé
IntersectionR ∩ Sgarde le communschéma commun exigé
DifférenceR − Sretireschéma commun exigé
DivisionR ÷ Sgarde ce qui couvre tout Sretire les attributs de S
Jointure externe gaucheR ⟕_{condition} Sapparie, et garde aussi les lignes de R sans partenaire, complétées par NULLconcatène les deux schémas

La dernière ligne n'a pas d'écriture dans le moteur des arbres. La figure suivante la montre à côté de la jointure ordinaire, sur les cours et leurs enseignants ; le chapitre sur les requêtes SQL la pratique sous le nom de LEFT JOIN.

LEFT JOIN … ON garde toutes les lignes de gauche, complétées par des NULL à droite quand rien ne correspond.

Cours(gauche, 7 lignes)

idCoursintitulevolumeHorairecoefficientidEnseignant
1Bases de donnees3021
2Algorithmique241.52
3Mathematiques362.53
4Physique272NULL
5Informatique3021
6Statistiques1813
7Reseaux211.5NULL

Enseignant(droite, 3 lignes)

idEnseignantnomEnsprenomEnsspecialite
1DurandClaireBases de donnees
2FerrandMarcAlgorithmique
3AubertSophieStatistiques

ON Cours.idEnseignant = Enseignant.idEnseignant

Résultat7 lignes = 5 appariées + 2 de Cours sans correspondance
idCoursintitulevolumeHorairecoefficientCours.idEnseignantEnseignant.idEnseignantnomEnsprenomEnsspecialite
1Bases de donnees30211DurandClaireBases de donnees
2Algorithmique241.522FerrandMarcAlgorithmique
3Mathematiques362.533AubertSophieStatistiques
4Physique272NULLNULLNULLNULLNULL
5Informatique30211DurandClaireBases de donnees
6Statistiques18133AubertSophieStatistiques
7Reseaux211.5NULLNULLNULLNULLNULL
  • NULL en orange : fabriqué par la jointure. Cette valeur n'est nulle part dans la base, elle comble la place d'une ligne qui n'existe pas.
SELECT *
FROM Cours
LEFT JOIN Enseignant
  ON Cours.idEnseignant = Enseignant.idEnseignant
Les cours et leur enseignant. Deux cours n'en ont pas : la jointure ordinaire les perd, la jointure externe gauche les garde, avec des NULL fabriqués à droite.
À manipuler
La figure s'ouvre sur LEFT : 7 lignes, dont Physique et Reseaux avec NULL dans les quatre colonnes d'Enseignant. Choisir INNER : 5 lignes, les deux cours sans enseignant ont disparu. RIGHT donne aussi 5 lignes et n'ajoute rien, puisque chacun des trois enseignants assure au moins un cours. CROSS donne 21 lignes, 7 × 3, sans aucune condition.
Vérification rapideon peut se reprendre

1.Une jointure R ⨝ S sous condition équivaut à :

2.Deux tables de 200 et 50 lignes. Combien de lignes compte leur produit cartésien ?

Composer, et donc dessiner

La clôture permet d'emboîter. L'expression suivante enchaîne trois opérations : une jointure, une sélection, une projection. Elle se lit de l'intérieur vers l'extérieur : « apparier chaque étudiant à sa classe, ne garder que la classe 1A, puis n'afficher que le nom et le prénom ».

Cliquer sur un nœud pour voir la relation qu'il produit.

π nom, prenomπ nom, prenomσ nomClasse = '1A'σ nomClasse = '1A'⨝ Etudiant.idClasse = Classe…⨝ Etudiant.idClasse = Classe.idClasseEtudiantEtudiantClasseClasse
π nom, prenom4 lignes · 2 attributs
nomprenom
MartinLea
DialloAmine
DupondSophie
PetitSofia
Le résultat contient quatre lignes et deux colonnes, nom et prenom. Cliquer sur chaque nœud pour suivre la transformation, de la table brute au résultat. En SQL : SELECT DISTINCT nom, prenom FROM Etudiant JOIN Classe ON Etudiant.idClasse = Classe.idClasse WHERE nomClasse = '1A'

L'arbre se lit de bas en haut. Les feuilles sont les tables telles qu'elles sont stockées ; chaque nœud consomme le résultat du niveau inférieur ; la racine porte le résultat final.

ÉlémentRôle
Feuillesles tables de la base
Nœuds internesles opérations, σ π ρ ⨝ × ∪ ∩ − ÷
Arêtesl'ordre d'évaluation, du bas vers le haut
Racinele résultat rendu

Cet arbre est aussi la façon dont un moteur représente une requête avant de l'exécuter. Le lire, c'est lire la forme logique du plan d'exécution ; le plan réel y ajoute le choix des algorithmes et des index, que le chapitre Exploiter une base fait lire. Et si le moteur se donne la peine de choisir entre plusieurs arbres, c'est qu'ils ne se valent pas.

Optimiser : pousser les sélections vers les feuilles

Voici la raison pour laquelle ce chapitre existe. Deux arbres peuvent rendre exactement le même résultat en faisant un travail très différent. Le nombre de lignes affiché à chaque nœud le montre directement. On compare le plus gros résultat intermédiaire, c'est-à-dire le nœud d'opération le plus large : les feuilles sont lues de toute façon, et aucune réécriture ne les réduit.

Soit à nouveau la question : le nom et le prénom des étudiants de la classe 1A. Première écriture, la plus naïve : tout combiner, puis filtrer le tas.

Cliquer sur un nœud pour voir la relation qu'il produit.

π nom, prenomπ nom, prenomσ Etudiant.idClasse = Classe…σ Etudiant.idClasse = Classe.idClasse ∧ nomClasse = '1A'××EtudiantEtudiantClasseClasse
π nom, prenom4 lignes · 2 attributs
nomprenom
MartinLea
DialloAmine
DupondSophie
PetitSofia
Version 1. Le produit cartésien fabrique 30 lignes, dont 26 seront jetées à l'étape suivante.

Deuxième écriture : remplacer le produit par une jointure, ce qui revient à effectuer une partie du filtrage plus tôt.

Cliquer sur un nœud pour voir la relation qu'il produit.

π nom, prenomπ nom, prenomσ nomClasse = '1A'σ nomClasse = '1A'⨝ Etudiant.idClasse = Classe…⨝ Etudiant.idClasse = Classe.idClasseEtudiantEtudiantClasseClasse
π nom, prenom4 lignes · 2 attributs
nomprenom
MartinLea
DialloAmine
DupondSophie
PetitSofia
Version 2. La jointure ne construit plus que 10 lignes. Le plus gros résultat intermédiaire est passé de 30 à 10.

Troisième écriture : filtrer Classe avant de joindre. La sélection descend jusqu'à la feuille.

Cliquer sur un nœud pour voir la relation qu'il produit.

π nom, prenomπ nom, prenom⨝ Etudiant.idClasse = Classe…⨝ Etudiant.idClasse = Classe.idClasseEtudiantEtudiantσ nomClasse = '1A'σ nomClasse = '1A'ClasseClasse
π nom, prenom4 lignes · 2 attributs
nomprenom
MartinLea
DialloAmine
DupondSophie
PetitSofia

Modifier l'expression, puis « Recalculer » : l'arbre se redessine sur les mêmes tables.

Version 3. La classe est filtrée d'abord : la jointure ne voit qu'une seule ligne à droite et n'en produit que 4, soit exactement le résultat. Plus rien n'est construit pour être jeté.
À manipuler
Dans chacune des trois versions, cliquer sur le nœud qui combine les deux tables, × puis ⨝ : 30 lignes, puis 10, puis 4, toujours sur 11 attributs. Dans la troisième, cliquer sur le nœud σ posé sur Classe : une ligne, la classe 1A, la seule que la jointure apparie. Dans sa saisie, remplacer nomClasse = '1A' par annee = 1, puis Recalculer : deux classes passent le filtre, et la jointure construit 7 lignes, encore exactement le résultat. La taille de la jointure suit celle de ce qui lui arrive.

Les trois arbres rendent les quatre mêmes lignes, et la troisième version ne fabrique aucune ligne intermédiaire inutile. Sur une base de démonstration, la différence de temps est invisible ; sur dix mille étudiants et cent classes, la version 1 construit un million de lignes pour en garder une centaine.

Ce que coûte chaque version

  • 1.

    Une enseigne a 50 000 commandes et 200 magasins, et cherche les commandes du magasin de Lille. Combien de lignes le produit Commande × Magasin de la version naïve fabrique-t-il ?

  • 2.

    Chaque commande vient d'un seul magasin. Combien de lignes construit la jointure de la deuxième version ?

  • 3.

    La troisième version filtre d'abord Magasin, qui ne garde que Lille. Si les commandes se répartissent également entre les magasins, combien de lignes sa jointure construit-elle ?

  • 4.

    Combien de fois la troisième version construit-elle moins de lignes que la première ?

Les règles de réécriture.

  1. Découper les conjonctions. σ_{a ∧ b}(R) rend la même relation que σ_{a}(σ_{b}(R)), et chaque morceau descend ensuite vers la feuille qui porte ses attributs : c'est ce découpage qui fait passer de la version 1 à la version 3. Une disjonction ne se découpe pas : une condition σ_{a ∨ b} qui mélange deux tables reste entière au-dessus de leur jointure.
  2. Descendre les sélections le plus près possible des feuilles. Filtrer avant de combiner est toujours au moins aussi bon.
  3. Remplacer produit puis sélection par une jointure. C'est la même opération, mais le moteur peut l'exécuter sans matérialiser le produit.
  4. Descendre les projections, en gardant les attributs dont les opérations supérieures ont besoin. Moins de colonnes transportées, moins de mémoire. En pratique, le moteur descend les projections sans dédoublonner, ce qui coûterait cher, et ne dédoublonne qu'à la racine, si DISTINCT est demandé.
  5. Commencer par la jointure la plus sélective quand il y en a plusieurs, pour que les suivantes travaillent sur peu de lignes.

Toutes ces règles disent la même chose : jeter tôt ce qui sera jeté de toute façon.

La règle des projections se voit aussi dans la figure, qui affiche à chaque nœud son nombre d'attributs. Sur la même question, une quatrième version ne laisse entrer dans la jointure que ce qui sert plus haut.

Cliquer sur un nœud pour voir la relation qu'il produit.

π nom, prenomπ nom, prenom⨝ Etudiant.idClasse = Classe…⨝ Etudiant.idClasse = Classe.idClasseπ nom, prenom, idClasseπ nom, prenom, idClasseEtudiantEtudiantπ idClasseπ idClasseσ nomClasse = '1A'σ nomClasse = '1A'ClasseClasse
π nom, prenom4 lignes · 2 attributs
nomprenom
MartinLea
DialloAmine
DupondSophie
PetitSofia
Version 4, les projections descendues. Etudiant ne garde que nom, prenom et idClasse, et Classe, une fois filtrée, que idClasse : la jointure construit les mêmes 4 lignes, sur 4 attributs au lieu de 11.

L'ordre compte sous la jointure de droite : la sélection sur nomClasse doit passer avant la projection π_{idClasse}, qui retire justement nomClasse. Et idClasse reste dans les deux projections, parce que la condition de jointure en a besoin, même si la racine ne l'affiche pas.

Vérification rapideon peut se reprendre

1.Où placer une sélection pour qu'un arbre coûte le moins cher possible ?

2.Dans σ_{nomClasse = '1A' ∨ ville = 'Lyon'}(Etudiant ⨝ Classe), peut-on descendre ville = 'Lyon' sur la feuille Etudiant ?

Un arbre à plusieurs jointures

Les trois versions précédentes n'avaient que deux tables. Le gain devient beaucoup plus net dès qu'il y en a davantage, puisque chaque jointure travaille sur ce que la précédente lui a laissé.

Objectif : le nom des étudiants inscrits à un cours dispensé par l'enseignante Durand. Quatre tables, trois jointures.

Cliquer sur un nœud pour voir la relation qu'il produit.

π nomπ nom⨝ Etudiant.idEtudiant = Inscr…⨝ Etudiant.idEtudiant = Inscription.idEtudiantEtudiantEtudiant⨝ Inscription.idCours = Cours…⨝ Inscription.idCours = Cours.idCoursInscriptionInscription⨝ Cours.idEnseignant = Enseig…⨝ Cours.idEnseignant = Enseignant.idEnseignantCoursCoursσ nomEns = 'Durand'σ nomEns = 'Durand'EnseignantEnseignant
π nom7 lignes · 1 attribut
nom
Martin
Diallo
Dupond
Lefebvre
Moreau
Marchand
Nguyen
Le résultat contient sept noms dans une seule colonne. La sélection sur l'enseignante est placée au plus bas : cliquer sur les nœuds de bas en haut pour voir la relation se construire, puis se réduire. En SQL : SELECT DISTINCT e.nom FROM Etudiant e JOIN Inscription i ON e.idEtudiant = i.idEtudiant JOIN Cours c ON i.idCours = c.idCours JOIN Enseignant en ON c.idEnseignant = en.idEnseignant WHERE en.nomEns = 'Durand'

Remarquer la projection finale : sept lignes contre huit pour la jointure qu'elle surmonte. Un étudiant inscrit à deux cours de Durand y figurait deux fois, et le dédoublonnage les confond. Il confondrait de même deux étudiants qui portent le même nom ; projeter sur idEtudiant, nom l'éviterait.

Vérification rapideon peut se reprendre

Trois arbres rendent le même résultat ; leur plus gros résultat intermédiaire compte 30, 10 et 4 lignes. Lequel choisir ?

Écrire des expressions, et les faire vérifier

Six exercices, du plus simple au plus composé, sur les tables du cours. Chacun s'écrit dans sa zone de saisie, au clavier ou avec la barre de symboles. « Vérifier » calcule l'expression sur plusieurs versions des tables, et la compare à la correction calculée sur les mêmes : une expression juste écrite autrement que la correction, jointure naturelle, sélection placée ailleurs, colonnes dans un autre ordre, est acceptée. Chaque version qui change une table la montre sous son libellé ; une version cachée ne dit que si elle passe, et démasque une expression écrite pour les seules données affichées. L'arbre de l'expression vérifiée se dessine sous le verdict, et se lit nœud par nœud comme les figures. Des indices s'ouvrent un par un ; une correction, après deux vérifications ou une réussite.

Exercice : écrire puis vérifier

Les villes des étudiants, sans répétition

La scolarité prépare une carte des lieux de résidence. Écrire l'expression qui donne les villes où résident des étudiants (attribut ville d'Etudiant), chacune une seule fois.

Expression à écrire

Une condition ou une liste d'attributs s'écrit en indice, entre accolades, une chaîne entre apostrophes, un nombre décimal avec un point. Au clavier, et, ou, non, <>, <=, >= et le tiret de la différence conviennent aussi.

Écrire l'expression, puis « Vérifier » : elle est calculée sur 3 cas, dont 1 caché, et comparée à la correction calculée sur les mêmes tables.

Les tables du cas « une étudiante de plus, installée à Lille »
Etudiant
idEtudiantnomprenomdateNaissanceemailvilleidClasseactif
1MartinLea2004-03-12lea.martin@ecole.frParis11
2DialloAmine2003-11-02amine.diallo@ecole.frLyon11
3DupondSophie2004-07-25sophie.dupond@ecole.frParis11
4DurandTom2003-05-30tom.durand@ecole.frNantes20
5LefebvreInes2004-01-18ines.lefebvre@ecole.frLyon21
6BernardYanis2002-09-08NULLParis21
7MoreauChi2004-02-14chi.moreau@ecole.frParis31
8MarchandHugo2003-08-21NULLBordeaux31
9NguyenElsa2004-11-30elsa.nguyen@ecole.frLyon31
10PetitSofia2002-12-05sofia.petit@ecole.frNantes10
11RouxInes2004-06-03ines.roux@ecole.frLille21

Les autres tables sont celles du cours.

Exercice : écrire puis vérifier

Les employés du support bien payés

Le service du personnel prépare la revue des salaires du support. Écrire l'expression qui donne les employés du service Support dont le salaire dépasse 2 000 €, avec tous leurs attributs.

Expression à écrire

Une condition ou une liste d'attributs s'écrit en indice, entre accolades, une chaîne entre apostrophes, un nombre décimal avec un point. Au clavier, et, ou, non, <>, <=, >= et le tiret de la différence conviennent aussi.

Écrire l'expression, puis « Vérifier » : elle est calculée sur 3 cas, dont 1 caché, et comparée à la correction calculée sur les mêmes tables.

Les tables du cas « un salaire relevé, une mutation, une embauche à 2 000 € »
Employe
idEmployenomprenomsalairedate_embaucheservice
1GirardNadia24502019-04-01Etudes
2LemoinePaul21002021-09-15Support
3BarbierSofia31002016-01-10Etudes
4RocheTom22002022-03-07Support
5VidalLucie27502018-11-23Support
6NoelAda20002024-02-12Support

Les autres tables sont celles du cours.

Exercice : écrire puis vérifier

Les étudiants parisiens et leur classe

Écrire l'expression qui donne le nom de chaque étudiant habitant Paris, avec le nom de sa classe (nomClasse).

Expression à écrire

Une condition ou une liste d'attributs s'écrit en indice, entre accolades, une chaîne entre apostrophes, un nombre décimal avec un point. Au clavier, et, ou, non, <>, <=, >= et le tiret de la différence conviennent aussi.

Écrire l'expression, puis « Vérifier » : elle est calculée sur 3 cas, dont 1 caché, et comparée à la correction calculée sur les mêmes tables.

Les tables du cas « Chi Moreau passe en 1A, Ines Lefebvre s'installe à Paris »
Etudiant
idEtudiantnomprenomdateNaissanceemailvilleidClasseactif
1MartinLea2004-03-12lea.martin@ecole.frParis11
2DialloAmine2003-11-02amine.diallo@ecole.frLyon11
3DupondSophie2004-07-25sophie.dupond@ecole.frParis11
4DurandTom2003-05-30tom.durand@ecole.frNantes20
5LefebvreInes2004-01-18ines.lefebvre@ecole.frParis21
6BernardYanis2002-09-08NULLParis21
7MoreauChi2004-02-14chi.moreau@ecole.frParis11
8MarchandHugo2003-08-21NULLBordeaux31
9NguyenElsa2004-11-30elsa.nguyen@ecole.frLyon31
10PetitSofia2002-12-05sofia.petit@ecole.frNantes10

Les autres tables sont celles du cours.

Exercice : écrire puis vérifier

Les cours de plus de 24 heures et leur enseignant

Écrire l'expression qui donne l'intitulé de chaque cours de plus de 24 heures (volumeHoraire), avec le nom de son enseignant (nomEns).

Expression à écrire

Une condition ou une liste d'attributs s'écrit en indice, entre accolades, une chaîne entre apostrophes, un nombre décimal avec un point. Au clavier, et, ou, non, <>, <=, >= et le tiret de la différence conviennent aussi.

Écrire l'expression, puis « Vérifier » : elle est calculée sur 3 cas, dont 1 caché, et comparée à la correction calculée sur les mêmes tables.

Les tables du cas « la physique reçoit un enseignant »
Cours
idCoursintitulevolumeHorairecoefficientidEnseignant
1Bases de donnees3021
2Algorithmique241.52
3Mathematiques362.53
4Physique2722
5Informatique3021
6Statistiques1813
7Reseaux211.5NULL

Les autres tables sont celles du cours.

Exercice : écrire puis vérifier

Les clients sans projet

Le service commercial veut relancer les clients avec qui l'entreprise n'a encore aucun projet. Écrire l'expression qui donne leur identifiant, idClient.

Sur les tables du cours, les quatre clients ont chacun un projet : la réponse juste y est une relation vide, ce qui n'est pas une erreur, mais la réponse « personne ».

Expression à écrire

Une condition ou une liste d'attributs s'écrit en indice, entre accolades, une chaîne entre apostrophes, un nombre décimal avec un point. Au clavier, et, ou, non, <>, <=, >= et le tiret de la différence conviennent aussi.

Écrire l'expression, puis « Vérifier » : elle est calculée sur 3 cas, dont 1 caché, et comparée à la correction calculée sur les mêmes tables.

Les tables du cas « avec un cinquième client, qui n'a pas encore de projet »
Client
idClientnomprenomville
1DurandPaulParis
2LeroySarahLyon
3MoreauKarimParis
4FontaineEvaBordeaux
5DurandLeaLyon

Les autres tables sont celles du cours.

Exercice : écrire puis vérifier

Les employés affectés à tous les projets

Écrire l'expression qui donne l'identifiant (idEmploye) des employés qui travaillent sur tous les projets de la table Projet. Un employé affecté en plus à d'autres travaux reste retenu.

Expression à écrire

Une condition ou une liste d'attributs s'écrit en indice, entre accolades, une chaîne entre apostrophes, un nombre décimal avec un point. Au clavier, et, ou, non, <>, <=, >= et le tiret de la différence conviennent aussi.

Écrire l'expression, puis « Vérifier » : elle est calculée sur 3 cas, dont 1 caché, et comparée à la correction calculée sur les mêmes tables.

Les tables du cas « Barbier rejoint les deux projets qui lui manquaient »
TravailleSur
idEmployeidProjetheures
11120
1340
2180
32200
3360
4435
5290
3110
345

Les autres tables sont celles du cours.

Ressources complémentaires

  • INSA Rouen, cours SGBD. Un support complet sur les systèmes de gestion de bases de données et l'algèbre relationnelle. Algèbre relationnelle, INSA Rouen
  • Bertrand Liaudet, INSIA. Optimisation et arbres algébriques, avec exemples et exercices. Le site d'origine, à l'université de Cergy-Pontoise, ne répond plus ; le lien mène à une copie archivée. Arbres algébriques, copie archivée
  • DB Fiddle. Un environnement pour exécuter des requêtes SQL et comparer leurs résultats. db-fiddle.com

Mettre en pratique

Cliquer sur un nœud pour voir la relation qu'il produit.

π prenom, nomπ prenom, nomσ actif = 0σ actif = 0EtudiantEtudiant
π prenom, nom2 lignes · 2 attributs
prenomnom
TomDurand
SofiaPetit
Que fait cet arbre ?
Lire l'arbre ci-dessus et en donner la requête SQL

De bas en haut : partir de Etudiant, ne garder que les lignes dont actif vaut 0, puis n'afficher que le prénom et le nom.

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

Le DISTINCT traduit la projection. Ici il ne change rien, les deux étudiants concernés ayant des noms différents, mais l'écriture reste la traduction fidèle.

Améliorer cet arbre

L'expression suivante donne le bon résultat, mais fait travailler la jointure pour rien.

Cliquer sur un nœud pour voir la relation qu'il produit.

π nomπ nomσ ville = 'Lyon'σ ville = 'Lyon'⨝ Etudiant.idClasse = Classe…⨝ Etudiant.idClasse = Classe.idClasseEtudiantEtudiantClasseClasse
π nom3 lignes · 1 attribut
nom
Diallo
Lefebvre
Nguyen
Avant. La jointure produit 10 lignes, la sélection en garde 3.

La condition ville = 'Lyon' ne porte que sur Etudiant. Rien n'oblige à attendre la jointure pour l'appliquer.

Cliquer sur un nœud pour voir la relation qu'il produit.

π nomπ nom⨝ Etudiant.idClasse = Classe…⨝ Etudiant.idClasse = Classe.idClasseσ ville = 'Lyon'σ ville = 'Lyon'EtudiantEtudiantClasseClasse
π nom3 lignes · 1 attribut
nom
Diallo
Lefebvre
Nguyen
Après. La sélection descend sur la feuille Etudiant : la jointure ne voit plus que 3 lignes à gauche au lieu de 10.

Le résultat est identique. Le travail intermédiaire est divisé par trois sur cette petite base, et dans le même rapport que la sélectivité du filtre sur une grande. Descendre aussi les projections, π_{nom, idClasse} sur Etudiant et π_{idClasse} sur Classe, ne laisserait passer dans la jointure que trois attributs au lieu de onze.

Pourquoi π_{nom}(Etudiant × Client) échoue-t-il ?

Parce que nom désigne deux colonnes différentes après le produit : celle de Etudiant et celle de Client. L'expression est ambiguë, et le moteur le signale plutôt que de choisir au hasard.

Cliquer sur un nœud pour voir la relation qu'il produit.

π nomπ nom××EtudiantEtudiantClientClient
π nom

Échec attendu.attribut ambigu : nom désigne Etudiant.nom et Client.nom. Préciser la table, par exemple Etudiant.nom

Le produit est calculé, 40 lignes et 12 attributs ; c'est la projection qui échoue. Cliquer sur le nœud π pour lire le message.

Deux façons de lever l'ambiguïté. Qualifier l'attribut :

Cliquer sur un nœud pour voir la relation qu'il produit.

π Etudiant.nomπ Etudiant.nom××EtudiantEtudiantClientClient
π Etudiant.nom10 lignes · 1 attribut
nom
Martin
Diallo
Dupond
Durand
Lefebvre
Bernard
Moreau
Marchand
Nguyen
Petit
Le résultat contient dix noms, un par étudiant. Le produit en fabriquait quarante lignes ; la projection les dédoublonne.

Ou renommer avant de combiner, ce qui vaut mieux quand l'expression est longue :

Cliquer sur un nœud pour voir la relation qu'il produit.

π nomEtudiantπ nomEtudiant××ρ nomEtudiant ← nomρ nomEtudiant ← nomEtudiantEtudiantClientClient
π nomEtudiant10 lignes · 1 attribut
nomEtudiant
Martin
Diallo
Dupond
Durand
Lefebvre
Bernard
Moreau
Marchand
Nguyen
Petit
Même résultat, les mêmes dix noms. Le renommage règle le conflit à la source plutôt qu'au moment de projeter.

Qualifier ne suffit plus quand les deux colonnes viennent de la même table : dans Client × Client, les deux colonnes s'appellent Client.nom. Le moteur le dit, et conseille de renommer l'une des deux copies par ρ, comme pour la jointure d'Employe avec sa copie E2.

Chapitre révisé en octobre 2026 · Une erreur dans ce chapitre ?

Dans la bibliothèque d'exercices