Jointures

Vous vous attaquez maintenant au plus important chapitre de cette partie. Le principe des jointures est plutôt simple à comprendre (quoiqu'on puisse faire des requêtes très compliquées avec), et totalement indispensable.

Les jointures vont vous permettre de jongler avec plusieurs tables dans la même requête. Pour commencer, nous verrons le principe général des jointures. Puis, nous ferons un petit détour par les alias, qui servent beaucoup dans les jointures (mais pas seulement). Ensuite, retour sur le sujet avec les deux types de jointures : internes et externes. Et pour finir, après un rapide tour d'horizon des syntaxes possibles pour faire une jointure, je vous propose quelques exercices pour mettre tout ça en pratique.

Principe des jointures et notion d'alias

Principe des jointures

Sans surprise, le principe des jointures est de joindre plusieurs tables. Pour ce faire, on utilise les informations communes des tables.

Par exemple, lorsque nous avons ajouté dans notre base les informations sur les espèces (leur nom latin et leur description), je vous ai dit que ce serait une très mauvaise idée de tout mettre dans la table Animal, car il nous faudrait alors répéter la même description pour tous les chiens, la même pour toutes les tortues, etc. Cependant, vous avez sans doute remarqué que du coup, si vous voulez afficher la description de l'espèce de Cartouche (votre petit préféré), vous avez besoin de deux requêtes.

Étape 1 : on trouve l'id de l'espèce de Cartouche grâce à la table Animal.

1
SELECT espece_id FROM Animal WHERE nom = 'Cartouche';

espece_id

1

Étape 2 : on trouve la description de l'espèce grâce à son id.

1
SELECT description FROM Espece WHERE id = 1;

description

Bestiole à quatre pattes qui aime les caresses et tire souvent la langue

Ne serait-ce pas merveilleux de pouvoir faire tout ça (et plus encore) en une seule requête ? C'est là que les jointures entrent en jeu ; on va utiliser l'information commune entre les deux tables : l'id de l'espèce, qui est présente dans Animal avec la colonne espece_id, et dans Espece avec la colonne id.

1
2
3
4
5
SELECT Espece.description 
FROM Espece 
INNER JOIN Animal 
    ON Espece.id = Animal.espece_id 
WHERE Animal.nom = 'Cartouche';

Et voilà le travail !

description

Bestiole à quatre pattes qui aime les caresses et tire souvent la langue

En fait, lorsque l'on fait une jointure, on crée une table virtuelle et temporaire qui reprend les colonnes des tables liées. Le schéma ci-dessous illustre ce principe.

Au départ, on a deux tables : Animal (id, sexe, nom, race_id, espece_id) et Espece (id, nom_courant, nom_latin). Les deux premières lignes d'Animal correspondent à la première ligne d'Espece, et la troisième ligne d'Animal à la deuxième ligne d'Espece. Une fois les deux tables jointes, on obtient une table possédant toutes les colonnes d'Animal et toutes les colonnes d'Espece, avec les valeurs correspondantes de chaque table. On peut voir que les cinquième et sixième colonnes de la table de jointure ont les mêmes valeurs. Ensuite, de cette table virtuelle, on peut extraire ce que l'on veut. La colonne nom_latin pour la ligne ayant "Caribou" dans la colonne nom, par exemple.

Principe des jointures

Notion d'alias

Je fais ici une petite parenthèse avant de vous expliquer en détail le fonctionnement des jointures pour vous parler d'un petit truc bien utile : les alias.

Les alias sont des noms de remplacement, que l'on donne de manière temporaire (le temps d'une requête en fait) à une colonne, une table, une donnée. Les alias sont introduits par le mot-clé AS. Ce mot-clé est facultatif, vous pouvez très bien définir un alias sans utiliser AS, mais je préfère personnellement toujours le mettre. Je trouve qu'on y voit plus clair.

Comment ça marche ?

Prenons cette requête toute simple :

1
SELECT 5+3;

5+3

8

Imaginons que ce calcul savant représente en fait le nombre de chiots de Cartouche, qui a eu une première portée de 5 chiots, et une seconde de seulement 3 chiots. Nous voudrions donc indiquer qu'il s'agit bien de ça, et non pas d'un calcul inutile destiné simplement à illustrer une notion obscure.

