VA
Comment lire ce livre ? Qui sommes nous ?
I. PAGES WEB
II. SERVEUR WEB
III. BASES DE DONNEES
IV. INTERACTION FRONT-BACK
Annexe I. ENSEIGNEMENT NSI
Annexe II. LES FRAMEWORKS

Chapitre 7 - Les requêtes

La manipulation des données se fait à l'aide de requêtes qui ciblent une ou plusieurs tables de la base de données et précisent les opérations à effectuer pour récupérer les données d'intérêt. Le moteur de la base de données interprète les requêtes pour récupérer rapidement les données et fournir le résultat.

Nous présentons dans ce chapitre la façon dont sont conçues les requêtes. Nous présentons d'abord les requêtes en utilisant l'algèbre relationnelle ce qui permet de bien comprendre leur sémantique. Pour chaque type de requêtes nous expliquons ensuite comment les coder en SQL pour qu'elles soient interprétables par les moteurs des bases de données.

Les requêtes mono-table

Une requête mono-table ne cible qu'une seule table. L'objectif est de récupérer certaines données contenues dans la table ciblée, ou de vérifier la non-existence de ces données.

Il existe deux opérations principales pour récupérer des données contenues dans une table : la projection (π) et la sélection (σ).

Définition
La projection, notée π en algèbre relationnelle, consiste à récupérer un sous-ensemble des attributs d'une table. L'opération de projection est notée πA1, ..., An(R) où R est la table ciblée et A1, ..., An sont les attributs que l'on souhaite récupérer.

En prenant l'exemple de la table des auteurs, la requête πprenom,nom(auteur) effectue une projection des attributs prenom et nom. Le résultat de l'exécution de cette requête donne le Tableau 7.1.

Prénom Nom
Etienne Carjat
Robert Doisneau
Yann Arthus-Bertrand

Tableau 7.1 : Résultat de la requête de projection.

En SQL, la projection est réalisée en utilisant le mot clé SELECT en précisant la table ciblée à l'aide du mot clé FROM. Le Code 7.1 présente la commande SQL permettant de récupérer les attributs prenom et nom de la table auteurs.

SELECT prenom, nom FROM auteurs;

Code 7.1 : Requête SQL de projection.

Définition
La sélection, notée σ en algèbre relationnelle, consiste à récupérer un sous-ensemble des lignes d'une table. Les lignes récupérées sont celles qui vérifient un critère de sélection. L'opération de sélection est notée σφ(R) où R est la table ciblée et φ est le critère de sélection.

En prenant l'exemple de la table des auteurs, la requête σnom='Carjat'(auteurs) effectue une sélection des lignes dont le nom est égal à Carjat. Le résultat de l'exécution de cette requête donne le nuplet (1, 'Etienne', 'Carjat').

En SQL, la sélection est réalisée en ajoutant une clause WHERE contenant le critère de sélection. Notons que la clause WHERE doit être utilisée avec la projection SELECT. On peut notamment utiliser le caractère "*" pour récupérer toutes les colonnes. Le Code 7.2 présente la commande SQL permettant de récupérer les lignes dont le nom est égal à Carjat.

SELECT * FROM auteurs WHERE nom='Carjat';

Code 7.2 : Requête SQL de sélection.

SQL propose différents opérateurs pour exprimer les critères de sélection (=, <=, IN, LIKE, etc.). Il est de plus possible de combiner les critères de sélection en utilisant les opérateurs logiques AND, OR et NOT.

Les requêtes multi-tables

Les requêtes multi-tables récupèrent des données en exploitant les associations entre plusieurs tables. Elles sont utilisées lorsque les informations recherchées se trouvent dans des tables différentes et qu'il est nécessaire de les combiner.

L'opération de base pour réaliser des requêtes multi-tables est le produit cartésien. Cette opération combine les lignes de deux tables en créant une nouvelle table.

Définition
Le produit cartésien, noté X en algèbre relationnelle, combine les lignes de deux tables dans une nouvelle table. Il est représenté par l'opération R X S, où R et S sont les tables à combiner. La table résultat contient tous les attributs de R et tous les attributs de S.

