LE SQL de A A Z - 3e Partie - L - SQLPro
LE SQL de A A Z - 3e Partie - L - SQLPro
Cette requête ne possède pas de critère de jointure entre une table et l'autre. Dans
ce cas, le compilateur SQL calcule le produit cartésien des deux ensembles, c'est-à-
dire qu'à chaque ligne de la première table, il accole l'ensemble des lignes de la
seconde à la manière d'une « multiplication des petits pains » !
Nous verrons qu'il existe une autre manière, normalisée cette fois, de générer ce
produit cartésien. Mais cette requête est à proscrire.
Dans notre exemple elle génère 17 400 lignes !
Il faut donc définir absolument un critère de jointure.
Dans le cas présent, ce critère est la correspondance entre les colonnes contenant
la référence de l'identifiant du client (CLI_ID).
Exemple 2 :
CLI_NOM
TEL_NUMERO
SELECT CLI_NOM, ------- -----
TEL_NUMERO ---------
FROM T_CLIENT, DUPONT 01-
T_TELEPHONE 45-42-56-63
WHERE CLI_ID = DUPONT 01-
CLI_ID 44-28-52-52
DUPONT 01-
44-28-52-50
DUPONT 06-
Nous n'avons pas fait mieux, car nous avons créé une clause toujours vraie, un peu
à la manière de 1 = 1 !
En fait il nous manque une précision : il s'agit de déterminer de quelles tables
proviennent les colonnes CLI_ID de droite et de gauche. Cela se précise à l'aide
d'une notation pointée en donnant le nom de la table.
Il est donc nécessaire d'indiquer au compilateur la provenance de chacune des
colonnes CLI_ID et donc d'opérer une distinction entre l'une et l'autre colonne.
Ainsi, chaque colonne devra être précédée du nom de la table, suivi d'un point.
Exemple 3 :
SELECT CLI_NOM, CLI_NOM
TEL_NUMERO TEL_NUMERO
FROM T_CLIENT, ------- --
T_TELEPHONE ------------
WHERE T_CLIENT.CLI_ID = DUPONT
T_TELEPHONE.CLI_ID 01-45-42-56-
63
DUPONT
01-44-28-52-
52
CLI_NOM
TEL_NUMERO
SELECT CLI_NOM, ------- ---
TEL_NUMERO -----------
FROM T_CLIENT C, DUPONT 01-
T_TELEPHONE T 45-42-56-63
WHERE C.CLI_ID = DUPONT 01-
T.CLI_ID 44-28-52-52
DUPONT 01-
44-28-52-50
BOUVIER 06-
Le fait de placer comme critère de jointure entre les tables, l'opérateur logique
« égal » donne ce que l'on appelle une « équijointure ».
Exemple 6 :
En effet :
les jointures faites dans la clause WHERE (ancienne syntaxe de 1986 !) ne
permettent pas de faire la distinction de prime abord entre ce qui relève du
filtrage et ce qui relève de la jointure ;
il est à priori absurde de vouloir filtrer dans le WHERE (ce qui restreint les
données du résultat) et de vouloir « élargir » ce résultat par une jointure dans la
même clause WHERE de filtrage ;
la lisibilité des requêtes est plus grande en utilisant la syntaxe à base de JOIN,
en isolant ce qui est du filtrage et de la jointure, mais aussi en isolant avec
clarté chaque condition de jointures entre chaque couple de tables ;
l'optimisation d'exécution de la requête est souvent plus pointue du fait de
l'utilisation du JOIN ;
lorsque l'on utilise l'ancienne syntaxe et que l'on supprime la clause WHERE à
des fins de tests, le moteur SQL réalise le produit cartésien des tables ce qui
revient la plupart du temps à mettre à genoux le serveur !
III - Syntaxe normalisée des jointures
Vous trouverez des compléments d'information sur le sujet aux pages 136 à 152 de
l'ouvrage « SQL », collection « La Référence », Campus Press éditeur.
Les jointures normalisées s'expriment à l'aide du mot clef JOIN dans la clause
FROM. Suivant la nature de la jointure, on devra préciser sur quels critères se base
la jointure.
Voici un tableau résumant les différents types de jointures normalisées :
SELECT ...
FROM <table gauche>
Jointure [INNER]JOIN
interne <table droite>
ON <condition
de jointure>
SELECT ...
FROM <table gauche>
LEFT | RIGHT |
Jointure
FULL OUTER JOIN <table
externe
droite>
ON
condition de jointure
SELECT ...
FROM <table gauche>
Jointure NATURAL JOIN
naturelle <table droite>
[USING
<noms de colonnes>]
SELECT ...
Jointure FROM <table gauche>
croisée CROSS JOIN
<table droite>
Jointure SELECT ...
d'union FROM <table gauche>
UNION JOIN
<table droite>
L'opérateur NATURAL JOIN permet d'éviter de préciser les colonnes concernées par
la jointure.
Dans ce cas, le compilateur SQL va rechercher dans les deux tables, les colonnes
dont le nom est identique. Bien entendu, le type de données doit être le même !
NOTA : on veillera au niveau de la modélisation et notamment au niveau du MPD
(Modèle Physique de Données) que les noms des colonnes de clefs en relation avec
d'autres tables par l'intermédiaire des clefs étrangères soient strictement identiques.
Exemple 8 :
CLI_NOM
TEL_NUMERO
------- --
SELECT CLI_NOM, -----------
TEL_NUMERO -
FROM T_CLIENT DUPONT
NATURAL JOIN 01-45-42-
T_TELEPHONE 56-63
DUPONT
01-44-28-
52-52
Mais cette syntaxe est rarement acceptée par les moteurs SQL actuels !
La partie optionnelle USING permet de restreindre les colonnes concernées,
lorsque plusieurs colonnes servent à définir la jointure naturelle. Ainsi la commande
SQL :
SELECT CLI_NOM, TEL_NUMERO
FROM T_CLIENT
NATURAL JOIN T_TELEPHONE
USING (CLI_ID)
Revient au même que la commande SQL de l'exemple 8.
III-B - Les jointures internes
Comme il s'agit de la plus commune des jointures, c'est celle qui s'exerce par défaut
si on ne précise pas le type de jointure. Après le mot clef ON on doit préciser le
critère de jointure.
Reprenons notre exemple de départ :
Exemple 9 :
CLI_NOM
TEL_NUMERO
SELECT CLI_NOM, TEL_NUMERO -------
FROM T_CLIENT ----------
INNER JOIN ----
T_TELEPHONE DUPONT
ON 01-45-42-
T_CLIENT.CLI_ID = 56-63
T_TELEPHONE.CLI_ID DUPONT
01-44-28-
52-52
Ou en utilisant le surnommage :
Exemple 10 :
CLI_NOM
TEL_NUMERO
SELECT CLI_NOM, ------- --
TEL_NUMERO ------------
FROM T_CLIENT C DUPONT
JOIN 01-45-42-56-
T_TELEPHONE T 63
ON DUPONT
C.CLI_ID = T.CLI_ID 01-44-28-52-
52
DUPONT
Que faut-il modifier dans la requête pour obtenir une ligne « BOUVIER » avec
aucune référence de téléphone associée dans la réponse ?
Il suffit en fait d'opérer à l'aide d'une jointure externe :
Exemple 13 :
ou encore :
SELECT CLI_NOM, TEL_NUMERO
FROM T_CLIENT C
LEFT OUTER JOIN T_TELEPHONE T
ON C.CLI_ID = T.CLI_ID AND TYP_CODE IS
NULL
La syntaxe de la jointure externe est la suivante :
SELECT ...
FROM <table gauche>
LEFT | RIGHT | FULL OUTER JOIN <table droite
1>
ON <condition de jointure>
[LEFT | RIGHT | FULL OUTER JOIN <table droite
2>
ON <condition de jointure 2>]
...
Les mots clefs LEFT, RIGHT et FULL indiquent la manière dont le moteur de
requête doit effectuer la jointure externe. Ils font référence à la table située à gauche
(LEFT) du mot clef JOIN ou à la table située à droite (RIGHT) de ce même mot clef.
Le mot FULL indique que la jointure externe est bilatérale.
SELECT colonnes
FROM TGauche LEFT OUTER JOIN TDroite ON condition
de jointure
SELECT colonnes
FROM TGauche RIGHT OUTER JOIN TDroite ON condition
de jointure
SELECT colonnes
FROM TGauche FULL OUTER JOIN TDroite ON condition
de jointure
est :
SELECT CLI_NOM, TEL_NUMERO
FROM T_TELEPHONE T
LEFT OUTER JOIN T_CLIENT C
ON C.CLI_ID = T.CLI_ID
WHERE TYP_CODE = 'FAX'
Remplacement d'un FULL OUTER JOIN avec jointures externes gauche et droite :
L'équivalent logique de…
Exemple 15 :
SELECT CLI_NOM, TEL_NUMERO
FROM T_CLIENT C
FULL OUTER JOIN T_TELEPHONE T
ON C.CLI_ID = T.CLI_ID
WHERE TYP_CODE = 'FAX'
est :
est :
NOTA : Sur certains moteurs SQL la jointure bilatérale externe (FULL OUTER JOIN)
s'exprime :
SELECT colonnes
FROM TGauche FULL JOIN TDroite ON condition de
jointure
ou encore :
SELECT colonnes
FROM table_1 t1, table_2 t2
WHERE t1.id1 (+)= t2.id2
Bonjour a tous,
Je me pose une petite question de syntaxe SQL sur
les jointures.
Et comme Frédéric va surement me répondre, je
prends un exemple de son site.
Y a-t-il une syntaxe meilleure que l'autre (si
oui pourquoi) ?
Date : 26 Dec 2002 14:20:39 -0800
De : Med Bouchenafa
-- test de jointure
Salut Fred,
La jointure croisée n'est autre que le produit cartésien de deux tables. Rappelons
que le produit cartésien de deux ensembles n'est autre que la multiplication
généralisée. Dans le cas des tables, c'est le fait d'associer à chacune des lignes de
la première table, toutes les lignes de la seconde. Ainsi, si la première table compte
267 lignes et la seconde 1214, on se retrouve avec un résultat contenant 324 138
lignes. Bien entendu, s'il n'y a aucun doublon dans les tables, toutes les lignes du
résultat seront aussi uniques.
La jointure croisée peut s'écrire de deux manières différentes :
à l'aide de l'opérateur normalisé :
SELECT colonnes
FROM table_1 CROSS JOIN table_2
ou à l'aide d'une clause FROM simplifiée :
SELECT colonnes
FROM table_1, table_2
Ce qui est certainement l'expression la plus minimaliste de tous les ordres SELECT
du SQL !
Nous voudrions savoir si notre table des tarifs de chambre (TJ_TRF_CHB) est
complète, c'est-à-dire si l'on a bien toutes les chambres (T_CHAMBRE) pour toutes
les dates de début de tarif (T_TARIF) :
Exemple 18 :
En comparant rapidement le contenu des deux tables, on peut s'assurer que tous les
tarifs ont bien été renseignés.
Avec la syntaxe normalisée, cette requête s'écrit :
Exemple 19 :
CHB_ID
TRF_DATE_DEBUT
SELECT CHB_ID, TRF_CHB_PRIX
TRF_DATE_DEBUT, 0 AS ------ -------
TRF_CHB_PRIX ------- ------
FROM T_TARIF CROSS ------
JOIN T_CHAMBRE 1 1999-
ORDER BY CHB_ID, 01-01
TRF_DATE_DEBUT 0,00
1 1999-
09-01
En fait c'est comme si l'on avait listé la première table, puis la seconde en évitant
toute colonne commune et compléter les espaces vides des valeurs NULL.
Mais cette jointure est très rarement implantée dans les moteurs SQL.
NOTA : si l'opérateur UNION JOIN n'est pas présent dans votre moteur, vous
pouvez le fabriquer comme suit, car elle (l'union) peut être facilement remplacée par
un autre ordre SQL presque aussi simple :
SELECT *
FROM <table gauche>
FULL OUTER JOIN <table droite>
ON <critère>
Où la condition <critère> est n'importe quel prédicat valant toujours faux comme
« 1=2 ».
En dehors du fait de linéariser des tables hétérogènes, il est franchement permis de
douter de l'utilité d'un tel opérateur.
IV - Nature des conditions de jointures
Nous allons maintenant analyser les différentes jointures basées sur la nature des
conditions pour bien les distinguer.
IV-A - Équijointure
L'équijointure consiste à opérer une jointure avec une condition d'égalité. Cette
condition d'égalité dans la jointure peut ne pas porter nécessairement sur les clefs
(primaires et étrangères).
Recherchons par exemple les clients dont le nom est celui d'une ville contenu dans
la table des adresses :
Exemple 21 :
CLI_ID
SELECT DISTINCT
CLI_NOM
C.CLI_ID, C.CLI_NOM,
ADR_VILLE
A.ADR_VILLE
------ --
FROM T_CLIENT C
----- ---
JOIN
------
T_ADRESSE A
92
ON
PARIS
C.CLI_NOM =
PARIS
A.ADR_VILLE
...
Nous avons donc bien réalisé une équijointure, mais elle n'est pas naturelle parce
qu'elle ne repose pas sur les clefs des tables.
Bien entendu, il existe un opérateur normalisé SQL 2 permettant de traiter le cas de
l'équijointure :
SELECT [DISTINCT ou ALL] * ou liste de colonnes
FROM table1 [INNER] JOIN table2 ON condition de
jointure
Le mot clef INNER n'étant pas obligatoire, mais voulant s'opposer aux mots clefs
OUTER, UNION et CROSS.
Ainsi, la requête précédente s'écrit à l'aide de cette syntaxe :
Exemple 22 :
IV-B - Non-équijointure
Il s'agit là d'utiliser n'importe quelle condition de jointure entre deux tables, excepté
la stricte égalité. Ce peuvent être les conditions suivantes :
> supérieur
supérieur ou
>=
égal
< inférieur
<> différent de
dans un
IN
ensemble
correspondance
LIKE
partielle
NOTA : pour récupérer toutes les colonnes d'une table, on peut utiliser l'opérateur *
suffixé par le nom de table, comme nous l'avons fait ici pour la table des factures.
Si notre table des tarifs avait été organisée par tranches, comme ceci :
TRF_DATE_DEBUT TRF_DATE_FIN TRF_TAUX_TAXES
TRF_PETIT_DEJEUNE
-------------- ------------ -------------- ------
-----------
1999-01-01 1999-08-31 18,60
6,00 E
1999-09-01 1999-12-31 20,60
7,00 E
2000-01-01 2000-08-31 20,60
8,00 E
2000-09-01 2000-12-31 20,60
alors,récupérer le tarif des chambres pour chacune des dates du planning devient un
exercice très simple :
Exemple 24 :
IV-C - Autojointure
Le problème consiste à joindre une table à elle-même. Il est assez fréquent que l'on
ait besoin de telles autojointures, car elles permettent notamment de modéliser des
structures de données complexes comme des arbres. Voici quelques exemples de
relation nécessitant une autojointure de tables :
dans une table des employées, connaître le supérieur hiérarchique de tout
employé ;
dans une table de nomenclature savoir quels sont les composants nécessaires
à la réalisation d'un module, ou les modules nécessaires à la réalisation d'un
appareil ;
dans une table de personnes, retrouver l'autre moitié d'un couple marié.
La représentation d'une telle jointure dans le modèle de données, se fait par le
rajout d'une colonne contenant une pseudo clef étrangère basée sur la clef de la
table.
Dans ce cas, une syntaxe possible pour l'autojointure est la suvante :
SELECT [DISTINCT ou ALL] * ou liste de colonnes
FROM laTable t1
INNER JOIN laTable t2
ON t1.laClef = t2.laPseudoClefEtrangère
C'est l'exemple typique ou l'utilisation de surnoms pour les tables est obligatoire,
sinon il y a risque de confusion pour le moteur SQL.
Pour donner un exemple concret à nos propos, nous allons modéliser le fait qu'une
chambre puisse communiquer avec une autre (par une porte). Dès lors, le challenge
est de trouver quelles sont les chambres qui communiquent entre elles par exemple
pour réaliser une sorte de suite. Pour ce faire, nous allons ajouter à notre table des
chambres une colonne de clef étrangère basée sur la clef de la table.
Dans ce cas, cette colonne doit obligatoirement accepter des valeurs nulles !
Voici l'ordre SQL pour rajouter la colonne CHB_COMMUNIQUE dans la table
T_CHAMBRE :
ALTER TABLE T_CHAMBRE ADD CHB_COMMUNIQUE INTEGER
Alimentons là de quelques valeurs exemples en considérant que la 7 communique
avec la 9 et la 12 avec la 14 :
UPDATE T_CHAMBRE SET CHB_COMMUNIQUE = 9 WHERE CHB_ID
= 7
UPDATE T_CHAMBRE SET CHB_COMMUNIQUE = 7 WHERE CHB_ID
= 9
UPDATE T_CHAMBRE SET CHB_COMMUNIQUE = 12 WHERE
CHB_ID = 14
UPDATE T_CHAMBRE SET CHB_COMMUNIQUE = 14 WHERE
CHB_ID = 12
Pour formuler la recherche de chambres communicantes, il suffit de faire la requête
suivante :
Exemple 25 :
SELECT DISTINCT
c1.CHB_ID, CHB_ID
c1.CHB_COMMUNIQUE CHB_COMMUNIQUE
SELECT colonnes
FROM table1 NATURAL JOIN table2 [USING col1, col2
... ]
[WHERE prédicat] ...
Le mot clef USING permet de restreindre les colonnes communes à prendre en
considération.
Jointure interne : la jointure s'effectue entre les tables sur les colonnes
précisées dans la condition de jointure :
SELECT colonnes
FROM table1 t1 [INNER ] JOIN table2 t2 ON condition
[WHERE prédicat] ...
Jointure externe : la jointure permet de récupérer les lignes des tables
correspondant au critère de jointure, mais aussi celles pour lesquelles il n'existe
pas de correspondance.
SELECT colonnes
FROM table1 t1 [RIGHT OUTER | LEFT OUTER | FULL
OUTER ] JOIN table2 t2 ON condition
[WHERE prédicat] ...
RIGHT OUTER : la table à droite de l'expression clef « RIGHT OUTER »
renvoie des lignes sans correspondance avec la table à gauche.
LEFT OUTER : la table à gauche de l'expression clef « LEFT OUTER » renvoie
des lignes sans correspondance avec la table à droite.
FULL OUTER : les deux tables renvoient des lignes sans correspondance
entre elles.
Jointure croisée : la jointure effectue le produit cartésien (la « multiplication »)
des deux tables.
Il n'y a pas de condition.
SELECT colonnes
FROM table1 t1 CROSS JOIN table2 t2
[WHERE prédicat] ...
Jointure d'union : la jointure concatène les tables sans aucune
correspondance de colonnes.
SELECT colonnes
FROM table1 UNION JOIN table2
Il n'y a pas de critère de jointure.
SQL
Oracle DB2
Paradox Access PostGreSQL Sybase Server
8 (400)
7
INNER Non
Oui Oui Oui Oui Oui Oui
JOIN (1)
CROSS Non
Non (3) Non (3) Oui Non (3) Oui Oui
JOIN (1) (3)
(1) Oracle ne connaît toujours pas le JOIN (ça fait quand même plus de dix ans de
retard pour cet éditeur pionnier qui semble s'endormir sur ses lauriers). Il faut donc
utiliser une syntaxe propriétaire. Exception : la version 9 supporte enfin les jointures
normalisées.
(4) de plus IBM DB2 (400) dispose d'un très intéressant « exception join » équivalent
à:
SELECT *
FROM tablegauche
LEFT OUTER JOIN tabledroite
ON références de jointure
WHERE tabledroite.clef IS NULL
Données concernant DB2 400 aimablement communiquées par kerigan.