Facile ! Il suffit d'utiliser les alias :

1
2
3
4
5
SELECT 5+3 AS Chiots_Cartouche;

-- OU, sans utiliser AS

SELECT 5+3 Chiots_Cartouche;

Chiots_Cartouche

8

Bon, tout ça c'est bien joli, mais pour l'instant ça n'a pas l'air très utile… Prenons un exemple plus parlant : retournez voir le schéma qui explique le principe des jointures un peu plus haut. La table virtuelle résultant de la jointure des tables Espece et Animal possède plusieurs colonnes : _

  • id
  • sexe
  • nom
  • race_id
  • espece_id
  • id
  • nom_courant
  • nom_latin

_ Mais que vois-je ? J'ai deux colonnes id ! Comment faire pour les différencier ? Comment être sûr de savoir à quoi elles se rapportent ? Avec les alias pardi ! Il suffit de donner l'alias espece_id à la colonne id de la table Espece, et animal_id à la colonne id de la table Animal. Tout à coup, ça vous semble plus intéressant non ?

Je vais vous laisser sur ce sentiment. Il n'y a pas grand-chose de plus à dire sur les alias, vous en comprendrez toute l'utilité à travers les nombreux exemples dans la suite de ce cours. L'important pour l'instant est que vous sachiez que ça existe et comment les définir.

Jointure interne

L'air de rien, dans l'introduction, je vous ai déjà montré comment faire une jointure. La première émotion passée, vous devriez vous être dit "Tiens, mais ça n'a pas l'air bien compliqué en fait, les jointures". En effet, une fois que vous aurez compris comment réfléchir aux jointures, tout se fera tout seul. Personnellement, ça m'aide vraiment d'imaginer la table virtuelle créée par la jointure, et de travailler sur cette table pour tout ce qui est conditions, tris, etc.

Revoici la jointure que je vous ai fait faire, et qui est en fait une jointure interne.

1
2
3
4
5
SELECT Espece.description 
FROM Espece 
INNER JOIN Animal 
    ON Espece.id = Animal.espece_id 
WHERE Animal.nom = 'Cartouche';

Décomposons !

  • SELECT Espece.description : je sélectionne la colonne description de la table Espece.
  • FROM Espece : je travaille sur la table Espece.
  • INNER JOIN Animal : je la joins (avec une jointure interne) à la table Animal.
  • ON Espece.id = Animal.espece_id : la jointure se fait sur les colonnes id de la table Espece et espece_id de la table Animal, qui doivent donc correspondre.
  • WHERE Animal.nom = 'Cartouche' : dans la table résultant de la jointure, je sélectionne les lignes qui ont la valeur "Cartouche" dans la colonne nom venant de la table Animal.

Si vous avez compris ça, vous avez tout compris !

Syntaxe

Comme d'habitude, voici donc la syntaxe à utiliser pour faire des requêtes avec jointure(s) interne(s).

1
2
3
4
5
6
7
8
9
SELECT *                                   -- comme d'habitude, vous sélectionnez les colonnes que vous voulez
FROM nom_table1   
[INNER] JOIN nom_table2                    -- INNER explicite le fait qu'il s'agit d'une jointure interne, mais c'est facultatif
    ON colonne_table1 = colonne_table2     -- sur quelles colonnes se fait la jointure
                                           -- vous pouvez mettre colonne_table2 = colonne_table1, l'ordre n'a pas d'importance

[WHERE ...]                               
[ORDER BY ...]                            -- les clauses habituelles sont bien sûr utilisables !
[LIMIT ...]

Condition de jointure

La clause ON sert à préciser la condition de la jointure. C'est-à-dire sur quel(s) critère(s) les deux tables doivent être jointes. Dans la plupart des cas, il s'agira d'une condition d'égalité simple, comme ON Animal.espece_id = Espece.id. Il est cependant tout à fait possible d'avoir plusieurs conditions à remplir pour lier les deux tables. On utilise alors les opérateurs logiques habituels. Par exemple, une jointure peut très bien se faire sur plusieurs colonnes :

1
2
3
4
5
6
SELECT *
FROM nom_table1
INNER JOIN nom_table2
   ON colonne1_table1 = colonne1_table2
      AND colonne2_table1 = colonne2_table2
      [AND ...];

Expliciter le nom des colonnes