En SQL, il existe deux façons d'exprimer le produit cartésien. La première consiste à simplement identifier les tables dans la clause FROM. La deuxième consiste à utiliser la clause CROSS JOIN. Le Code 7.3 présente deux exemples de requêtes permettant de faire un produit cartésien entre les auteurs et les images.

SELECT * FROM auteurs, images;
SELECT * FROM auteurs CROSS JOIN images;

Code 7.3 : Requête SQL de produit cartésien.

Le résultat de l'exécution du produit cartésien auteurs X images est présenté dans le Tableau 7.2 . On voit que toutes les lignes des deux tables sont combinées dans le résultat.

Id Prénom Nom Id Nom Année auteur
1 Etienne Carjat 1 Charles... 1863 1
1 Etienne Carjat 2 Le baiser... 1950 2
1 Etienne Carjat 3 Les frères 1936 2
1 Etienne Carjat 4 Coeur... 1992 3
2 Robert Doisneau 1 Charles... 1863 1
2 Robert Doisneau 2 Le baiser... 1950 2
2 Robert Doisneau 3 Les frères 1936 2
2 Robert Doisneau 4 Coeur... 1992 3
3 Yann Arthus-Bertrand 1 Charles... 1863 1
3 Yann Arthus-Bertrand 2 Le baiser... 1950 2
3 Yann Arthus-Bertrand 3 Les frères 1936 2
3 Yann Arthus-Bertrand 4 Coeur... 1992 3

Tableau 7.2 : Résultat de la requête auteurs X images.

Notons que le produit cartésien permet d'obtenir une table résultat contenant des colonnes qui ont le même nom. En effet, le produit cartésien auteurs X images donne une table avec les attributs id, prenom et nom venant de la table auteurs mais aussi id, nom, annee et auteurs venant de la table images. On a donc deux colonnes nommées id et deux colonnes nommées nom. Pour faire face à ce problème il faut utiliser l'opération de renommage des attributs (ρ).

Définition
Le renommage des attributs, noté ρ en algèbre relationnelle, change les noms des attributs d'une table. L'opération de renommage est représentée par ρA1 -> B1, ..., An -> Bn(R), où R est la table à renommer, A1 ... An sont les attributs à renommer et B1, ..., Bn sont les nouveaux noms des attributs.

Ainsi, en reprenant notre exemple, la requête auteurs X ρid -> pid, nom -> pnom, auteur -> phtg_id(images) effectue un produit cartésien entre les tables auteurs et images en renommant les attributs id, nom et auteur de la table images par pid, pnom et phtg_id. Le résultat de l'exécution de cette requête donne le Tableau 7.3.

id prénom nom pid pnom année phtg_id
1 Etienne Carjat 1 Charles ... 1863 1
1 Etienne Carjat 2 Le baiser ... 1950 2
1 Etienne Carjat 3 Les frères 1936 2
1 Etienne Carjat 4 Coeur ... 1992 3
2 Robert Doisneau 1 Charles ... 1863 1
2 Robert Doisneau 2 Le baiser ... 1950 2
2 Robert Doisneau 3 Les frères 1936 2
2 Robert Doisneau 4 Coeur ... 1992 3
3 Yann Arthus-Bertrand 1 Charles ... 1863 1
3 Yann Arthus-Bertrand 2 Le baiser ... 1950 2
3 Yann Arthus-Bertrand 3 Les frères 1936 2
3 Yann Arthus-Bertrand 4 Coeur ... 1992 3

Tableau 7.3 : Résultat de la requête de produit cartésien avec renommage.

En SQL, le renommage des attributs se fait en utilisant la clause AS. Le Code 7.4 présente l'expression du produit cartésien entre les auteurs et les images en utilisant le renommage des attributs.

SELECT p.id, p.prenom, p.nom, i.id AS pid, i.nom AS pnom, i.annee, i.auteur AS phtg\_id FROM auteurs AS p, images AS i;

Code 7.4 : Requête SQL avec renommage des attributs.

