Nous allons maintenant apprendre à imbriquer plusieurs requêtes, ce qui vous permettra de faire en une seule fois ce qui vous aurait, jusqu'ici, demandé plusieurs étapes.
Une sous-requête est une requête à l'intérieur d'une autre requête. Avec le SQL, vous pouvez construire des requêtes imbriquées sur autant de niveaux que vous voulez. Vous pouvez également mélanger jointures et sous-requêtes. Tant que votre requête est correctement structurée, elle peut être aussi complexe que vous voulez.
Une sous-requête peut être faite dans une requête de type
SELECT
, INSERT
, UPDATE
ou DELETE
(et quelques autres que nous n'avons pas encore vues). Nous ne verrons dans ce chapitre que les requêtes de sélection. Les jointures et sous-requêtes pour la modification, l'insertion et la suppression de données étant traitées dans le prochain chapitre.
La plupart des requêtes de sélection que vous allez voir dans ce chapitre sont tout à fait réalisables autrement, souvent avec une jointure. Certains préfèrent les sous-requêtes aux jointures parce que c'est légèrement plus clair comme syntaxe, et peut-être plus intuitif. Il faut cependant savoir qu'une jointure sera toujours au moins aussi rapide que la même requête faite avec une sous-requête. Par conséquent, s'il est important pour vous d'optimiser les performances de votre application, utilisez plutôt des jointures lorsque c'est possible.
Lorsque l'on fait une requête de type
SELECT
, le résultat de la requête nous est envoyé sous forme de table. Et grâce aux sous-requêtes, il est tout à fait possible d'utiliser cette table et de refaire une recherche uniquement sur les lignes de celle-ci.
Exemple : on sélectionne toutes les femelles parmi les perroquets et les tortues .
SELECT Animal.id, Animal.sexe, Animal.date_naissance, Animal.nom, Animal.espece_id
FROM Animal
INNER JOIN Espece
ON Espece.id = Animal.espece_id
WHERE sexe = 'F'
AND Espece.nom_courant IN ('Tortue d''Hermann', 'Perroquet amazone');
id
|
sexe
|
date_naissance
|
nom
|
espece_id
|
---|---|---|---|---|
4
|
F
|
2009-08-03 05:12:00
|
NULL
|
3
|
6
|
F
|
2009-06-13 08:17:00
|
Bobosse
|
3
|
45
|
F
|
2007-04-01 18:17:00
|
Nikki
|
3
|
46
|
F
|
2009-03-24 08:23:00
|
Tortilla
|
3
|
47
|
F
|
2009-03-26 01:24:00
|
Scroupy
|
3
|
48
|
F
|
2006-03-15 14:56:00
|
Lulla
|
3
|
49
|
F
|
2008-03-15 12:02:00
|
Dana
|
3
|
50
|
F
|
2009-05-25 19:57:00
|
Cheli
|
3
|
51
|
F
|
2007-04-01 03:54:00
|
Chicaca
|
3
|
52
|
F
|
2006-03-15 14:26:00
|
Redbul
|
3
|
60
|
F
|
2009-03-26 07:55:00
|
Parlotte
|
4
|
Parmi ces femelles perroquets et tortues, on veut connaître la date de naissance de la plus âgée. On va donc faire une sélection dans la table des résultats de la requête.
SELECT MIN(date_naissance)
FROM (
SELECT Animal.id, Animal.sexe, Animal.date_naissance, Animal.nom, Animal.espece_id
FROM Animal
INNER JOIN Espece
ON Espece.id = Animal.espece_id
WHERE sexe = 'F'
AND Espece.nom_courant IN ('Tortue d''Hermann', 'Perroquet amazone')
) AS tortues_perroquets_F;
MIN(date_naissance)
|
---|
2006-03-15 14:26:00
|
Les règles à respecter
Parenthèses
Une sous-requête doit toujours se trouver dans des parenthèses, afin de définir clairement ses limites.
Alias
Dans le cas des sous-requêtes dans le
Nommer votre table intermédiaire permet de plus de vous y référer si vous faites une jointure dessus, ou si certains noms de colonnes sont ambigus et que le nom de la table doit être précisé. Attention au fait qu'il ne s'agit pas de la table Animal, mais bien d'une table tirée d'Animal.
Par conséquent, si vous voulez préciser le nom de la table dans le
FROM
, il est également obligatoire de préciser un alias pour la table intermédiaire (le résultat de notre sous-requête). Si vous ne le faites pas, MySQL déclenchera une erreur. Ici, on l'a appelée tortues_perroquets_F.Nommer votre table intermédiaire permet de plus de vous y référer si vous faites une jointure dessus, ou si certains noms de colonnes sont ambigus et que le nom de la table doit être précisé. Attention au fait qu'il ne s'agit pas de la table Animal, mais bien d'une table tirée d'Animal.
Par conséquent, si vous voulez préciser le nom de la table dans le
SELECT
principal, vous devez écrireSELECT MIN(tortues_perroquets_F.date_naissance)
, et non pas SELECT MIN(Animal.date_naissance)
.Cohérence des colonnes
Les colonnes sélectionnées dans le
SELECT
"principal" doivent bien sûr être présentes dans la table intermédiaire. La requête suivante, par exemple, ne fonctionnera pas :
SELECT MIN(date_naissance)
FROM (
SELECT Animal.id, Animal.nom
FROM Animal
INNER JOIN Espece
ON Espece.id = Animal.espece_id
WHERE sexe = 'F'
AND Espece.nom_courant IN ('Tortue d''Hermann', 'Perroquet amazone')
) AS tortues_perroquets_F;
En effet, tortues_perroquets_F n'a que deux colonnes : id et nom. Il est donc impossible de sélectionner la colonne date_naissance de cette table.
Noms ambigus
Pour finir, attention aux noms de colonnes ambigus. Une table, même intermédiaire, ne peut pas avoir deux colonnes ayant le même nom. Si deux colonnes ont le même nom, il est nécessaire de renommer explicitement au moins l'une des deux.
Donc, si on veut sélectionner la colonne Espece.id en plus dans la sous-requête, on peut procéder ainsi :
SELECT MIN(date_naissance)
FROM (
SELECT Animal.id, Animal.sexe, Animal.date_naissance, Animal.nom, Animal.espece_id,
Espece.id AS espece_espece_id -- On renomme la colonne id de Espece, donc il n'y a plus de doublons.
FROM Animal -- Attention de ne pas la renommer espece_id, puisqu'on sélectionne aussi la colonne espece_id dans Animal !
INNER JOIN Espece
ON Espece.id = Animal.espece_id
WHERE sexe = 'F'
AND Espece.nom_courant IN ('Tortue d''Hermann', 'Perroquet amazone')
) AS tortues_perroquets_F;