Il peut arriver que vous ayez dans vos deux tables des colonnes portant le même nom. C'est le cas dans notre exemple, puisque la table Animal comporte une colonne id, tout comme la table Espece. Il est donc important de préciser de quelle colonne on parle dans ce cas-là. Vous l'avez vu dans notre requête, on utilise pour ça l'opérateur . (nom_table.nom_colonne). Pour les colonnes ayant un nom non-ambigu (qui n'existe dans aucune autre table de la jointure), il n'est pas obligatoire de préciser la table. En général, je précise la table quand il s'agit de grosses requêtes avec plusieurs jointures. En revanche, pour les petites jointures courantes, il est vrai que c'est moins long à écrire si on ne précise pas la table.

Exemple : sélection du nom des animaux commençant par "Ch", ainsi que de l'id et la description de leur espèce.

1
2
3
4
5
6
7
SELECT Espece.id,                   -- ici, pas le choix, il faut préciser
       Espece.description,          -- ici, on pourrait mettre juste description
       Animal.nom                   -- idem, la précision n'est pas obligatoire. C'est cependant plus clair puisque les espèces ont un nom aussi
FROM Espece   
INNER JOIN Animal
     ON Espece.id = Animal.espece_id
WHERE Animal.nom LIKE 'Ch%';

id

description

nom

2

Bestiole à quatre pattes qui saute très haut et grimpe aux arbres

Choupi

3

Bestiole avec une carapace très dure

Cheli

3

Bestiole avec une carapace très dure

Chicaca

Utiliser les alias

Les alias sont souvent utilisés avec les jointures. Ils permettent notamment de renommer les tables, et ainsi d'écrire moins de code.

Exemple : on renomme la table Espece "e", et la table Animal "a".

1
2
3
4
5
6
7
SELECT e.id,                  
       e.description,          
       a.nom                   
FROM Espece AS e          -- On donne l'alias "e" à Espece
INNER JOIN Animal AS a    -- et l'alias "a" à Animal.
     ON e.id = a.espece_id
WHERE a.nom LIKE 'Ch%';

Comme vous le voyez, le code est plus compact. Ici encore, c'est quelque chose que j'utilise souvent pour de petites requêtes ponctuelles. Par contre, pour de grosses requêtes, je préfère les noms explicites ; c'est ainsi plus facile de s'y retrouver.

Une autre utilité des alias est de renommer les colonnes pour que le résultat soit plus clair. Observez le résultat de la requête précédente. Vous avez trois colonnes : id, description et nom. Le nom de la table dont provient la colonne n'est indiqué nulle part. A priori, vous savez ce que vous avez demandé, surtout qu'il n'y a pas encore trop de colonnes, mais imaginez que vous sélectionniez une vingtaine de colonnes. Ce serait quand même mieux de savoir de quel id on parle, s'il s'agit du nom de l'animal, de son maître, du père, du fils ou du Saint-Esprit ! Il est intéressant là aussi d'utiliser les alias.

Exemple : on donne des alias aux colonnes (id_espece pour id de la table Espece, description_espece pour Espece.description et nom_bestiole pour Animal.nom).

1
2
3
4
5
6
7
SELECT Espece.id AS id_espece,                  
       Espece.description AS description_espece,          
       Animal.nom AS nom_bestiole                   
FROM Espece   
INNER JOIN Animal
     ON Espece.id = Animal.espece_id
WHERE Animal.nom LIKE 'Ch%';

id_espece

description_espece

nom_bestiole

2

Bestiole à quatre pattes qui saute très haut et grimpe aux arbres

Choupi

3

Bestiole avec une carapace très dure

Cheli

3

Bestiole avec une carapace très dure

Chicaca

C'est tout de suite plus clair !

Pourquoi "interne" ?

INNER JOIN permet donc de faire une jointure interne sur deux tables. Mais que signifie donc ce "interne" ?

C'est très simple ! Lorsque l'on fait une jointure interne, cela veut dire qu'on exige qu'il y ait des données de part et d'autre de la jointure. Donc, si l'on fait une jointure sur la colonne a de la table A et la colonne b de la table B :

1
2
3
4
SELECT * 
FROM A
INNER JOIN B 
    ON A.a = B.b

Ceci retournera uniquement les lignes pour lesquelles A.a et B.b correspondent.