Le produit cartésien combine deux tables mais il ajoute des lignes qui n'ont pas beaucoup de sens surtout lorsque les deux tables sont associées par une clé étrangère. Par exemple, la deuxième ligne du produit cartésien auteurs X images associe l'auteur Etienne Cargat à l'image le baiser de l'hôtel de ville alors que la clé étrangère précise que c'est Robert Doisneau qui a pris cette image (voir Tableau 7.3). Pour éviter ce problème il faut supprimer du résultat du produit cartésien toutes les entités qui ont été mal associées. Cela peut se faire en utilisant l'opération mono-table de sélection (σ) et ne sélectionner que les lignes pour lesquelles la clé étrangère est bien égale à la clé primaire (id du auteur dans notre cas). Ainsi la requête σimages.auteur=auteur.id(auteurs X images) ne garde que les entités qui sont cohérentes vis à vis des valeurs des clés primaires et étrangères.

Cette utilisation produit cartésien suivie d'une sélection est très souvent utilisée pour manipuler les données des tables qui sont liées par des clés étrangères. C'est ce qu'on appelle une opération de jointure. Une jointure combine les données de plusieurs tables en se basant sur une relation d'égalité entre les clés primaires et étrangères. L'opération de jointure interne est représentée par le symbole en algèbre relationnelle. Elle effectue le produit cartésien des tables et filtre les lignes qui satisfont la condition d'égalité spécifiée.

Définition
Une jointure est une opération qui fait un produit cartésien entre deux tables et qui ne garde que les lignes qui respectent une contrainte d'égalité entre deux colonnes (une colonne dans chacune des tables). L'opérateur de jointure interne est noté R ⨝φ S en algèbre relationnelle, où R et S sont les deux relations à combiner et où φ précise la contrainte d'égalité.

En SQL, la jointure interne est obtenue en utilisant la clause JOIN avec la clause ON qui permet de spécifier la condition sur les clés. Le Code 7.5 présente la requête SQL qui permet de faire la jointure interne entre les auteurs et les images. Le résultat de cette requête est présenté dans le Tableau 7.4 . Nous avons montré l'effet de l'opérateur de sélection en rayant les lignes supprimées. On voit bien qu'il ne reste que les lignes pour lesquelles la clé étrangère (colonne auteur) correspond bien à la clé primaire (première colonne Id).

SELECT * FROM auteurs JOIN images ON auteurs.id = images.auteur;

Code 7.5 : Requête SQL de jointure interne.

Id Prénom Nom Id Nom Année auteur
1 Etienne Carjat 1 Charles... 1863 1
1 Etienne Carjat 2 Le baiser... 1950 2
1 Etienne Carjat 3 Les frères 1936 2
1 Etienne Carjat 4 Coeur... 1992 3
2 Robert Doisneau 1 Charles... 1863 1
2 Robert Doisneau 2 Le baiser... 1950 2
2 Robert Doisneau 3 Les frères 1936 2
2 Robert Doisneau 4 Coeur... 1992 3
3 Yann Arthus-Bertrand 1 Charles... 1863 1
3 Yann Arthus-Bertrand 2 Le baiser... 1950 2
3 Yann Arthus-Bertrand 3 Les frères 1936 2
3 Yann Arthus-Bertrand 4 Coeur... 1992 3

Tableau 7.4 : Résultat de la requête auteurs ⨝auteurs.id = images.auteur images.

Le Code 7.6 montre comment on peut faire une jointure et renommer les attributs des tables pour éviter les conflits de noms. On renomme l'attribut nom de la table images en titre et on ne garde que le nom et le prénom de l'auteur ainsi que l'année de l'image dans le résultat. Le résultat de cette requête est présenté dans le Tableau 7.5.

SELECT auteurs.prenom, auteurs.nom, images.nom as titre, images.annee FROM auteurs JOIN images ON auteurs.id = images.auteur;

Code 7.6 : Requête SQL de jointure avec renommage des attributs et projection pour simplifier le résultat.

prénom nom titre année
Etienne Carjat Charles ... 1863
Robert Doisneau Le baiser ... 1950
Robert Doisneau Les frères 1936
Yann Arthus-Bertrand Coeur ... 1992

Tableau 7.5 : Résultat d'une jointure avec renommage et projection des attributs.

Résultats des requêtes

Les requêtes SQL retournent des tables qui nécessitent souvent d'autres traitements (tri, calcul de moyenne, etc.) pour obtenir le résultat souhaité. Heureusement, SQL propose plusieurs opérations pour affiner les résultats. Nous présentons ici quelques unes de ces opérations.

