Les jointures sont un moyen de consulter les informations provenant de plusieurs tables en même temps, sans passer par une sous-requête. Pour spécifier une jointure dans un SELECT il suffit de préciser plusieurs tables dans la clause FROM.
SELECT liste_colonnes FROM table1, table2
/* fait une jointure entre table 1 et table2 */
Concrètement pour une question donnée portant sur plusieurs tables, il est possible de répondre soit par une jointure, soit par une sous-requeête. Le seule différence portera éventuellement sur les performances de l’une et de l’autre,mais le résultat final sera le même.
- Jointure brute
Une jointure brute fait un produit cartésien entre les données de T1 et T2, c’est à dire associe toutes les colonnes de chaque ligne de T1, à toutes les colonnes de chaque ligne de T2, en effectuant toutes les combinaisons possibles.
Ainsi une jointure brute sur la table ‘Employes’ et la table ‘Services’ renvoie 56 lignes de 10 colonnes (6 colonnes de la table Employes, accolées à 4 colonnes de la table Services).
mysql> SELECT COUNT(*) FROM employes;
+———-+
| COUNT(*) |
+———-+
| 14 |
+———-+
mysql> SELECT COUNT(*) FROM services;
+———-+
| COUNT(*) |
+———-+
| 4 |
+———-+
mysql> SELECT COUNT(*)
-> FROM employes, services;
+———-+
| COUNT(*) |
+———-+
| 56 |
+———-+
mysql> SELECT employes.nom, services.lieu
-> FROM employes, services;
+————-+———-+
| nom | lieu |
+————-+———-+
| BAILLARGEON | MARSEILLE|
| BAILLARGEON | TOULOUSE |
| BAILLARGEON | BORDEAUX |
| BAILLARGEON | CALAIS |
| FRANCHI | MARSEILLE|
| FRANCHI | TOULOUSE |
| FRANCHI | BORDEAUX |
| FRANCHI | CALAIS |
| OUVRARD | MARSEILLE|
| OUVRARD | TOULOUSE |
| OUVRARD | BORDEAUX |
| OUVRARD | CALAIS |
| MURDOCK | MARSEILLE|
| MURDOCK | TOULOUSE |
| MURDOCK | BORDEAUX |
| MURDOCK | CALAIS |
| MARTIN | MARSEILLE|
| MARTIN | TOULOUSE |
| MARTIN | BORDEAUX |
| MARTIN | CALAIS |
| PIANETTI | MARSEILLE|
| PIANETTI | TOULOUSE |
| PIANETTI | BORDEAUX |
| PIANETTI | CALAIS |
| DELEGLISE | MARSEILLE|
| DELEGLISE | TOULOUSE |
| DELEGLISE | BORDEAUX |
| DELEGLISE | CALAIS |
| GORLIN | MARSEILLE|
| GORLIN | TOULOUSE |
| GORLIN | BORDEAUX |
| GORLIN | CALAIS |
| CHIRAC | MARSEILLE|
| CHIRAC | TOULOUSE |
| CHIRAC | BORDEAUX |
| CHIRAC | CALAIS |
| HULK | MARSEILLE|
| HULK | TOULOUSE |
| HULK | BORDEAUX |
| HULK | CALAIS |
| PARKER | MARSEILLE|
| PARKER | TOULOUSE |
| PARKER | BORDEAUX |
| PARKER | CALAIS |
| XAVIER | MARSEILLE|
| XAVIER | TOULOUSE |
| XAVIER | BORDEAUX |
| XAVIER | CALAIS |
| GRIM | MARSEILLE|
| GRIM | TOULOUSE |
| GRIM | BORDEAUX |
| GRIM | CALAIS |
| LEE | MARSEILLE|
| LEE | TOULOUSE |
| LEE | BORDEAUX |
| LEE | CALAIS |
+————-+———-+
On voit clairement sur ce dernier exemple, que certaines lignes du résultat n’ont pas de sens: pas exemple ‘LEE’ ne travaille pas à la fois à MARSEILLE, DALLAS, BORDEAUX et CALAIS! On a bien ici un effet pervers de la combinatoire de la jointure brute.
Eliminer les lignes parasites sera du ressort de l’équi-jointure…
- EquiJointure
Il est donc intéressant de filtrer ce résultat pour ne ramener que les lignes ayant un sens, c’est à dire les lignes où existe une correspondance réelle, en l’occurrence celles qui concerne un même numéro de service (comme par miracle une colonne commune aux deux tables).
Cette condition est facile à écrire, il suffit de rajouter une clause WHERE dans le SELECT en imposant que le no de service de l’employé soit égal au numéro de service de la table SERVICES.
Cette égalité nécessaire définit ce qu’on appelle une EQUI JOINTURE.
Cette ‘condition de jointure’, permet de relier les tables sur une colonne commune : la clé de jointure. Sur notre table ‘EMPLOYES’ c’est ‘NO_SERVICE’ qui fait le lien et est ce qu’on appelle la clé de jointure.
La requête précédente avec cette nouvelle condition, renverra forcément beaucoup moins de lignes. En l’occurrence 14 lignes ‘mariant’ correctement les informations de EMPLOYES et SERVICES concernant le MEME service:
mysql> SELECT employes.nom, services.lieu
-> FROM employes, services
-> WHERE employes.no_service= services.no_service;
+————-+———-+
| nom | lieu |
+————-+———-+
| DELEGLISE | MARSEILLE |
| CHIRAC | MARSEILLE |
| LEE | MARSEILLE |
| BAILLARGEON | TOULOUSE |
| MURDOCK | TOULOUSE |
| GORLIN | TOULOUSE |
| PARKER | TOULOUSE |
| GRIM | TOULOUSE |
| FRANCHI | BORDEAUX |
| OUVRARD | BORDEAUX |
| MARTIN | BORDEAUX |
| PIANETTI | BORDEAUX |
| HULK | BORDEAUX |
| XAVIER | BORDEAUX |
+————-+———-+
14 rows in set (0.01 sec)
- On évitera de faire des ‘SELECT *’ dans les jointures pour éviter les colonnes redondantes comme ‘’no_service’ ici. On précisera explicitement les colonnes utiles à afficher. On verra dans le paragraphe suivant qu’il est possible de mettre la clause de jointure ailleurs que dans la clause WHERE…
Il est bien évidemment possible de rajouter des conditions à la condition de jointure, comme dans tout SELECT. Si l’on veut limiter la requête précédente aux employés du seul service no 10:
mysql> SELECT employes.nom, services.lieu
-> FROM employes, services
-> WHERE employes.no_service= services.no_service
-> AND employes.no_service=10;
+———–+———-+
| nom | lieu |
+———–+———-+
| DELEGLISE | MARSEILLE |
| CHIRAC | MARSEILLE |
| LEE | MARSEILLE |
+———–+———-+
- Dans tous les exemples précédents on a pris soin de préfixer les noms de colonnes par les noms de table (pour lever toute ambigüité puisqu’on a désormais 2 tables). C’est une bonne habitude à prendre. Il est bien sûr impératif de le faire lorsqu’une colonne à le même nom dans les deux tables…
Pour s’en persuader voyons ce qui se passe sur l’exemple suivant:
mysql> SELECT nom, lieu
-> FROM employes, services
-> WHERE employes.no_service= services.no_service;
ERROR 1052 (23000): Column ‘nom’ in field list is ambiguous
mysql> — et si on rajoute le préfixe ’employes.’
mysql> — sur le nom…tout se passe bien
mysql> SELECT employes.nom, lieu
-> FROM employes, services
-> WHERE employes.no_service= services.no_service;
+————-+———-+
| nom | lieu |
+————-+———-+
| DELEGLISE | MARSEILLE |
| CHIRAC | MARSEILLE |
| LEE | MARSEILLE |
| BAILLARGEON | TOULOUSE |
| MURDOCK | TOULOUSE |
| GORLIN | TOULOUSE |
| PARKER | TOULOUSE |
| GRIM | TOULOUSE |
| FRANCHI | BORDEAUX |
| OUVRARD | BORDEAUX |
| MARTIN | BORDEAUX |
| PIANETTI | BORDEAUX |
| HULK | BORDEAUX |
| XAVIER | BORDEAUX |
+————-+———-+
14 rows in set (0.00 sec)
- On peut également utiliser les syntaxes INNER JOIN ou NATURAL JOIN pour ce type de jointure.
- Auto-jointure ou jointure réflexive
Une auto-jointure ou jointure réflexive est simplement une jointure d’une table sur elle-même.
Cette possibilité peut s’avérer particulièrement utile pour répondre à certaines questions auto référentes, comme par exemple employés et managers, employés d’un même job, d’un m^mem service, etc.
Pour les comprendre et les mettre en œuvre il suffira de faire mentalement comme si on avait deux tables identiques distinctes, sur lesquelles on fait une jointure.
- Les jointures externes
- Nous avons jusqu’ici eu de la chance dans nos equi-jointures puisque il y une correspondance exacte entre les no de service de EMPLOYES et de SERVICES.
Cependant, dans la vie courante, il n’y a parfois pas de correspondance systématique entre les valeurs des colonnes de jointures.
Supposons par exemple que notre société vienne de créer un tout nouveau département à Paris, mais que du personnel n’y soit pas encore affecté.
En SQL nous allons traduire cela par l’ajout (insertion) d’un service ‘RH’ à Paris, dans la table des services:
mysql> — on insère…
mysql> INSERT INTO services
-> VALUES (50, ‘RH’,’Paris’);
mysql> COMMIT;
mysql> – et on vérifie
mysql> SELECT * FROM services;
+————+————–+———-+
| NO_SERVICE | NOM | LIEU |
+————+————–+———-+
| 10 | COMPTABILITE | MARSEILLE |
| 20 | RECHERCHE | TOULOUSE |
| 30 | VENTES | BORDEAUX |
| 40 | OPERATIONS | CALAIS |
| 50 | RH | Paris |
+————+————–+———-+
5 rows in set (0.00 sec)
Listons comme précédemment, les employés et leur localisation, et voyons ce qui se passe pour Paris…
mysql> SELECT employes.nom, lieu
-> FROM employes, services
-> WHERE employes.no_service= services.no_service;
+————-+———-+
| nom | lieu |
+————-+———-+
| DELEGLISE | MARSEILLE |
| CHIRAC | MARSEILLE |
| LEE | MARSEILLE |
| BAILLARGEON | TOULOUSE |
| MURDOCK | TOULOUSE |
| GORLIN | TOULOUSE |
| PARKER | TOULOUSE |
| GRIM | TOULOUSE |
| FRANCHI | BORDEAUX |
| OUVRARD | BORDEAUX |
| MARTIN | BORDEAUX |
| PIANETTI | BORDEAUX |
| HULK | BORDEAUX |
| XAVIER | BORDEAUX |
+————-+———-+
On voit que comme on aurait pu s’en douter Paris n’apparait pas, ce qui est logique puisque il n’y a pas de service numéro 50 dans la table EMPLOYES, et donc pas de correspondance.
Il peut être néanmoins pertinent d’avoir TOUS les services, même VIDE! Heureusement nous allons pouvoir utiliser une jointure externe, c’est fait pour cela.
La syntaxe peut être l’une des suivantes:
SELECT … FROM
reference_tableA LEFT [OUTER] JOIN reference_tableB condition_jointure
|
reference_tableA LEFT [OUTER] JOIN reference_tableB
|
reference_tableA NATURAL [LEFT [OUTER]] JOIN reference_tableB
|
{ OJ reference_tableA LEFT OUTER JOIN reference_tableB ON expr_conditionnelle }
|
reference_tableA RIGHT [OUTER] JOIN reference_tableB condition_jointure
|
reference_tableA RIGHT [OUTER] JOIN reference_tableB
ON expr_conditionnelle | USING (liste_colonne)
Pour les LEFT JOIN:
S’il y a une ligne dans A qui répond à la clause WHERE, mais qu’il n’y avait aucune ligne dans B qui répondait à la condition du LEFT JOIN, alors une ligne supplémentaire de B est générée avec toutes les colonnes mises à NULL.
Dans nos tables de démonstration le cas ne se présente pas mais il est facile de le créer de toutes pièces. Il suffit de rajouter un employé qui n’est pas encore affecté à un service.
- Cet ajout serait dans la réalité certainement aberrant! Un nouvel embauché est en général affecté à un service particulier. Ce serait d’ailleurs une bonne raison pour créer une contrainte d’intégrité référentielle entre la table des services et des employés.
On insère un employé sans service:
mysql> INSERT INTO employes (no_employe, nom)
-> VALUES (1111, ‘Thor’);
Query OK, 1 row affected (0.03 sec)
mysql> COMMIT;
Query OK, 0 rows affected (0.00 sec)
mysql> — on verifie que les champs non renseignés sont bien à NULL
mysql> SELECT nom, no_employe, job, mgr, no_service FROM employes
-> WHERE nom=’Thor’;
+——+————+——+——+————+
| nom | no_employe | job | mgr | no_service |
+——+————+——+——+————+
| Thor | 1111 | NULL | NULL | NULL |
+——+————+——+——+————+
1 row in set (0.00 sec)
Mais une jointure ‘normale ‘donne toujours bien sûr 14 lignes:
mysql> SELECT employes.nom, no_employe, job, mgr, employes.no_service FROM emplo
yes
-> INNER JOIN services
-> ON employes.no_service = services.no_service;
+————-+————+———–+——+————+
| nom | no_employe | job | mgr | no_service |
+————-+————+———–+——+————+
| DELEGLISE | 7782 | MANAGER | 7839 | 10 |
| CHIRAC | 7839 | PRESIDENT | NULL | 10 |
| LEE | 7934 | EMPLOYE | 7782 | 10 |
| BAILLARGEON | 7369 | EMPLOYE | 7902 | 20 |
| MURDOCK | 7566 | MANAGER | 7839 | 20 |
| GORLIN | 7788 | ANALYSTE | 7566 | 20 |
| PARKER | 7876 | EMPLOYE | 7788 | 20 |
| GRIM | 7902 | ANALYST | 7566 | 20 |
| FRANCHI | 7499 | VENDEUR | 7698 | 30 |
| OUVRARD | 7521 | VENDEUR | 7698 | 30 |
| MARTIN | 7654 | VENDEUR | 7698 | 30 |
| PIANETTI | 7698 | MANAGER | 7839 | 30 |
| HULK | 7844 | VENDEUR | 7698 | 30 |
| XAVIER | 7900 | EMPLOYE | 7698 | 30 |
+————-+————+———–+——+————+
14 rows in set (0.00 sec)
Le LEFT OUTER JOIN forcera la correspondance entre le no_service inexistant dans employés et un no de service à NULL dans services. ET on obtiendra bien une ligne de résultat supplémentaire:
mysql> SELECT employes.nom, no_employe, job, mgr, employes.no_service FROM employes
-> LEFT OUTER JOIN services
-> ON employes.no_service = services.no_service;
+————-+————+———–+——+————+
| nom | no_employe | job | mgr | no_service |
+————-+————+———–+——+————+
| Thor | 1111 | NULL | NULL | NULL |
| BAILLARGEON | 7369 | EMPLOYE | 7902 | 20 |
| FRANCHI | 7499 | VENDEUR | 7698 | 30 |
| OUVRARD | 7521 | VENDEUR | 7698 | 30 |
| MURDOCK | 7566 | MANAGER | 7839 | 20 |
| MARTIN | 7654 | VENDEUR | 7698 | 30 |
| PIANETTI | 7698 | MANAGER | 7839 | 30 |
| DELEGLISE | 7782 | MANAGER | 7839 | 10 |
| GORLIN | 7788 | ANALYSTE | 7566 | 20 |
| CHIRAC | 7839 | PRESIDENT | NULL | 10 |
| HULK | 7844 | VENDEUR | 7698 | 30 |
| PARKER | 7876 | EMPLOYE | 7788 | 20 |
| XAVIER | 7900 | EMPLOYE | 7698 | 30 |
| GRIM | 7902 | ANALYST | 7566 | 20 |
| LEE | 7934 | EMPLOYE | 7782 | 10 |
+————-+————+———–+——+————+
15 rows in set (0.00 sec)
Pour les RIGHT JOIN:
C’est bien évidemment l’inverse. A savoir: si certaines valeurs de la deuxième table n’ont pas de correspondance dans la première, par exemple le numéro de service de la table des services, n’a pas d’employés associé dans la table des employés, la clause RIGHT OUTER JOIN ramènes quand même des employés virtuels avec leurs informations complétés à NULL. C’est ce qu’on voit sur l’exemple suivant:
mysql> SELECT employes.nom, no_employe,
job, services.no_service, lieu
FROM employes RIGHT OUTER JOIN services
ON employes.no_service= services.no_service;
+————-+————+———–+————+———-+
| nom | no_employe | job | no_service | lieu |
+————-+————+———–+————+———-+
| DELEGLISE | 7782 | MANAGER | 10 | MARSEILLE |
| CHIRAC | 7839 | PRESIDENT | 10 | MARSEILLE |
| LEE | 7934 | EMPLOYE | 10 | MARSEILLE |
| BAILLARGEON | 7369 | EMPLOYE | 20 | TOULOUSE |
| MURDOCK | 7566 | MANAGER | 20 | TOULOUSE |
| GORLIN | 7788 | ANALYSTE | 20 | TOULOUSE |
| PARKER | 7876 | EMPLOYE | 20 | TOULOUSE |
| GRIM | 7902 | ANALYST | 20 | TOULOUSE |
| FRANCHI | 7499 | VENDEUR | 30 | BORDEAUX |
| OUVRARD | 7521 | VENDEUR | 30 | BORDEAUX |
| MARTIN | 7654 | VENDEUR | 30 | BORDEAUX |
| PIANETTI | 7698 | MANAGER | 30 | BORDEAUX |
| HULK | 7844 | VENDEUR | 30 | BORDEAUX |
| XAVIER | 7900 | EMPLOYE | 30 | BORDEAUX |
| NULL | NULL | NULL | 40 | CALAIS |
| NULL | NULL | NULL | 50 | Paris |
+————-+————+———–+————+———-+
16 rows in set (0.00 sec)
Concrètement le numéro de service 50 de la deuxième table (des services) qui n’a pas de correspondance apparait désormais dans la jointure externe.
On remarque également que le numéro 40 apparait en plus. On en déduit qu’il y avait déjà un département vide, le département 40, ce que l’on vérifie facilement dans la table des employés:
mysql> SELECT DISTINCT no_service
-> FROM employes;
+————+
| no_service |
+————+
| 10 |
| 20 |
| 30 |
+————+
3 rows in set (0.05 sec)
- Quelques exercices pratiques sur les jointures
Afin de mettre en pratique les concepts que nous avons vus sur les différents types de jointures, nous allons nous atteler à résoudre quelques exercices.
Voici les énoncés:
- Lister les employés travaillant à Toulouse.
- Lister les analystes travaillant à Toulouse.
- Lister les noms des employés et de leurs chefs respectifs
- Lister les noms des employés et de leurs chefs respectifs, y compris ceux qui éventuellement n’ont pas de chef
- Donner le nombre d’employés de chaque département avec des employés.
- Donner le nombre d’employés de chaque département y compris ceux qui sont vides.
- Donner la liste des employés et de leurs chefs et la ville dans laquelle ils travaillent.
Et les solutions:
- Lister les employés travaillant à Toulouse:
mysql> SELECT employes.nom, services.lieu, employes.job
-> FROM employes INNER JOIN services
-> ON employes.no_service = services.no_service
-> WHERE services.lieu=’DALLAS’;
+————-+——–+———-+
| nom | lieu | job |
+————-+——–+———-+
| BAILLARGEON | TOULOUSE| EMPLOYE |
| MURDOCK | TOULOUSE| MANAGER |
| GORLIN | TOULOUSE| ANALYSTE |
| PARKER | TOULOUSE| EMPLOYE |
| GRIM | TOULOUSE| ANALYSTE |
+————-+——–+———-+
5 rows in set (0.00 sec)
- Lister les analystes travaillant à Toulouse.
On ajoute une condition supplémentaire sue le Job:
mysql> SELECT employes.nom, services.lieu, job
-> FROM employes INNER JOIN services
-> ON employes.no_service = services.no_service
-> WHERE services.lieu=’DALLAS’
-> AND job=’ANALYSTE’;
+————-+——–+———-+
| nom | lieu | job |
+————-+——–+———-+
| GORLIN | TOULOUSE| ANALYSTE |
| GRIM | TOULOUSE| ANALYSTE |
+————-+——–+———-+
2 rows in set (0.00 sec)
- Lister les noms des employés et de leurs chefs respectifs
Pour mener a bien cette requête il faut bien comprendre le contenu de la table employé: Chaque employé a un numéro d’employé (no_employe) et un numéro de chef (mgr). ‘mgr’ est en fait le no d’employé du chef. Il y a donc une relation hiérarchique implicite dans la table, que l’on peut parcourir en suivant un employé, son numéro, puis son numéro de chef…qui pointe sur un nouveau numéro d’employé.
On peut considérer qu’on a 2 tables virtuelles: une table des employés et une table (identique à la première!) des chefs. Le lien hiérarchique est représenté simplement par l’égalité employe.mgr = employe.no_employe, qui traduit bien que l’on suit un employé, son no de mgr, puis le no d’employe du mgr, pour avoir les informations concernant le ‘chef’. En SQL cela nous donne:
mysql> select emp.nom employe, chef.nom chef
from employes emp inner join employes chef
on emp.mgr = chef.no_employe;
+————-+———–+
| employe | chef |
+————-+———–+
| BAILLARGEON | GRIM |
| FRANCHI | PIANETTI |
| OUVRARD | PIANETTI |
| MURDOCK | CHIRAC |
| MARTIN | PIANETTI |
| PIANETTI | CHIRAC |
| DELEGLISE | CHIRAC |
| GORLIN | MURDOCK |
| HULK | PIANETTI |
| PARKER | GORLIN |
| XAVIER | PIANETTI |
| GRIM | MURDOCK |
| LEE | DELEGLISE |
+————-+———–+
13 rows in set (0.00 sec)
On remarque qu’il n’y a que 13 lignes car il y a un employé (le président) qui n’a pas de chef, donc le critère de jointure n’est pas vérifié…
- Lister les noms des employés et de leurs chefs respectifs, y compris ceux qui éventuellement n’ont pas de chef
C’est encore une fois la notion de jointure externe qui vient à notre secours pour récupérer les enregistrements sans correspondance:
mysql> select emp.nom employe, chef.nom chef
-> from employes chef right outer join employes emp
-> on emp.mgr = chef.no_employe;
+————-+———–+
| employe | chef |
+————-+———–+
| BAILLARGEON | GRIM |
| FRANCHI | PIANETTI |
| OUVRARD | PIANETTI |
| MURDOCK | CHIRAC |
| MARTIN | PIANETTI |
| PIANETTI | CHIRAC |
| DELEGLISE | CHIRAC |
| GORLIN | MURDOCK |
| CHIRAC | NULL |
| HULK | PIANETTI |
| PARKER | GORLIN |
| XAVIER | PIANETTI |
| GRIM | MURDOCK |
| LEE | DELEGLISE |
+————-+———–+
14 rows in set (0.00 sec)
On a bien désormais récupéré Mr Chirac qui (par définition) n’a pas de chef…
- Donner le nombre d’employés de chaque département avec des employés.
mysql> SELECT COUNT(E.no_employe) ‘Nb emp’, S.no_service, S.nom
-> from employes E INNER JOIN services S
-> ON E.no_service = S.no_service
-> group by S.no_service;
+——–+————+————–+
| Nb emp | no_service | nom |
+——–+————+————–+
| 3 | 10 | COMPTABILITE |
| 5 | 20 | RECHERCHE |
| 6 | 30 | VENTES |
+——–+————+————–+
3 rows in set (0.00 sec)
- Donner le nombre d’employés de chaque département y compris ceux qui sont vides.
Mysql> SELECT COUNT(E.no_employe) ‘nb emp’, S.no_service, S.nom
from employes E RIGHT OUTER JOIN services S
ON E.no_service = S.no_service
group by S.no_service;
+——–+————+————–+
| nb emp | no_service | nom |
+——–+————+————–+
| 3 | 10 | COMPTABILITE |
| 5 | 20 | RECHERCHE |
| 6 | 30 | VENTES |
| 0 | 40 | OPERATIONS |
+——–+————+————–+
4 rows in set (0.00 sec)
- Donner la liste des employés et de leurs chefs et la ville dans laquelle ils travaillent.
mysql> SELECT E.no_employe, E.nom, M.nom, S.lieu
-> FROM employes E
-> INNER JOIN employes M INNER JOIN services s
-> ON E.MGR = M.no_employe
-> AND E.no_service = S.no_service;
+————+————-+———–+———-+
| no_employe | nom | nom | lieu |
+————+————-+———–+———-+
| 7782 | DELEGLISE | CHIRAC | MARSEILLE |
| 7934 | LEE | DELEGLISE | MARSEILLE |
| 7369 | BAILLARGEON | GRIM | TOULOUSE |
| 7566 | MURDOCK | CHIRAC | TOULOUSE |
| 7788 | GORLIN | MURDOCK | TOULOUSE |
| 7876 | PARKER | GORLIN | TOULOUSE |
| 7902 | GRIM | MURDOCK | TOULOUSE |
| 7499 | FRANCHI | PIANETTI | BORDEAUX |
| 7521 | OUVRARD | PIANETTI | BORDEAUX |
| 7654 | MARTIN | PIANETTI | BORDEAUX |
| 7698 | PIANETTI | CHIRAC | BORDEAUX |
| 7844 | HULK | PIANETTI | BORDEAUX |
| 7900 | XAVIER | PIANETTI | BORDEAUX |
+————+————-+———–+———-+
13 rows in set (0.00 sec)


