Prérequis : requêtes de sélection , jointures , modification des données
À l’issue de ce TP, vous saurez :
- explorer une base de données SQLite à partir de son schéma ;
- formuler des requêtes de sélection, de filtrage et de tri ;
- utiliser les fonctions d’agrégation sur des données réelles ;
- écrire des jointures pour croiser les informations de plusieurs tables ;
- ajouter, modifier et supprimer des lignes ;
- tester les contraintes d’intégrité en tentant des opérations invalides.
Présentation
On travaille avec la base de données d’une bibliothèque de lycée, qui contient trois tables.
Le TP se fait dans Capytale (bouton « Version Capytale ») : la base y est chargée automatiquement et le résultat attendu figure sous la plupart des questions. On peut aussi télécharger la base bibliotheque.db
et l’ouvrir avec DB Browser for SQLite, dans l’onglet Exécuter le SQL.
- Dans Capytale, écrire une seule requête par cellule : seul le résultat de la dernière requête d’une cellule s’affiche.
- Les parties 1 à 4 interrogent la base sans la modifier. Les parties 5 et 6 la modifient : exécuter leurs requêtes une seule fois, dans l’ordre.
- Dans Capytale, les modifications disparaissent quand on recharge la page. Si la base n’est plus dans l’état attendu, cliquer sur Sauvegarder, recharger la page, puis exécuter de nouveau, dans l’ordre, les requêtes de la partie en cours.
Schéma relationnel
- Livres(id_livre, titre, auteur, genre, annee_publication)
- Eleves(id_eleve, nom, prenom, classe)
- Emprunts(id_emprunt, #id_livre, #id_eleve, date_emprunt, date_retour)
Les attributs soulignés forment la clé primaire ; ceux précédés de # sont des clés étrangères : Emprunts.id_livre référence Livres.id_livre et Emprunts.id_eleve référence Eleves.id_eleve.
Les dates sont écrites sous la forme 'AAAA-MM-JJ'. Tous les attributs sont déclarés NOT NULL, sauf date_retour, qui n’a pas de valeur (NULL) tant que le livre n’est pas rendu.
Partie 1 - Découverte de la base
Exécuter la requête
SELECT * FROM Livres;, puis afficher de même toute la tableEleves. Combien de livres la base contient-elle ? Combien d’élèves ?Afficher toute la table
Emprunts. Pour certains emprunts, la date de retour est absente : c’est la valeurNULL, que Capytale affichenull. Quels emprunts sont concernés ? Que cela signifie-t-il ?
Correction
-- 1.
SELECT * FROM Livres;
SELECT * FROM Eleves;
-- 2.
SELECT * FROM Emprunts;
1. La base contient 12 livres et 8 élèves.
2. Les emprunts 13, 14 et 15 n’ont pas de date de retour : les livres correspondants n’ont pas encore été rendus.
Partie 2 - Requêtes de sélection
Afficher le titre de tous les livres.
Indice
Une seule colonne, sans condition.
Afficher le titre et l’auteur de chaque livre, par ordre alphabétique des titres.
Indice
La clause
ORDER BYse place en fin de requête ; l’ordre croissant est l’ordre par défaut.Afficher les genres présents dans la base, chacun une seule fois.
Indice
Le mot-clé
DISTINCT, placé juste aprèsSELECT, supprime les doublons.Afficher le titre des livres publiés après 1950.
Indice
La condition porte sur l’attribut
annee_publication.Afficher le titre des livres de science-fiction, du plus ancien au plus récent.
Indice
Le genre est écrit
'Science-fiction'dans la base. La clauseORDER BYse place aprèsWHERE.Afficher le nom et le prénom des élèves de la classe Terminale A.
Indice
La classe s’écrit
'Terminale A', entre apostrophes.
Correction
-- 3.
SELECT titre FROM Livres;
-- 4.
SELECT titre, auteur FROM Livres
ORDER BY titre;
-- 5.
SELECT DISTINCT genre FROM Livres;
-- 6.
SELECT titre FROM Livres
WHERE annee_publication > 1950;
-- 7.
SELECT titre FROM Livres
WHERE genre = 'Science-fiction'
ORDER BY annee_publication;
-- 8.
SELECT nom, prenom FROM Eleves
WHERE classe = 'Terminale A';
Résultats attendus : 3. 12 titres ; 4. 12 lignes, de 1984 (George Orwell) à Les Misérables (Victor Hugo) ; 5. 7 genres (Conte, Roman, Aventure, Science-fiction, Fantasy, Dystopie, Poésie) ; 6. Fondation, Dune, Harry Potter à l’école des sorciers, Le Seigneur des anneaux et Fahrenheit 451 ; 7. Fondation (1951) puis Dune (1965) ; 8. Martin Alice et Bernard Lucas.
Partie 3 - Fonctions d’agrégation
Retrouver par une requête le nombre de livres obtenu à la question 1.
Indice
COUNT(*)compte les lignes d’une table.Afficher l’année de publication du livre le plus ancien.
Indice
Le livre le plus ancien est celui dont l’année de publication est la plus petite : une fonction d’agrégation la donne directement.
Compter les genres différents représentés dans la base. L’expression
COUNT(DISTINCT genre)compte chaque valeur différente degenreune seule fois.Indice
Comparer le résultat avec celui de la question 5.
Compter les emprunts enregistrés dans la base.
Indice
Chaque ligne de la table
Empruntscorrespond à un emprunt.Compter les emprunts dont le livre n’a pas encore été rendu. Une valeur absente se teste avec
IS NULL, jamais avec= NULL.Indice
La condition porte sur l’attribut
date_retour.
Correction
-- 9.
SELECT COUNT(*) FROM Livres;
-- 10.
SELECT MIN(annee_publication) FROM Livres;
-- 11.
SELECT COUNT(DISTINCT genre) FROM Livres;
-- 12.
SELECT COUNT(*) FROM Emprunts;
-- 13.
SELECT COUNT(*) FROM Emprunts
WHERE date_retour IS NULL;
Résultats attendus : 9. 12 ; 10. 1844 ; 11. 7 ; 12. 15 ; 13. 3.
Partie 4 - Jointures
Afficher, pour chaque emprunt, le prénom et le nom de l’élève ainsi que la date de l’emprunt, du plus ancien au plus récent.
Indice
Les dates sont dans
Emprunts, les noms dansEleves: joindre ces deux tables sur l’attribut communid_eleve.Afficher le titre de chaque livre emprunté, avec le prénom de l’emprunteur et la date de l’emprunt.
Indice
Trois tables : relier
EmpruntsàLivresparid_livre, puis àElevesparid_eleve, avec deuxJOIN.Afficher le titre des livres qui n’ont pas encore été rendus, avec le prénom et le nom de l’élève concerné.
Indice
Reprendre la jointure de la question 15 et ajouter la condition de la question 13.
Afficher le titre et la date d’emprunt des livres empruntés par Alice Martin.
Indice
Jointure sur les trois tables, avec une condition sur le prénom et une condition sur le nom, reliées par
AND.Afficher, pour chaque élève, son prénom, son nom et son nombre d’emprunts, du plus grand nombre au plus petit, puis par ordre alphabétique des noms en cas d’égalité.
Indice
GROUP BY Eleves.id_eleveregroupe les lignes d’un même élève :COUNT(*)compte alors les emprunts de chaque groupe. Nommer ce nombre avecASpermet de trier sur lui. Voir le complément surGROUP BYdu cours sur les requêtes de sélection.Afficher, pour chaque classe, le nombre d’emprunts réalisés par ses élèves.
Indice
Même jointure qu’à la question 18, avec un regroupement par
classe.Afficher, sans doublon, le prénom des élèves qui ont emprunté au moins un livre de science-fiction.
Indice
Jointure sur les trois tables, condition sur le genre, et
DISTINCTaprèsSELECT.Afficher le titre de chaque livre emprunté et son nombre d’emprunts, du plus emprunté au moins emprunté. Quel est le livre le plus emprunté ?
Indice
Regrouper les emprunts par livre avec
GROUP BY Livres.id_livre, compter, puis trier sur ce nombre.Question facultative. Afficher le titre des livres qui n’ont jamais été empruntés. Un tel livre n’apparaît dans aucune ligne de la table
Emprunts. La conditionid_livre NOT IN (SELECT id_livre FROM Emprunts)est vraie quand l’identifiant du livre ne figure pas dans le résultat de la requête entre parenthèses, appelée sous-requête.Indice
La requête principale porte sur la table
Livres; la condition ci-dessus se place aprèsWHERE.
Correction
-- 14.
SELECT Eleves.prenom, Eleves.nom, Emprunts.date_emprunt
FROM Emprunts
JOIN Eleves ON Emprunts.id_eleve = Eleves.id_eleve
ORDER BY Emprunts.date_emprunt;
-- 15.
SELECT Livres.titre, Eleves.prenom, Emprunts.date_emprunt
FROM Emprunts
JOIN Livres ON Emprunts.id_livre = Livres.id_livre
JOIN Eleves ON Emprunts.id_eleve = Eleves.id_eleve;
-- 16.
SELECT Livres.titre, Eleves.prenom, Eleves.nom
FROM Emprunts
JOIN Livres ON Emprunts.id_livre = Livres.id_livre
JOIN Eleves ON Emprunts.id_eleve = Eleves.id_eleve
WHERE Emprunts.date_retour IS NULL;
-- 17.
SELECT Livres.titre, Emprunts.date_emprunt
FROM Emprunts
JOIN Livres ON Emprunts.id_livre = Livres.id_livre
JOIN Eleves ON Emprunts.id_eleve = Eleves.id_eleve
WHERE Eleves.prenom = 'Alice' AND Eleves.nom = 'Martin';
-- 18.
SELECT Eleves.prenom, Eleves.nom, COUNT(*) AS nb
FROM Emprunts
JOIN Eleves ON Emprunts.id_eleve = Eleves.id_eleve
GROUP BY Eleves.id_eleve
ORDER BY nb DESC, Eleves.nom;
-- 19.
SELECT Eleves.classe, COUNT(*) AS nb
FROM Emprunts
JOIN Eleves ON Emprunts.id_eleve = Eleves.id_eleve
GROUP BY Eleves.classe;
-- 20.
SELECT DISTINCT Eleves.prenom
FROM Emprunts
JOIN Livres ON Emprunts.id_livre = Livres.id_livre
JOIN Eleves ON Emprunts.id_eleve = Eleves.id_eleve
WHERE Livres.genre = 'Science-fiction';
-- 21.
SELECT Livres.titre, COUNT(*) AS nb
FROM Emprunts
JOIN Livres ON Emprunts.id_livre = Livres.id_livre
GROUP BY Livres.id_livre
ORDER BY nb DESC;
-- 22.
SELECT titre FROM Livres
WHERE id_livre NOT IN (SELECT id_livre FROM Emprunts);
Résultats attendus : 14. 15 lignes, d’Alice Martin (2025-09-05) à Emma Dubois (2025-12-15) ; 15. 15 lignes ; 16. Harry Potter à l’école des sorciers (Alice Martin), Fondation (Léa Petit) et 1984 (Emma Dubois) ; 17. Le Petit Prince (2025-09-05), L’Étranger (2025-09-20) et Harry Potter à l’école des sorciers (2025-12-10) ; 18. Emma Dubois et Alice Martin 3, Lucas Bernard, Léa Petit et Noah Thomas 2, Raphaël Laurent, Chloé Moreau et Hugo Robert 1 ; 19. Première A 3, Première B 2, Terminale A 5, Terminale B 5 ; 20. Lucas et Léa ; 21. 11 lignes, les quatre premières avec 2 emprunts ; 22. Les Fleurs du mal.
21. Quatre livres sont à égalité avec 2 emprunts : Le Petit Prince, Fondation, Harry Potter à l’école des sorciers et 1984. Il n’y a donc pas un seul livre le plus emprunté ; ajouter LIMIT 1 à la requête n’afficherait que l’un des quatre.
Partie 5 - Modification des données
Un nouveau livre arrive : Le Meilleur des mondes, d’Aldous Huxley, genre Dystopie, publié en 1932. L’ajouter avec l’identifiant 13, puis afficher la ligne de ce livre.
Indice
Les valeurs s’écrivent dans l’ordre des colonnes du schéma, les chaînes entre apostrophes.
Le 5 janvier 2026, Raphaël Laurent (identifiant 8) emprunte ce livre. Enregistrer cet emprunt avec l’identifiant 16, puis afficher, grâce à une jointure, le titre du livre, le prénom de l’emprunteur et la date de l’emprunt 16.
Indice
La date s’écrit
'2026-01-05'et l’absence de date de retourNULL. Pour l’affichage, reprendre la jointure de la question 15.Le 12 janvier 2026, Emma Dubois rend le livre 1984 qu’elle avait emprunté le 15 décembre 2025. Trouver l’identifiant de cet emprunt, enregistrer la date de retour, puis afficher la ligne de l’emprunt.
Indice
Le
SELECTqui donne l’identifiant reprend la jointure de la question 15, avec trois conditions. L’UPDATEdésigne ensuite l’emprunt par sa clé primaire.a. Le livre Les Fleurs du mal (identifiant 12) est retiré de la bibliothèque. Le supprimer de la table
Livres, puis compter les livres de la base.Indice
DELETE FROMsuivi d’une condition sur la clé primaire.b. Le livre 1984 (identifiant 9), trop abîmé, doit aussi être retiré. Essayer de le supprimer. Quel message obtient-on ? Pourquoi ?
Indice
Des lignes de la table
Empruntsdésignent-elles le livre 9 ?
Correction
-- 23.
INSERT INTO Livres (id_livre, titre, auteur, genre, annee_publication)
VALUES (13, 'Le Meilleur des mondes', 'Aldous Huxley', 'Dystopie', 1932);
SELECT * FROM Livres WHERE id_livre = 13;
-- 24.
INSERT INTO Emprunts (id_emprunt, id_livre, id_eleve, date_emprunt, date_retour)
VALUES (16, 13, 8, '2026-01-05', NULL);
SELECT Livres.titre, Eleves.prenom, Emprunts.date_emprunt
FROM Emprunts
JOIN Livres ON Emprunts.id_livre = Livres.id_livre
JOIN Eleves ON Emprunts.id_eleve = Eleves.id_eleve
WHERE Emprunts.id_emprunt = 16;
-- 25.
SELECT Emprunts.id_emprunt
FROM Emprunts
JOIN Livres ON Emprunts.id_livre = Livres.id_livre
JOIN Eleves ON Emprunts.id_eleve = Eleves.id_eleve
WHERE Livres.titre = '1984' AND Eleves.nom = 'Dubois'
AND Emprunts.date_emprunt = '2025-12-15';
UPDATE Emprunts
SET date_retour = '2026-01-12'
WHERE id_emprunt = 15;
SELECT * FROM Emprunts WHERE id_emprunt = 15;
-- 26. a.
DELETE FROM Livres
WHERE id_livre = 12;
SELECT COUNT(*) FROM Livres;
-- 26. b.
DELETE FROM Livres
WHERE id_livre = 9;
Résultats attendus : 23. la ligne (13, Le Meilleur des mondes, Aldous Huxley, Dystopie, 1932) ; 24. Le Meilleur des mondes, Raphaël, 2026-01-05 ; 25. l’identifiant 15, puis la ligne (15, 9, 3, 2025-12-15, 2026-01-12) ; 26. a. 12 livres.
26. b. Le SGBD refuse la requête avec le message FOREIGN KEY constraint failed. Les emprunts 5 et 15 référencent le livre 9 : le supprimer laisserait des clés étrangères sans correspondance. Pour retirer ce livre, il faudrait d’abord supprimer ces deux emprunts, ce qui effacerait l’historique des prêts. Les Fleurs du mal, jamais emprunté, a pu être supprimé sans difficulté.
Partie 6 - Contraintes d’intégrité
Les requêtes des questions 27 à 29 sont données : les exécuter, puis lire le message du SGBD.
Pourquoi la requête suivante est-elle refusée ?
INSERT INTO Eleves VALUES (1, 'Dupont', 'Marie', 'Terminale C');Indice
Un élève d’identifiant 1 existe-t-il déjà ?
Pourquoi la requête suivante est-elle refusée ?
INSERT INTO Emprunts VALUES (17, 99, 1, '2026-01-10', NULL);Indice
Le livre 99 existe-t-il dans la table
Livres?Pourquoi la requête suivante est-elle refusée ? Que garantit la contrainte
NOT NULLposée sur l’attributtitre?INSERT INTO Livres VALUES (14, NULL, 'Voltaire', 'Conte', 1759);Indice
Quelle valeur la requête donne-t-elle au titre ?
Le SGBD SQLite ne vérifie les clés étrangères que si on le lui demande, à chaque connexion, avec la commande
PRAGMA foreign_keys = ON;. Dans Capytale, la base est chargée avec cette commande, et DB Browser l’exécute lui-même à l’ouverture de chaque base ; dans le TP sur Python et les bases de données, il faudra l’exécuter soi-même.a. Désactiver la vérification avec
PRAGMA foreign_keys = OFF;, puis exécuter de nouveau l’insertion de la question 28. Que se passe-t-il ?b. Compter les emprunts d’Alice Martin (identifiant 1) dans la table
Emprunts, puis afficher, grâce à une jointure avec la tableLivres, le titre des livres qu’elle a empruntés. Pourquoi les deux résultats ne concordent-ils pas ?Indice
L’emprunt 17 désigne le livre 99 : quelle ligne de
Livresla jointure peut-elle lui associer ?c. Supprimer l’emprunt 17, réactiver la vérification avec
PRAGMA foreign_keys = ON;, puis la contrôler avecPRAGMA foreign_keys;, qui affiche 1 quand elle est active.
Correction
-- 27.
INSERT INTO Eleves VALUES (1, 'Dupont', 'Marie', 'Terminale C');
-- 28.
INSERT INTO Emprunts VALUES (17, 99, 1, '2026-01-10', NULL);
-- 29.
INSERT INTO Livres VALUES (14, NULL, 'Voltaire', 'Conte', 1759);
-- 30. a.
PRAGMA foreign_keys = OFF;
INSERT INTO Emprunts VALUES (17, 99, 1, '2026-01-10', NULL);
-- 30. b.
SELECT COUNT(*) FROM Emprunts WHERE id_eleve = 1;
SELECT Livres.titre
FROM Emprunts
JOIN Livres ON Emprunts.id_livre = Livres.id_livre
WHERE Emprunts.id_eleve = 1;
-- 30. c.
DELETE FROM Emprunts
WHERE id_emprunt = 17;
PRAGMA foreign_keys = ON;
PRAGMA foreign_keys;
Résultats attendus : 30. b. 4 emprunts, mais 3 titres : Le Petit Prince, L’Étranger et Harry Potter à l’école des sorciers ; 30. c. 1.
27. Le SGBD refuse la requête avec le message UNIQUE constraint failed: Eleves.id_eleve. L’identifiant 1 est déjà celui d’Alice Martin : une clé primaire doit être unique.
28. Le SGBD refuse la requête avec le message FOREIGN KEY constraint failed. Aucun livre n’a l’identifiant 99 : la clé étrangère id_livre doit désigner un livre qui existe dans la table Livres.
29. Le SGBD refuse la requête avec le message NOT NULL constraint failed: Livres.titre. La requête ne donne pas de titre au livre. La contrainte NOT NULL garantit que chaque livre a un titre : une ligne sans titre est refusée.
30. a. L’insertion est acceptée sans message : sans vérification, la base contient désormais un emprunt qui ne correspond à aucun livre.
30. b. La table compte 4 emprunts d’Alice Martin, mais la jointure n’affiche que 3 titres : aucune ligne de Livres ne correspond au livre 99 de l’emprunt 17. Une clé étrangère sans correspondance rend la base incohérente ; la vérification des clés étrangères l’empêche.