La première opération est celle qui consiste à trier les résultats d'une requêtes en fonction d'un ou de plusieurs attributs. Cela se fait directement dans la requête en utilisant la clause ORDER BY. Par défaut, le classement est ascendant (du plus petit au plus grand). Il est possible de préciser l'ordre en utilisant la clause ASC ou DESC. Par exemple, le Code 7.7 présente la requête SQL qui permet de trier les résultats par année.

SELECT * FROM auteurs JOIN images ON auteurs.id = images.auteur ORDER BY annee;

Code 7.7 : Requête SQL de jointure avec tri.

Une autre opération permet de limiter la taille de la table résultat. Cela se fait directement dans la requête en utilisant la clause LIMIT. Par exemple, le Code 7.8 présente la requête SQL qui permet de trier les résultats par année et de ne retourner que le premier éléments.

SELECT * FROM auteurs JOIN images ON auteurs.id = images.auteur ORDER BY annee LIMIT 1;

Code 7.8 : Requête SQL de jointure avec tri et limite.

Enfin, les fonctions d'agrégation en SQL sont des opérations puissantes qui calculent des valeurs agrégées à partir de plusieurs entitées. Par exemple, la fonction COUNT() est utilisée pour compter le nombre de lignes qui correspondent à un critère spécifié. Pour illustrer son utilisation, considérons le cas où nous souhaitons déterminer le nombre total d'auteurs dans notre base de données. Dans ce cas, nous utiliserions COUNT(*) dans notre requête SQL (voir Code 7.9).

SELECT COUNT(*) FROM auteurs;

Code 7.9 : Requête SQL de comptage.

La fonction AVG() calcule la moyenne d'une colonne numérique spécifique. Supposons que nous voulons connaître l'âge moyen des auteurs. Dans ce cas, nous utiliserions AVG(age) (voir code 7.10).

SELECT AVG(age) FROM auteurs;

Code 7.10 : Requête SQL de calcul de moyenne.

SQL propose bien d'autres fonctions comme par exemple SUM() qui additionne toutes les valeurs d'une colonne numérique, MIN() qui est utilisée pour trouver la plus petite valeur d'une colonne spécifique ou MAX() qui est utilisée pour trouver la plus grande valeur d'une colonne spécifique.

Les requêtes imbriquées

Les résultats d'une requête SQL peuvent être utilisés dans d'autres requêtes, on appelle cela des requêtes imbriquées.

Un exemple assez classique est quand une première requête trouve la valeur maximum MAX() et qu'une deuxième requête cherche la ligne qui contient cette valeur maximum. Le Code 7.11 présente la requête SQL qui permet de trouver l'auteur qui a créé l'image qui a le plus de likes (on considère que la table des images contient une colonne pour stocker le nombre de likes).

SELECT * FROM auteurs JOIN images ON auteurs.id = images.auteur WHERE likes = (SELECT MAX(likes) FROM images);

Code 7.11 : Requête SQL de jointure avec requête imbriquée qui utilise la fonction MAX().

Une autre possibilité est d'utiliser les quantificateurs ALL et ANY. Le quantificateur ALL vérifie qu'une condition est vraie pour toutes les lignes d'une table alors que le quantificateur ANY vérifie qu'une condition est vraie pour au moins une ligne d'une table. Par exemple, la requête SQL du Code 7.12 utilise le quantificateur ALL et vérifie que tous les auteurs ont créé au moins une image.

SELECT * FROM auteurs WHERE id = ALL (SELECT auteur FROM images);

Code 7.12 : Requête SQL avec quantificateur ALL.

Ce qu'il faut retenir

Ce chapitre présente les concepts fondamentaux des requêtes sur les bases de données relationnelles. Trois points sont à retenir:

  • Les requêtes mono-table permettent de sélectionner des lignes (σ) ou de faire une projection sur les colonnes (π).
  • Les requêtes multi-tables permettent de combiner les tables à l'aide de produit scalaires (X) ou de jointures (⨝).
  • SQL propose différentes opérations pour affiner les résultats et ainsi combiner les requêtes.

Pour s'exercer