Exemple : on veut connaître la race des chats :

1
2
3
4
5
6
SELECT Animal.nom AS nom_animal, Race.nom AS race
FROM Animal
INNER JOIN Race
    ON Animal.race_id = Race.id
WHERE Animal.espece_id = 2             -- ceci correspond aux chats
ORDER BY Race.nom, Animal.nom;

nom_animal

race

Callune

Bleu russe

Caribou

Bleu russe

Cawette

Bleu russe

Feta

Bleu russe

Filou

Bleu russe

Raccou

Bleu russe

Schtroumpfette

Bleu russe

Bagherra

Maine coon

Bilba

Maine coon

Capou

Maine coon

Cracotte

Maine coon

Farceur

Maine coon

Milla

Maine coon

Zara

Maine coon

Zonko

Maine coon

Boucan

Singapura

Boule

Singapura

Fiero

Singapura

On peut voir ici que les chats Choupi et Roucky pour lesquels je n'ai pas d'information sur la race (race_id est NULL), ne sont pas repris dans les résultats. De même, aucun des chats n'est de la race "Sphynx", celle-ci n'est donc pas reprise. Si je veux les inclure, je dois utiliser une jointure externe.

Jointure externe

Comme je viens de vous le dire, une jointure externe permet de sélectionner également les lignes pour lesquelles il n'y a pas de correspondance dans une des tables jointes. MySQL permet deux types de jointures externes : les jointures par la gauche et les jointures par la droite.

Jointures par la gauche

Lorsque l'on fait une jointure par la gauche (grâce aux mots-clés LEFT JOIN ou LEFT OUTER JOIN), cela signifie que l'on veut toutes les lignes de la table de gauche (sauf restrictions dans une clause WHERE bien sûr), même si certaines n'ont pas de correspondance avec une ligne de la table de droite.

Alors, table de gauche, table de droite, laquelle est laquelle ? C'est très simple, nous lisons de gauche à droite, donc la table de gauche est la première table mentionnée dans la requête, c'est-à-dire, en général, la table donnée dans la clause FROM.

Si l'on veut de nouveau connaître la race des chats, mais que cette fois-ci nous voulons également afficher les chats qui n'ont pas de race, on peut utiliser la jointure suivante (je ne prends que les chats dont le nom commence par "C" afin de réduire le nombre de lignes, mais vous pouvez choisir les conditions que vous voulez) :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
SELECT Animal.nom AS nom_animal, Race.nom AS race
FROM Animal                                               -- Table de gauche
LEFT JOIN Race                                            -- Table de droite
    ON Animal.race_id = Race.id
WHERE Animal.espece_id = 2 
    AND Animal.nom LIKE 'C%'
ORDER BY Race.nom, Animal.nom;

-- OU 

SELECT Animal.nom AS nom_animal, Race.nom AS race
FROM Animal                                               -- Table de gauche
LEFT OUTER JOIN Race                                      -- Table de droite
    ON Animal.race_id = Race.id
WHERE Animal.espece_id = 2
    AND Animal.nom LIKE 'C%'
ORDER BY Race.nom, Animal.nom;

nom_animal

race

Choupi

NULL

Callune

Bleu russe

Caribou

Bleu russe

Cawette

Bleu russe

Capou

Maine coon

Cracotte

Maine coon

On ne connaît pas la race de Choupi, et pourtant il fait bien partie des lignes sélectionnées alors qu'avec la jointure interne il n'apparaissait pas. Simplement, les colonnes qui viennent de la table Race (la colonne Race.nom AS race dans ce cas-ci) sont NULL pour les lignes qui n'ont pas de correspondance (la ligne de Choupi ici).

Jointures par la droite

Les jointures par la droite (RIGHT JOIN ou RIGHT OUTER JOIN), c'est évidemment le même principe, sauf que ce sont toutes les lignes de la table de droite qui sont sélectionnées même s'il n'y a pas de correspondance dans la table de gauche.

Exemple : toujours avec les races de chats.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
SELECT Animal.nom AS nom_animal, Race.nom AS race
FROM Animal                                                -- Table de gauche
RIGHT JOIN Race                                            -- Table de droite
    ON Animal.race_id = Race.id
WHERE Race.espece_id = 2
ORDER BY Race.nom, Animal.nom;

