dimanche 16 mars 2014

19 - Sous-requêtes

0 commentaires

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 SELECTINSERTUPDATE 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 .
1SELECT Animal.idAnimal.sexeAnimal.date_naissanceAnimal.nomAnimal.espece_id
2FROM Animal
3INNER JOIN Espece
4    ON Espece.id = Animal.espece_id
5WHERE sexe = 'F' 
6AND 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.
1SELECT MIN(date_naissance)
2FROM (
3    SELECT Animal.idAnimal.sexeAnimal.date_naissanceAnimal.nomAnimal.espece_id
4    FROM Animal
5    INNER JOIN Espece
6        ON Espece.id = Animal.espece_id
7    WHERE sexe = 'F'
8    AND Espece.nom_courant IN ('Tortue d''Hermann''Perroquet amazone')
9) 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 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 :
1SELECT MIN(date_naissance)
2FROM (
3    SELECT Animal.idAnimal.nom
4    FROM Animal
5    INNER JOIN Espece
6        ON Espece.id = Animal.espece_id
7    WHERE sexe = 'F'
8    AND Espece.nom_courant IN ('Tortue d''Hermann''Perroquet amazone')
9) 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 :
1SELECT MIN(date_naissance)
2FROM (
3    SELECT Animal.idAnimal.sexeAnimal.date_naissanceAnimal.nomAnimal.espece_id
4            Espece.id AS espece_espece_id         -- On renomme la colonne id de Espece, donc il n'y a plus de doublons.
5    FROM Animal                                   -- Attention de ne pas la renommer espece_id, puisqu'on sélectionne aussi la colonne espece_id dans Animal !
6    INNER JOIN Espece
7        ON Espece.id = Animal.espece_id
8    WHERE sexe = 'F'
9    AND Espece.nom_courant IN ('Tortue d''Hermann''Perroquet amazone')
10) AS tortues_perroquets_F;

Leave a Reply