Les bases de données : le modèle relationnel et les premières requêtes
Cours complet · informatique (tronc commun des prépas scientifiques), chapitre 15 · prépas scientifiques, tronc commun
Travailler ce chapitre sur Adloun Exercices corrigés de ce chapitre
<i class="fa-solid fa-compass mr-2" style="color:#9A563B"></i>15.1 Introduction et motivation
Le chapitre 4 savait lire un fichier de mesures : une table, des lignes, des colonnes — et tout le travail à la charge de notre programme Python. Mais les données du monde réel ne vivent pas dans un fichier : la médiathèque municipale connaît ses livres, ses auteurs, ses usagers et les emprunts qui les relient ; l'hôpital, ses patients, ses médecins et leurs rendez-vous ; le lycée, ses élèves, ses classes et ses options. Plusieurs familles d'objets, des liens entre elles, des milliers de mises à jour par jour, et des questions qui traversent tout : « quels usagers de Lyon ont emprunté un livre de Verne en octobre ? ».
Pour cela, l'informatique a construit l'un de ses plus beaux édifices : la base de données relationnelle, où les données sont rangées en tables reliées par des clés, et qu'on interroge dans un langage déclaratif, SQL — on y écrit ce qu'on veut, pas comment le calculer : le moteur de la base se charge de l'algorithme. C'est un changement de posture complet par rapport aux deux premières années de ce livre, et une expérience à vivre : la requête qui aurait demandé trente lignes de Python s'écrit en trois lignes de SQL. Ce chapitre pose le modèle — tables, clés primaires et étrangères, entités et associations — et les requêtes sur une table ; le chapitre 16 fera parler les tables entre elles.
15.2 Le vocabulaire des bases de données
15.2.1 Tables, attributs, enregistrements
Une table (ou relation) est un ensemble de données organisé en lignes et colonnes :
- chaque colonne est un attribut, doté d'un nom et d'un domaine — le type de ses valeurs : entier, flottant ou chaîne de caractères (nous nous en tiendrons à ces trois-là) ;
- chaque ligne est un enregistrement : une valeur par attribut, décrivant un objet ;
- le schéma de la table est la donnée de son nom et de la liste de ses attributs avec leurs domaines — la carte d'identité de la table, indépendante de son contenu.
On note le schéma sous la forme : livre(id_livre, titre, id_auteur, annee, pages).
Quatre tables. Les deux premières décrivent les objets :
| `auteur` | |||
|---|---|---|---|
| `id_auteur` | `nom` | `naissance` | |
| 1 | Verne | 1828 | |
| 2 | Austen | 1775 | |
| 3 | Tolstoï | 1828 | |
| 4 | Colette | 1873 |
| `livre` | ||||
|---|---|---|---|---|
| `id_livre` | `titre` | `id_auteur` | `annee` | `pages` |
| 1 | Vingt mille lieues sous les mers | 1 | 1870 | 448 |
| 2 | De la Terre à la Lune | 1 | 1865 | 320 |
| 3 | Orgueil et préjugés | 2 | 1813 | 480 |
| 4 | Guerre et paix | 3 | 1869 | 1536 |
| 5 | Anna Karénine | 3 | 1877 | 864 |
| 6 | Le Blé en herbe | 4 | 1923 | 192 |
| 7 | Michel Strogoff | 1 | 1876 | 512 |
| 8 | Les Vrilles de la vigne | 4 | 1908 | 256 |
Les deux suivantes décrivent les personnes et les liens :
| `usager` | ||
|---|---|---|
| `id_usager` | `nom` | `ville` |
| 1 | Alice | Lyon |
| 2 | Bruno | Paris |
| 3 | Chloé | Lyon |
| 4 | David | Nantes |
| 5 | Emma | Paris |
| 6 | Farid | Nantes |
| `emprunt` | ||
|---|---|---|
| `id_usager` | `id_livre` | `jour` |
| 1 | 1 | 2025-09-03 |
| 1 | 4 | 2025-09-10 |
| 2 | 3 | 2025-09-05 |
| 3 | 1 | 2025-09-12 |
| 4 | 2 | 2025-09-20 |
| 3 | 6 | 2025-10-01 |
| 3 | 5 | 2025-10-02 |
| 2 | 7 | 2025-10-05 |
| 5 | 4 | 2025-10-06 |
| 1 | 3 | 2025-10-08 |
Domaines : id_, naissance, annee, pages sont des entiers ; nom, titre, ville, jour des chaînes. Noter jour : une chaîne* au format AAAA-MM-JJ — choix délibéré, car l'ordre alphabétique de ces chaînes coïncide alors avec l'ordre chronologique (exercice 10) ; aucune notion de type « date » n'est au programme.
Le fichier de notes du chapitre 4 était déjà, sans le mot, une table — une seule. La force du modèle relationnel commence à plusieurs tables : l'auteur de Michel Strogoff n'est pas stocké comme la chaîne « Verne » dans la table livre, mais comme le numéro 1 renvoyant à la table auteur. Pourquoi cette indirection ? L'exercice 9 le montre en détruisant l'alternative : stocker le nom partout, c'est le stocker plusieurs fois — et un jour, le mettre à jour quelque part et pas ailleurs.
15.2.2 La clé primaire
Une clé primaire d'une table est un attribut — ou un groupe d'attributs — dont la valeur identifie de façon unique chaque enregistrement : deux lignes ne peuvent pas porter la même valeur de clé. Dans auteur, livre et usager, la clé primaire est l'identifiant numérique (id_auteur, id_livre, id_usager) — un numéro arbitraire attribué une fois pour toutes. Dans emprunt, aucun attribut seul ne suffit : c'est le triplet qui fait clé (Alice peut réemprunter le même livre un autre jour ; le couple sans la date ne serait donc pas une clé).
« Le nom identifie bien les gens » est l'erreur de conception classique : deux usagers peuvent s'appeler Martin, un auteur peut changer de nom de plume, un titre peut être réédité. Une clé primaire doit être stable et certaine — d'où la pratique universelle de l'identifiant numérique sans signification, qui n'a aucune raison de changer. Les attributs « parlants » décrivent ; la clé identifie.
15.3 Entités et associations
15.3.1 Modéliser avant de stocker
Concevoir une base commence comme au chapitre 12 (les graphes) : par une modélisation. On identifie les entités — les familles d'objets à décrire : auteurs, livres, usagers — et les associations qui les lient, classées par leur multiplicité :
- association : chaque objet de l'une est lié à au plus un objet de l'autre (un pays, une capitale) ;
- association : un objet de la première peut être lié à plusieurs de la seconde, mais pas l'inverse (un auteur écrit plusieurs livres ; chaque livre a ici un seul auteur) ;
- association : plusieurs des deux côtés (un usager emprunte plusieurs livres, un livre est emprunté par plusieurs usagers).
Aucun formalisme précis n'est exigé : ce schéma « sagittal » informel — des boîtes, des flèches, des multiplicités — suffit à discuter une conception avant d'écrire la moindre table. C'est le brouillon qui évite les bases mal nées.
15.3.2 Traduire les associations : la clé étrangère
Une clé étrangère est un attribut d'une table dont les valeurs sont des valeurs de clé primaire d'une autre table — un renvoi. L'attribut id_auteur de la table livre est une clé étrangère vers auteur : la ligne pointe vers l'auteur , Verne. C'est l'outil unique de traduction des associations :
- : une clé étrangère du côté — chaque livre porte le numéro de son auteur ;
- : une clé étrangère (de valeurs toutes distinctes) dans l'une des deux tables ;
- : aucune clé étrangère directe ne suffit — un livre ne peut pas porter le numéro de son emprunteur, ils sont plusieurs. La solution : séparer l'association en deux associations autour d'une table d'association — c'est exactement la table
emprunt, qui porte deux clés étrangères (id_usageretid_livre) et les attributs propres au lien (lejour).
Méthode : Concevoir un schéma relationnel
- Lister les entités (les noms du cahier des charges) ; une table par entité, avec un identifiant numérique en clé primaire.
- Lister les associations et leurs multiplicités — en interrogeant les deux sens : « un livre peut-il avoir plusieurs auteurs ? un auteur plusieurs livres ? ».
- Traduire : clé étrangère côté ; table d'association à deux clés étrangères (et y loger les attributs du lien : date, note, quantité).
- Contrôler en racontant des histoires : « Alice réemprunte le même livre » — le schéma le permet-il ? doit-il le permettre ?
15.4 Interroger une table : SELECT
15.4.1 Sélection, projection, renommage
La requête fondamentale de SQL combine deux gestes indépendants :
- la sélection (clause
WHERE) : garder les lignes qui satisfont une condition ; - la projection (liste après
SELECT) : garder certaines colonnes — ou des expressions calculées à partir d'elles, renommables avecAS.
SELECT titre, annee -- projection : deux colonnes
FROM livre
WHERE annee >= 1870; -- sélection : les lignes qui passent le filtre
Vingt mille lieues sous les mers | 1870
Anna Karénine | 1877
Le Blé en herbe | 1923
Michel Strogoff | 1876
Les Vrilles de la vigne | 1908
SELECT projette toutes les colonnes ; sans clause WHERE, toutes les lignes passent. Les conditions utilisent les comparateurs =, <> (différent), <, <=, >, >=, combinés par AND, OR, NOT ; les expressions, les opérateurs +, -, , /. Les chaînes s'écrivent entre apostrophes : ville = 'Lyon'.
Combien d'années chaque livre de la table a-t-il d'avance sur ses cent ans ?
SELECT titre, 2025 - annee AS age, pages / 100 AS centaines
FROM livre
WHERE pages >= 400 AND annee < 1875;
Vingt mille lieues sous les mers | 155 | 4
Orgueil et préjugés | 212 | 4
Guerre et paix | 156 | 15
La colonne calculée 2025 - annee n'existe dans aucune table : elle naît dans la requête, et AS la baptise. (L'ordre des lignes d'un résultat, en l'absence de la clause ORDER BY ci-dessous, n'est pas spécifié — une table est un ensemble de lignes, pas une liste : ne jamais compter sur l'ordre d'affichage spontané.)
SQL est déclaratif : la requête dit le résultat voulu, jamais la boucle qui le construit. L'équivalent Python — [(l.titre, 2025 - l.annee) for l in livres if l.pages >= 400] — décrit un parcours ; la requête décrit un contenu. C'est le moteur qui choisit l'algorithme, et il le choisit bien : toute l'algorithmique des chapitres précédents tourne sous le capot, invisible.
15.4.2 Trier, dédoublonner, tronquer
ORDER BY exprtrie le résultat (ASCcroissant par défaut,DESCdécroissant) ; plusieurs critères se séparent par des virgules — les suivants départagent les ex æquo du premier ;SELECT DISTINCTélimine les lignes identiques du résultat ;LIMITne garde que les premières lignes du résultat ;OFFSETen saute d'abord .
SELECT titre, pages FROM livre
ORDER BY pages DESC, titre ASC -- à pages égales, ordre alphabétique
LIMIT 3;
Guerre et paix | 1536
Anna Karénine | 864
Michel Strogoff | 512
« Les trois premiers livres » n'a de sens que si un ordre est imposé : LIMIT 3 sans ORDER BY renvoie trois lignes quelconques — souvent les mêmes d'une exécution à l'autre, jamais garanties. Même piège pour OFFSET (la pagination d'un site web : page 2 = LIMIT 10 OFFSET 10, mais sur un tri stable et total, sinon des lignes apparaissent sur deux pages ou aucune). Le réflexe : LIMIT et OFFSET ne voyagent qu'accompagnés d'ORDER BY — et d'un ORDER BY sans ex æquo si la pagination doit être exacte (ajouter la clé primaire en dernier critère y pourvoit).
SELECT DISTINCT ville FROM usager ORDER BY ville;
Lyon
Nantes
Paris
Six usagers, trois villes : DISTINCT a réduit les doublons. Il porte sur la ligne projetée entière : SELECT DISTINCT ville, nom ne dédoublonnerait rien ici (aucun couple répété). (Penser au coût : dédoublonner exige de regrouper ou trier les lignes — l'ensemble du chapitre 2 et le tri du chapitre 9 se cachent dans ce mot-clé ; rien n'est gratuit, même en déclaratif.)
15.4.3 Les opérateurs ensemblistes
Deux requêtes dont les résultats ont le même nombre de colonnes, de domaines compatibles, se combinent comme des ensembles : UNION (réunion, doublons éliminés), INTERSECT (intersection), EXCEPT (différence — les lignes de la première absentes de la seconde).
SELECT id_usager FROM usager -- tous les usagers...
EXCEPT
SELECT id_usager FROM emprunt; -- ...moins ceux qui ont emprunté
6
Farid (numéro ) est le seul usager à n'avoir jamais rien emprunté. Cette tournure « tous, sauf ceux qui… » est l'idiome EXCEPT par excellence — la négation d'existence, malaisée à dire autrement avec nos seuls outils.
Énumérer plusieurs tables dans le FROM produit toutes les combinaisons de lignes — le produit cartésien : FROM usager, livre engendre lignes, chaque usager apparié à chaque livre. Brut, il est rarement la réponse ; filtré par une condition d'égalité entre clés, il devient l'outil le plus important de SQL — la jointure, que l'exercice 8 fait naître et que le chapitre 16 consacre.
Le programme ne demande ni de créer ni de modifier des tables — la base est toujours fournie, et on l'interroge au travers d'un logiciel (console sqlite3, DB Browser, ou l'interface de l'épreuve). Toutes les requêtes de ces deux chapitres s'exécutent telles quelles sur la base mediatheque ; le lecteur est instamment invité à les rejouer et les varier — le SQL s'apprend les mains sur la table, au sens propre.
<i class="fa-solid fa-dumbbell mr-2" style="color:#2E7559"></i>15.5 Exercices résolus
Niveau (Application directe du cours)
Pour la base médiathèque : donner le schéma des quatre tables avec les domaines ; identifier la clé primaire de chacune ; expliquer pourquoi le couple ne peut pas servir de clé primaire à emprunt ; dire quelles clés étrangères existent et quelles associations elles traduisent.
Démonstration (Solution)
Schémas : , clé id_auteur ; , clé id_livre ; , clé id_usager ; , clé composée . Le couple sans le jour échouerait dès qu'un usager réemprunte le même livre à une autre date — deux lignes porteraient la même valeur de « clé », interdit. Clés étrangères : livre.id_auteur auteur (association « écrit ») ; emprunt.id_usager usager et emprunt.id_livre livre — les deux moitiés de l'association « emprunte ». (Lire un schéma avant de le requêter est le réflexe professionnel : les clés disent ce qui identifie, les clés étrangères dessinent le graphe des tables — au chapitre 16, les jointures suivront exactement ces flèches.)
Écrire les requêtes : (a) titres et années des livres de plus de pages ; (b) nom des usagers qui ne sont pas de Paris ; (c) titres des livres parus entre et ; (d) auteurs nés en .
Démonstration (Solution)
SELECT titre, annee FROM livre WHERE pages > 500; -- (a)
SELECT nom FROM usager WHERE NOT ville = 'Paris'; -- (b)
SELECT titre FROM livre WHERE annee >= 1860 AND annee <= 1880; -- (c)
SELECT nom FROM auteur WHERE naissance = 1828; -- (d)
(a) rend Guerre et paix, Anna Karénine, Michel Strogoff ; (b) Alice, Chloé, David, Farid (ville <> 'Paris' est équivalent) ; (c) les cinq livres de à ; (d) Verne et Tolstoï — nés la même année, et la base le sait. (Discipline du chapitre 1, transposée : avant d'exécuter, prédire le résultat à la main sur les petites tables — l'écart entre prédiction et exécution est, ici aussi, le meilleur des professeurs.)
Produire la table : titre, âge du livre en (colonne age) et épaisseur estimée en millimètres à pages par millimètre (colonne mm), pour les livres du dix-neuvième siècle (–), du plus épais au plus fin.
Démonstration (Solution)
SELECT titre, 2025 - annee AS age, pages / 16 AS mm
FROM livre
WHERE annee >= 1801 AND annee <= 1900
ORDER BY mm DESC;
Guerre et paix | 156 | 96
Anna Karénine | 148 | 54
Michel Strogoff | 149 | 32
Orgueil et préjugés | 212 | 30
Vingt mille lieues sous les mers | 155 | 28
De la Terre à la Lune | 160 | 20
Trois leçons en une requête : les colonnes calculées se trient comme les autres (le ORDER BY peut citer l'alias mm) ; la division pages / 16 entre entiers rend ici un entier (le programme passe outre ces subtilités — retenir seulement qu'un quotient affiché sans décimales doit éveiller l'attention) ; et la borne double s'écrit en deux comparaisons jointes par AND. (Remarquer Orgueil et préjugés : plus vieux mais moins épais que Michel Strogoff — le tri porte sur mm, pas sur l'âge, et les deux colonnes calculées vivent indépendamment.)
Niveau (Application avec raisonnement intermédiaire)
(a) Les deux livres les plus récents. (b) Le troisième livre le plus long (et lui seul). (c) Montrer sur la base pourquoi LIMIT 2 sans ORDER BY ne répond pas à la question (a) ; (d) que devient le classement (b) si deux livres avaient le même nombre de pages — et comment rendre le podium reproductible ?
Démonstration (Solution)
SELECT titre, annee FROM livre ORDER BY annee DESC LIMIT 2; -- (a)
SELECT titre, pages FROM livre
ORDER BY pages DESC LIMIT 1 OFFSET 2; -- (b)
(a) Le Blé en herbe () puis Les Vrilles de la vigne (). (b) OFFSET 2 saute les deux plus longs (Guerre et paix, Anna Karénine) : reste Michel Strogoff (). (c) Sans ORDER BY, le moteur rend deux lignes à sa convenance — typiquement les deux premières insérées, qui sont les livres de Verne de et : ni récents, ni faux, juste non spécifiés ; la requête est syntaxiquement correcte et sémantiquement vide — la pire combinaison (chapitre 1 : l'erreur silencieuse). (d) Avec des ex æquo, le troisième n'est pas défini : selon l'humeur du moteur, l'un ou l'autre sort. Remède : compléter le tri — ORDER BY pages DESC, id_livre — pour le rendre total ; le résultat devient reproductible (et la pagination web cohérente). (Tout classement présenté à un humain doit être total : c'est la stabilité des tris du chapitre 9, revue côté requête.)
(a) La liste des jours où au moins un emprunt a eu lieu. (b) La liste des couples (usager, jour) d'activité — Chloé venue deux jours distincts doit apparaître deux fois. (c) Combien de lignes rend SELECT DISTINCT id_usager FROM emprunt, et que signifie ce nombre ? (d) Pourquoi DISTINCT est-il inutile dans SELECT DISTINCT id_livre FROM livre ?
Démonstration (Solution)
SELECT DISTINCT jour FROM emprunt ORDER BY jour; -- (a) 10 emprunts, 10 jours
SELECT DISTINCT id_usager, jour FROM emprunt ORDER BY jour; -- (b)
SELECT DISTINCT id_usager FROM emprunt; -- (c) 5 lignes
(a) Dix emprunts à dix dates toutes différentes : le DISTINCT ne supprime rien ici, mais la requête est la bonne — elle resterait juste si deux emprunts partageaient un jour. (b) Le couple est dédoublonné en bloc : Chloé-01/10 et Chloé-02/10 sont deux couples distincts, tous deux conservés. (c) Cinq : le nombre d'usagers distincts ayant emprunté au moins une fois — tous sauf Farid ; c'est déjà un embryon de statistique (le chapitre 16 fera mieux avec COUNT). (d) id_livre est la clé primaire de livre : ses valeurs sont uniques par définition — le DISTINCT est sans effet, et l'écrire révèle qu'on n'a pas lu le schéma. (Règle d'hygiène : chaque DISTINCT doit pouvoir se justifier — « quelles lignes identiques peuvent apparaître, et pourquoi ? » ; un DISTINCT réflexe masque souvent une requête mal posée, et il coûte un dédoublonnage entier.)
(a) Les identifiants des livres écrits par Verne (id_auteur ) ou parus avant — sans OR. (b) Les identifiants des livres empruntés et écrits par Tolstoï (id_auteur ). (c) Les identifiants des livres jamais empruntés. Donner les résultats.
Démonstration (Solution)
SELECT id_livre FROM livre WHERE id_auteur = 1 -- (a)
UNION
SELECT id_livre FROM livre WHERE annee < 1820;
SELECT id_livre FROM emprunt -- (b)
INTERSECT
SELECT id_livre FROM livre WHERE id_auteur = 3;
SELECT id_livre FROM livre -- (c)
EXCEPT
SELECT id_livre FROM emprunt;
(a) — qu'un OR dans un seul WHERE aurait aussi donné : les ensemblistes deviennent indispensables quand les deux moitiés viennent de tables différentes, comme en (b) et (c). (b) emprunts Tolstoï : les deux ont circulé. (c) : Les Vrilles de la vigne dort sur l'étagère. (Les trois opérateurs dédoublonnent leur résultat — sémantique d'ensemble, fidèle à la théorie ; et (c) est le frère de l'EXCEPT du cours sur les usagers : « jamais » se dit toujours par différence.)
Un festival de cinéma veut sa base : des films (titre, durée, pays), des salles (nom, capacité), des séances (un film, une salle, un jour et une heure), et des spectateurs qui réservent des places pour des séances. Donner entités, associations avec multiplicités, schéma des tables avec clés primaires et étrangères — puis raconter deux histoires de contrôle (méthode du cours).
Démonstration (Solution)
Entités : film, salle, spectateur — et seance, entité née d'une association : une séance lie un film à une salle (deux associations : un film a plusieurs séances, une salle accueille plusieurs séances). La réservation est une association entre spectateur et seance table d'association. Schéma :
| table | schéma (clé primaire soulignée, : clé étrangère) |
|---|---|
| `film` | (`id_film`, titre, duree, pays) |
| `salle` | (`id_salle`, nom, capacite) |
| `seance` | (`id_seance`, `id_film` film, `id_salle` salle, jour, heure) |
| `reservation` | (`id_spectateur` , `id_seance` , nb_places) |
| `spectateur` | (`id_spectateur`, nom) |
Histoires de contrôle : « le même film passe deux fois le même jour dans deux salles » — deux lignes de seance, licite ✓ ; « un spectateur réserve deux fois la même séance » — interdit par la clé composée de reservation : il doit modifier nb_places, pas dupliquer la ligne — choix de conception qu'on aurait pu faire autrement (ajouter la date de réservation à la clé). (Noter le passage de seance du statut d'association au statut d'entité à part entière, avec son identifiant : dès qu'un lien porte ses propres attributs et que d'autres tables doivent le référencer, on le promeut — la modélisation est un art de décisions explicites, pas un algorithme.)
Niveau (Raisonnement subtil ou plusieurs étapes)
(a) Combien de lignes produit SELECT FROM livre, auteur ? Que contiennent-elles ? (b) Parmi ces lignes, lesquelles ont un sens* ? Écrire la requête qui ne garde qu'elles et affiche titre et nom d'auteur. (c) Vérifier le compte des lignes restantes, et expliquer pourquoi cette construction mérite un nom à elle.
Démonstration (Solution)
(a) lignes : chaque livre accolé à chaque auteur — Guerre et paix apparaît avec Verne, Austen, Tolstoï et Colette. Trois lignes sur quatre sont des accouplements absurdes. (b) Les lignes sensées sont celles où la clé étrangère retrouve sa clé primaire :
SELECT titre, nom
FROM livre, auteur
WHERE livre.id_auteur = auteur.id_auteur;
Vingt mille lieues sous les mers | Verne
De la Terre à la Lune | Verne
Orgueil et préjugés | Austen
Guerre et paix | Tolstoï
Anna Karénine | Tolstoï
Le Blé en herbe | Colette
Michel Strogoff | Verne
Les Vrilles de la vigne | Colette
(Les noms d'attributs ambigus se préfixent par leur table : livre.id_auteur.) (c) Huit lignes — une par livre, exactement : chaque livre a un auteur (association ), donc passe le filtre une fois et une seule. Cette construction — produit cartésien égalité de clés — recolle ce que la modélisation avait séparé : c'est la jointure, l'opération centrale du chapitre 16, qui lui offre sa syntaxe dédiée (JOIN … ON). (La voir naître du produit cartésien immunise contre sa magie apparente : une jointure mal conditionnée — clé oubliée dans le WHERE — redonne le produit cartésien, et ses , ou ses , lignes : le bogue SQL le plus célèbre.)
Un stagiaire propose de simplifier : une seule table — adieu la table auteur et la clé étrangère. Recenser méthodiquement ce que ce schéma casse : redondance, anomalies de mise à jour, d'insertion, de suppression — puis dire ce que le schéma du cours répond à chaque problème.
Démonstration (Solution)
Redondance : Verne et sa date de naissance sont recopiés sur ses trois livres — trois fois la même information, et l'espace gaspillé est le moindre des maux. Anomalie de mise à jour : corriger la naissance de Verne () exige de modifier trois lignes ; en oublier une, et la base se contredit elle-même — « quelle est la naissance de Verne ? » a deux réponses, et aucun moyen interne de trancher (c'est le bogue de cohérence du chapitre 10, version données : un état que rien ne devait permettre). Anomalie d'insertion : enregistrer un nouvel auteur dont la médiathèque n'a pas encore de livre est impossible — il n'a pas de ligne où exister. Anomalie de suppression : retirer Le Blé en herbe et Les Vrilles de la vigne fait disparaître Colette de la base — supprimer un livre a détruit un auteur. Le schéma du cours répond d'un coup aux quatre : l'information « Verne, » vit en un seul exemplaire dans auteur (une mise à jour, un point de vérité), les auteurs existent indépendamment de leurs livres, et la clé étrangère id_auteur relie sans recopier. (C'est le principe le plus fécond de la conception de bases — chaque fait en un seul endroit — et le lecteur l'a déjà rencontré sous d'autres habits : ne pas dupliquer un invariant dans deux variables (chapitre 10), ne pas stocker deux fois une arête… La théorie qui systématise ces idées, la « normalisation », attendra ; l'instinct, lui, est déjà là.)
(a) Démontrer : si deux dates sont écrites AAAA-MM-JJ (avec zéros de tête : 2025-09-03), alors l'ordre lexicographique des chaînes coïncide avec l'ordre chronologique. (b) Exhiber un contre-exemple au format français JJ/MM/AAAA, et un autre si l'on omet les zéros de tête. (c) En déduire les emprunts de septembre , triés du plus récent au plus ancien.
Démonstration (Solution)
(a) L'ordre lexicographique compare les chaînes caractère par caractère depuis la gauche (chapitre 2). Au format AAAA-MM-JJ, les champs sont rangés du plus significatif (l'année) au moins significatif (le jour), chacun à largeur fixe grâce aux zéros de tête : deux dates diffèrent d'abord là où elles diffèrent chronologiquement, et la comparaison de caractères y tranche comme la comparaison des nombres (les chiffres sont ordonnés dans la table des caractères). C'est l'argument de la numération positionnelle (chapitre 11), appliqué aux chaînes. (b) Au format français : '02/01/2026' < '15/06/2025' lexicographiquement (le 0 bat le 1) alors que janvier est après juin — le champ le moins significatif compare en premier. Sans zéros de tête : '2025-9-12' > '2025-10-02' car '9' > '1' — la largeur variable casse l'alignement positionnel. (c)
SELECT id_usager, id_livre, jour FROM emprunt
WHERE jour >= '2025-09-01' AND jour <= '2025-09-30'
ORDER BY jour DESC;
Cinq emprunts, du au septembre. La comparaison de chaînes jour >= '2025-09-01' est ici une comparaison de dates parce que le format s'y prête — toute la valeur du choix de conception fait en amont. (Ce format est une norme internationale, et la leçon dépasse SQL : choisir la représentation qui rend les opérations futures triviales — on a trié des dates sans aucune notion de date, comme le chapitre 2 trouvait les anagrammes sans aucune notion d'anagramme, par la seule clé canonique.)
- Modèle relationnel : tables (relations) d'enregistrements (lignes) à attributs (colonnes) typés par un domaine (entier, flottant, chaîne) ; le schéma = noms et domaines. Clé primaire : attribut(s) identifiant chaque ligne de façon unique — parfois composée (emprunt : usager livre jour) ; identifiant numérique stable plutôt qu'attribut « parlant ».
- Modéliser : entités tables ; associations , , ; clé étrangère = renvoi vers la clé primaire d'une autre table ; clé étrangère côté ; table d'association à deux clés étrangères (séparation en deux ), qui loge les attributs du lien ; contrôler le schéma en lui racontant des histoires.
- Un fait, un endroit : dupliquer l'information (nom d'auteur dans chaque livre) crée redondance et anomalies de mise à jour, d'insertion, de suppression — la clé étrangère relie sans recopier.
- SELECT : projection (colonnes, expressions calculées,
AS) sélection (WHEREavec=,<>,<,<=,>,>=,AND,OR,NOT) ; SQL est déclaratif — le résultat, pas l'algorithme ; sansORDER BY, l'ordre des lignes n'est pas spécifié. - Habillage :
ORDER BY(multi-critères,ASC/DESC; le rendre total — clé en dernier critère — pour podiums et pagination) ;DISTINCT(dédoublonne la ligne projetée entière ; chaque usage doit se justifier) ;LIMIT/OFFSET(jamais sansORDER BY). - Ensemblistes :
UNION,INTERSECT,EXCEPT(schémas compatibles, doublons éliminés) ; « jamais / sauf »EXCEPT. Produit cartésien (FROM, ) : toutes les combinaisons — filtré par l'égalité clé étrangère clé primaire, il devient la jointure (chapitre 16). - Dates : chaînes
AAAA-MM-JJ— champs du plus au moins significatif, largeur fixe ordre lexicographique ordre chronologique ; les autres formats trient faux.
15.6 Exercices d'entraînement
Cette banque d'exercices, classée par thème, couvre l'intégralité du chapitre. La numérotation prolonge celle des dix exercices résolus. Légende : application directe, raisonnement intermédiaire, approfondissement ; le symbole signale un classique incontournable. Sauf mention contraire, les requêtes portent sur la base médiathèque.
A. Schémas, clés, modélisation
- () Pour chaque table de la base : combien d'enregistrements, combien d'attributs, et le domaine de chacun ?
- () La table
empruntpourrait-elle prendre pour clé primaire ? Et ? Discuter selon les règles de la médiathèque (un même livre peut-il être emprunté deux fois le même jour ?). - ( ) Modéliser le lycée : élèves, classes (un élève, une classe), professeurs, matières, et « tel professeur enseigne telle matière à telle classe » — identifier la table d'association à trois clés étrangères.
- () Modéliser un réseau de bus : lignes, arrêts, et l'ordre des arrêts sur chaque ligne (l'attribut de lien
rangdans la table d'association) — puis raconter l'histoire de contrôle « la ligne 12 passe deux fois par le même arrêt ». - () Une association : chaque classe a un unique professeur principal, qui n'est principal que d'une classe. Donner deux traductions possibles (clé étrangère dans
classe, ou dansprofesseur) et discuter leurs mérites ; pourquoi une table d'association serait-elle excessive ?
B. Sélections et projections
- () Écrire : les livres de moins de pages ; les usagers dont le nom commence après « C » dans l'alphabet (
nom > 'C') ; les emprunts d'octobre. - () Que rend
SELECT titre FROM livre WHERE annee > 1900 OR pages > 1000 AND id_auteur = 3? Lever l'ambiguïté avec des parenthèses dans les deux lectures possibles, et dire laquelle SQL applique (ANDavantOR). - ( ) Le piège de la double inégalité : pourquoi
WHERE 1860 <= annee <= 1880ne fait-il pas ce qu'on croit dans la plupart des moteurs ? (Évaluer de gauche à droite : un booléen comparé à .) Écrire la forme correcte. - () Sur
SELECT DISTINCT ville FROM usager LIMIT 2: le résultat est-il bien défini ? Ajouter ce qui manque, et expliquer l'ordre d'application (dédoublonnage, tri, troncature). - () Reproduire
SELECT/WHERE/ORDER BY/LIMITen Python pur sur la tablelivrereprésentée en liste de dictionnaires (chapitre 2) — une fonction par clause, composables ; mesurer qu'on vient de réécrire un moteur de requêtes naïf.
C. Ensemblistes et produit cartésien
- () Les années où est né un auteur ou est paru un livre (
UNIONsur deux projections) ; les villes qui sont à la fois des villes d'usagers et… des noms d'auteurs (INTERSECT— résultat vide, et c'est une information). - ( ) Les usagers qui n'ont emprunté aucun livre de Verne : écrire avec
EXCEPT(tous les emprunteurs, moins les emprunteurs de Verne — ce second ensemble exige le chapitre 16 ou une liste d'identifiants en dur : faire avecid_livre IN (1, 2, 7)si le moteur le permet (INest hors liste officielle des opérateurs), sinon troisOR). - ()
UNIONélimine les doublons : construire deux requêtes dont les résultats se recouvrent et vérifier le compte ; chercher dans la documentation la variante qui les conserve (UNION ALL, hors programme mais éclairante). - () Combien de lignes produit
FROM emprunt, emprunt? EtFROM usager, livre, auteur? Donner la formule générale et calculer pour une base réelle ( clients, produits) — pourquoi le produit cartésien non filtré est interdit de production.
D. Études
- () Installer
sqlite3ou DB Browser, charger la base médiathèque (fournie), rejouer toutes les requêtes du chapitre et en inventer cinq nouvelles avec leurs résultats prédits puis vérifiés. - ( ) L'audit du format de dates : la base d'une association stocke
'3/9/2025','12/09/2025','2025-10-1'… Recenser tout ce qui casse (tris, comparaisons, doublons de format) et rédiger la règle de migration versAAAA-MM-JJ. - () Concevoir la base complète d'une bibliothèque réelle : exemplaires multiples d'un même livre (l'entité
exemplaireentrelivreetemprunt!), retours, prolongations, réservations — schéma, clés, diagramme sagittal, histoires de contrôle ; identifier ce que le schéma du cours simplifiait. - ( ) Dossier « du fichier à la base » : reprendre le fichier de mesures du chapitre 4, le découper en entités (stations, capteurs, relevés), justifier chaque clé, et écrire les cinq requêtes que le programme Python du chapitre 4 calculait à la main — rédigé selon les six compétences du programme.