- OU

SELECT Animal.nom AS nom_animal, Race.nom AS race
FROM Animal                                              -- Table de gauche
RIGHT OUTER JOIN Race                                    -- Table de droite
    ON Animal.race_id = Race.id
WHERE Race.espece_id = 2
ORDER BY Race.nom, Animal.nom;

nom_animal

race

Callune

Bleu russe

Caribou

Bleu russe

Cawette

Bleu russe

Feta

Bleu russe

Filou

Bleu russe

Raccou

Bleu russe

Schtroumpfette

Bleu russe

Bagherra

Maine coon

Bilba

Maine coon

Capou

Maine coon

Cracotte

Maine coon

Farceur

Maine coon

Milla

Maine coon

Zara

Maine coon

Zonko

Maine coon

Boucan

Singapura

Boule

Singapura

Fiero

Singapura

NULL

Sphynx

On a bien une ligne avec la race "Sphynx", bien que nous n'ayons aucun sphynx dans notre table Animal.

À noter que toutes les jointures par la droite peuvent être faites grâce à une jointure par la gauche (et vice versa). Voici l'équivalent avec une jointure par la gauche de la requête que nous venons d'écrire :

1
2
3
4
5
6
SELECT Animal.nom AS nom_animal, Race.nom AS race
FROM Race                                               -- Table de gauche
LEFT JOIN Animal                                        -- Table de droite
    ON Animal.race_id = Race.id
WHERE Race.espece_id = 2
ORDER BY Race.nom, Animal.nom;

Syntaxes alternatives

Les syntaxes que je vous ai montrées jusqu'ici, avec [INNER] JOIN et LEFT|RIGHT [OUTER] JOIN, sont les syntaxes classiques que vous retrouverez le plus souvent. Il existe cependant d'autres manières de faire des jointures.

Jointures avec USING

Lorsque les colonnes qui servent à joindre les deux tables ont le même nom, vous pouvez utiliser la clause USING au lieu de la clause ON.

1
2
3
4
5
6
7
8
9
SELECT *
FROM table1
[INNER | LEFT | RIGHT] JOIN table2 USING (colonneJ);  -- colonneJ est présente dans les deux tables

-- équivalent à 

SELECT *
FROM table1
[INNER | LEFT | RIGHT] JOIN table2 ON Table1.colonneJ = table2.colonneJ;

Si la jointure se fait sur plusieurs colonnes, il suffit de lister les colonnes en les séparant par des virgules : USING (colonne1, colonne2,…)

Jointures naturelles

Comme pour les jointures avec USING, il est possible d'utiliser les jointures naturelles dans le cas où les colonnes servant à la jointure ont le même nom dans les deux tables. Simplement, dans le cas d'une jointure naturelle, on ne donne pas la (les) colonne(s) sur laquelle (lesquelles) joindre les tables : c'est déterminé automatiquement. Donc si on a les trois tables suivantes :

  • table1 : colonnes A, B, C
  • table2 : colonnes B, E, F
  • table3 : colonnes A, C, E

Exemple 1 : jointure de table1 et table2 (une colonne ayant le même nom : B).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
SELECT * 
FROM table1
NATURAL JOIN table2;

-- EST ÉQUIVALENT À

SELECT *
FROM table1
INNER JOIN table2 
    ON table1.B = table2.B;

Exemple 2 : jointure de table1 et table3 (deux colonnes ayant le même nom : A et C).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
SELECT * 
FROM table1
NATURAL JOIN table3;

-- EST ÉQUIVALENT À

SELECT *
FROM table1
INNER JOIN table3
    ON table1.A = table3.A AND table1.C = table3.C;

Pour utiliser ce type de jointure, il faut donc être certain que toutes les colonnes nécessaires à la jointure ont le même nom dans les deux tables, mais aussi que les colonnes ayant le même nom sont uniquement celles qui servent à la jointure.

Notez que vous pouvez également réaliser une jointure externe par la gauche avec les jointures naturelles à l'aide de NATURAL LEFT JOIN.

Jointures sans JOIN