Questions de cours

  1. A quoi sert la requête mono-table πA1, ..., An(R) ?

    • a) A retourner une copie de la table R en supprimant les lignes qui ne correspondent pas à la contrainte exprimée par la requête.
    • b) A retourner une copie de la table R en supprimant les colonnes qui ne correspondent pas à la contrainte exprimée par la requête.
    • c) A vérifier que la table R respecte la troisième forme normale.
  2. A quoi sert la requête mono-table σφ(R) ?

    • a) A retourner une copie de la table R en supprimant les lignes qui ne correspondent pas à la contrainte exprimée par la requête.
    • b) A retourner une copie de la table R en supprimant les colonnes qui ne correspondent pas à la contrainte exprimée par la requête.
    • c) A vérifier que la table R respecte la troisième forme normale.
  3. Que fait la requête multi-tables R X S ?

    • a) C'est une projection suivie d'une sélection entre les tables R et S.
    • b) C'est une jointure entre les tables R et S.
    • c) C'est un produit cartésien entre les tables R et S.
  4. Que fait la requête multi-tables R ⨝φ S ?

    • a) C'est une projection suivie d'une sélection entre les tables R et S.
    • b) C'est une jointure entre les tables R et S.
    • c) C'est un produit cartésien entre les tables R et S.
  5. Est-il possible d'écrire une requête qui trouve le plus grand élément dans une table ?

    • a) Non, les requêtes permettent uniquement de construire de nouvelles tables.
    • b) Oui, grâce à la fonction MAX()
    • c) Oui, en réalisant une jointure.

Réponses: 1-b, 2-a, 3-c, 4-b, 5-b

Exercice 1 - Compétitions sportives

Nous allons reprendre les concepts de compétitions sportives proposés dans l'exercice 1 du chapitre 6 (voir Chapitre 6 - exercice 1).

  1. Téléchargez competitions-sportives.sql et initialisez la base de données competitions_sportives (notez que ce script n'est pas une correction de l'exercice 1 du chapitre 6).

  2. Exécutez la requête SQL qui permet de lister tous les noms des catégories (uniquement les noms).

  3. Exécutez la requête SQL qui permet de lister tous les prénoms et tous les noms des joueurs.

  4. Exécutez la requête SQL qui permet d'obtenir le nombre de joueurs.

  5. Exécutez la requête SQL qui permet d'obtenir tous les joueurs pour qui la categorie_id vaut trois.

  6. Exécutez la requête SQL qui permet d'obtenir le nombre de joueurs pour qui la categorie_id vaut trois.

  7. Exécutez la requête SQL qui permet d'obtenir tous les joueurs dont la catégorie a pour nom poussin.

  8. Reprenez la requête précédente et faites en sorte que la table résultat ait les colonnes suivantes : nom, prénom, catégorie.

  9. Exécutez la requête SQL qui permet d'obtenir toutes les compétitions dont la catégorie a pour nom poussin.

  10. Exécutez la requête SQL qui permet d'obtenir tous les joueurs inscrits dans la compétition dont l'id est 2.

  11. Exécutez la requête SQL qui permet d'obtenir tous les joueurs inscrits dans des compétitions dont la catégorie a pour nom poussin. Le résultat devra contenir le prénom et le nom des joueurs ainsi que le nom de la compétition.

Projet - mur d'images

Nous allons reprendre notre projet qui permet d'afficher des images (voir Chapitre 6, projet).

L'objectif est ici de constuire un fichier query.sql qui va contenir les requêtes SQL nécessaires pour notre application.

Récupérez le code que vous avez réalisé dans le chapitre 6 et copiez-le dans un nouveau répertoire nommé chap7-projet.

Proposez les requêtes SQL pour :

  1. Obtenir la liste de toutes les images.

  2. Obtenir la liste des images triées par date (de la plus récente à la plus ancienne).

  3. Obtenir les trois images les plus récentes.

  4. Obtenir la liste des images prises après le '2022-01-01'.

  5. Obtenir toutes les images qui ont plus de dix likes.

  6. Obtenir toutes les images dont l'orientation est portrait (ou paysage).

  7. Obtenir toutes les images de l'auteur Marcel Duchamp.

  8. Obtenir toutes les images de l'auteur Marcel Duchamp dont l'orientation est portrait.

  9. Obtenir le nombre de likes obtenus par Marcel Duchamp sur toutes ses images.

  10. Obtenir tous les commentaires de l'image dont l'id est 28.

  11. Obtenir l'image qui a le plus de likes.