Cela peut paraître absurde, mais il est tout à fait possible de faire une jointure sans utiliser le mot JOIN. Ce n'est cependant possible que pour les jointures internes. Il suffit de mentionner les tables que l'on veut joindre dans la clause FROM (séparées par des virgules), et de mettre la condition de jointure dans la clause WHERE (sans clause ON).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
SELECT *
FROM table1, table2
WHERE table1.colonne1 = table2.colonne2;

-- équivalent à 

SELECT *
FROM table1
[INNER] JOIN table2
    ON table1.colonne1 = table2.colonne2;

Je vous déconseille cependant d'utiliser cette syntaxe. En effet, lorsque vous ferez des grosses jointures, avec plusieurs conditions dans la clause WHERE, vous serez bien contents de pouvoir différencier au premier coup d’œil les conditions de jointures des conditions "normales".

Exemples d'application et exercices

Maintenant que vous savez comment faire une jointure, on va un peu s'amuser. Cette partie sera en fait un mini-TP. Je vous dis à quel résultat vous devez parvenir en utilisant des jointures, vous essayez, et ensuite, vous allez voir la réponse et les explications.

Techniquement, vous avez vu toute la théorie nécessaire pour réaliser toutes les requêtes que je vous demanderai ici. Cependant, il y aura des choses que je ne vous ai pas "montrées" explicitement, comme les jointures avec plus de deux tables, ou les auto-jointures (joindre une table avec elle-même). C'est voulu !

Je ne m'attends pas à ce que vous réussissiez à construire toutes les requêtes sans jeter un œil à la solution. Le but ici est de vous faire réfléchir, et surtout de vous faire prendre conscience qu'on peut faire pas mal de chose en SQL, en combinant plusieurs techniques par exemple. Si un jour vous vous dites "Tiens, ce serait bien si en SQL on pouvait…", arrêtez de vous le dire et faites-le, simplement ! Vous serez probablement plus souvent limités par votre imagination que par SQL.

Bien, ça c'est dit, donc allons-y !

Pour jouer le jeu jusqu'au bout, je vous propose de considérer que vous ne connaissez pas les id correspondants aux différentes races et espèces. Donc quand je demande la liste des chiens par exemple, il n'est pas intéressant de sélectionner les animaux WHERE espece_id = 1 ; il est bien plus utile de faire une jointure avec la table Espece.

A/ Commençons par des choses faciles

Des jointures sur deux tables, avec différentes conditions à respecter.

1. Moi, j'aime bien les chiens de berger

Vous devez obtenir la liste des races de chiens qui sont des chiens de berger.

On considère (même si ce n'est pas tout à fait vrai) que les chiens de berger ont "berger" dans leur nom de race.

1
2
3
4
SELECT Race.nom AS Race 
FROM Race
INNER JOIN Espece ON Espece.id = Race.espece_id
WHERE Espece.nom_courant = 'chien' AND Race.nom LIKE '%berger%';

Bon, c'était juste un échauffement. Normalement vous ne devriez pas avoir eu de difficultés avec cette requête. Peut-être avez-vous oublié la condition Espece.nom_courant = 'chien' ? On ne sait jamais, une race de chat (ou autre) pourrait très bien contenir "berger", or j'ai explicitement demandé les chiens.

2. Mais de quelle couleur peut bien être son pelage ?

Vous devez obtenir la liste des animaux (leur nom, date de naissance et race) pour lesquels nous n'avons aucune information sur la couleur que devrait avoir leur pelage.

Dans la description des races, j'utilise parfois "pelage", parfois "poils", et parfois "robe".

1
2
3
4
5
6
7
8
9
SELECT Animal.nom AS nom_animal, Animal.date_naissance, Race.nom AS race
FROM Animal
LEFT JOIN Race
    ON Animal.race_id = Race.id
WHERE (Race.description NOT LIKE '%poil%' 
        AND Race.description NOT LIKE '%robe%'
        AND Race.description NOT LIKE '%pelage%'
      )
      OR Race.id IS NULL;

Il faut donc :

  • soit qu'on ne connaisse pas la race (on n'a alors aucune information sur le pelage de la race, a fortiori) ;
  • soit qu'on connaisse la race, mais que dans la description de celle-ci il n'y ait pas d'information sur le pelage.

1/ On ne connaît pas la race

Les animaux dont on ne connaît pas la race ont NULL dans la colonne race_id. Mais vu que l'on fait une jointure sur cette colonne, il ne faut pas oublier de faire une jointure externe, sinon tous ces animaux sans race seront éliminés. Une fois que c'est fait, pour les sélectionner, il suffit de mettre la condition Animaux.race_id IS NULL par exemple, ou simplement Race.#n'importe quelle colonne# IS NULL vu qu'il n'y a pas de correspondance avec Race.

2/ Pas d'information sur le pelage dans la description de la race

Je vous ai donné comme indice le fait que j'utilisais les mots "pelage", "poil" ou "robe" dans les descriptions des espèces. Il fallait donc sélectionner les races pour lesquelles la description ne contient pas ces mots. D'où l'utilisation de NOT LIKE.

Si on met tout ça ensemble : il fallait faire une jointure externe des tables Animal et Race, et ensuite sélectionner les animaux qui répondaient à l'une ou l'autre des conditions (opérateur logique OR).

B/ Compliquons un peu les choses

Jointures sur deux tables, ou plus !

1. La race ET l'espèce

Vous devez obtenir la liste des chats et des perroquets amazones, avec leur sexe, leur espèce (nom latin) et leur race s'ils en ont une. Regroupez les chats ensemble, les perroquets ensemble et, au sein de l'espèce, regroupez les races.

1
2
3
4
5
6
7
8
SELECT Animal.nom as nom_animal, Animal.sexe, Espece.nom_latin as espece, Race.nom as race
FROM Animal
INNER JOIN Espece
    ON Animal.espece_id = Espece.id
LEFT JOIN Race
    ON Animal.race_id = Race.id
WHERE Espece.nom_courant IN ('Perroquet amazone', 'Chat')
ORDER BY Espece.nom_latin, Race.nom;

Comme vous voyez, c'est non seulement très simple de faire des jointures sur plus d'une table, mais c'est également possible de mélanger jointures internes et externes. Si ça vous pose problème, essayez vraiment de vous imaginer les étapes. D'abord, on fait la jointure d'Animal et d'Espece. On se retrouve alors avec une grosse table qui possède toutes les colonnes d'Animal et toutes les colonnes d'Espece. Ensuite, à cette grosse table (à la fois virtuelle et intermédiaire), on joint la table Race, grâce à la colonne Animal.race_id. Notez que l'ordre dans lequel vous faites les jointures n'est pas important.

En ce qui concerne la clause ORDER BY, j'ai choisi de trier par ordre alphabétique, mais il est évident que vous pouviez également trier sur les id de l'espèce et de la race. L'important ici était de trier d'abord sur une colonne d'Espece, ensuite sur une colonne de Race.

2. Futures génitrices

Vous devez obtenir la liste des chiennes dont on connaît la race, et qui sont en âge de procréer (c'est-à-dire nées avant juillet 2010). Affichez leur nom, date de naissance et race.

1
2
3
4
5
6
7
8
9
SELECT Animal.nom AS nom_chienne, Animal.date_naissance, Race.nom AS race
FROM Animal
INNER JOIN Espece
    ON Animal.espece_id = Espece.id
INNER JOIN Race
    ON Animal.race_id = Race.id
WHERE Espece.nom_courant = 'chien'
    AND Animal.date_naissance < '2010-07-01'
    AND Animal.sexe = 'F';

Cette fois, il fallait faire une jointure interne avec Race puisqu'on voulait que la race soit connue. Le reste de la requête ne présentait pas de difficulté majeure.

C/ Et maintenant, le test ultime !

Jointures sur deux tables ou plus, avec éventuelles auto-jointures.

Je vous ai fait rajouter, à la fin du chapitre précédent, deux jolies petites colonnes dans la table Animal : mere_id et pere_id. Le moment est venu de les utiliser !

1. Mon père, ma mère, mes frères et mes sœurs (Wohooooo)

Vous devez obtenir la liste des chats dont on connaît les parents, ainsi que le nom de ces parents.

1
2
3
4
5
6
7
8
9
SELECT Animal.nom, Pere.nom AS Papa, Mere.nom AS Maman
FROM Animal
INNER JOIN Animal AS Pere
    ON Animal.pere_id = Pere.id
INNER JOIN Animal AS Mere
    ON Animal.mere_id = Mere.id
INNER JOIN Espece
    ON Animal.espece_id = Espece.id
WHERE Espece.nom_courant = 'chat';

Si celle-là, vous l'avez trouvée tout seuls, je vous félicite ! Sinon, c'est un peu normal. Vous voici face au premier cas dans lequel les alias sont obligatoires. En effet, vous aviez sans doute compris que vous pouviez faire FROM Animal INNER JOIN Animal, puisque j'avais mentionné les auto-jointures, mais vous avez probablement bloqué sur la clause ON. Comment différencier les colonnes d'Animal dans FROM des colonnes d'Animal dans JOIN ? Vous savez maintenant qu'il suffit d'utiliser des alias. Il faut faire une jointure sur trois tables puisqu'au final, vous avez besoin des noms de trois animaux. Or en liant deux tables Animal ensemble, vous avez deux colonnes nom. Pour pouvoir en avoir trois, il faut lier trois tables.

Prenez le temps de bien comprendre les auto-jointures, le pourquoi du comment et le comment du pourquoi. Faites des schémas si besoin, imaginez les tables intermédiaires.

2. Je suis ton père

Histoire de se détendre un peu, vous devez maintenant obtenir la liste des enfants de Bouli (nom, sexe et date de naissance).

1
2
3
4
5
SELECT Animal.nom, Animal.sexe, Animal.date_naissance
FROM Animal
INNER JOIN Animal AS Pere
    ON Animal.pere_id = Pere.id
WHERE Pere.nom = 'Bouli';

Après la requête précédente, celle-ci devrait vous sembler plutôt facile ! Notez qu'il y a plusieurs manières de faire bien sûr. En voici une autre :

1
2
3
4
5
SELECT Enfant.nom, Enfant.sexe, Enfant.date_naissance
FROM Animal
INNER JOIN Animal AS Enfant
    ON Enfant.pere_id = Animal.id
WHERE Animal.nom = 'Bouli';

L'important, c'est le résultat ! Évidemment, si vous avez utilisé 45 jointures et 74 conditions, alors ce n'est pas bon non plus. Du moment que vous n'avez joint que deux tables, ça devrait être bon.

3. C'est un pure race ?

Courage, c'est la dernière (et la plus thrash :ninja: )!

Vous devez obtenir la liste des animaux dont on connaît le père, la mère, la race, la race du père, la race de la mère. Affichez le nom et la race de l'animal et de ses parents, ainsi que l'espèce de l'animal (pas des parents).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
SELECT Espece.nom_courant AS espece, Animal.nom AS nom_animal, Race.nom AS race_animal,
    Pere.nom AS papa, Race_pere.nom AS race_papa,
    Mere.nom AS maman, Race_mere.nom AS race_maman
FROM Animal
INNER JOIN Espece
    ON Animal.espece_id = Espece.id
INNER JOIN Race
    ON Animal.race_id = Race.id
INNER JOIN Animal AS Pere
    ON Animal.pere_id = Pere.id
INNER JOIN Race AS Race_pere
    ON Pere.race_id = Race_pere.id
INNER JOIN Animal AS Mere
    ON Animal.mere_id = Mere.id
INNER JOIN Race AS Race_mere
    ON Mere.race_id = Race_mere.id;

Pfiou :euh: ! Le principe est exactement le même que pour avoir le nom des parents. Il suffit de rajouter une jointure avec Race pour le père, pour la mère, et pour l'enfant, en n'oubliant pas de bien utiliser les alias bien sûr. C'est avec ce genre de requête que l'on se rend compte à quel point il est important de bien structurer et indenter sa requête, et à quel point un choix d'alias intelligent peut clarifier les choses.

Si vous avez survécu jusqu'ici, vous devriez maintenant avoir compris en profondeur le principe des jointures, et être capables de manipuler de nombreuses tables sans faire tout tomber par terre.


En résumé

  • Une jointure est une opération qui consiste à joindre plusieurs tables ensemble, sur la base d'informations communes.
  • Lorsque l'on fait une jointure interne, on ne prend que les données pour lesquelles il existe une correspondance entre la table 1 et la table 2.
  • Par contre, dans une jointure externe, on récupère toutes les données d'une des deux tables (table 1), plus les données de la seconde table (table 2) pour lesquelles il existe une correspondance dans la table 1.
  • On peut joindre une table à elle-même. Cela s'appelle une auto-jointure.