14-11-2010, 03:28 PM
Citation :The conditional_expr used with ON is any conditional expression of the form that can be used in a WHERE clause. Generally, you should use the ON clause for conditions that specify how to join tables, and the WHERE clause to restrict which rows you want in the result set.
Citation :If there is no matching row for the right table in the ON or USING part in a LEFT JOIN, a row with all columns set to NULL is used for the right table. You can use this fact to find rows in a table that have no counterpart in another table:
SELECT left_tbl.*
FROM left_tbl LEFT JOIN right_tbl ON left_tbl.id = right_tbl.id
WHERE right_tbl.id IS NULL;
This example finds all rows in left_tbl with an id value that is not present in right_tbl (that is, all rows in left_tbl with no corresponding row in right_tbl). This assumes that right_tbl.id is declared NOT NULL.
La clause ON est une clause qui permet de renseigner les conditions de jointures des tables. Il n'est pas question de restriction de sélection, juste d'indiquer quels champs permet de les joindre. On constate encore plus l'incohérence de mettre des expressions conditionnelles dans la clause ON quand on utilise à la place la clause USING. Il n'est pas possible d'écrire d'expressions conditionnelles, on ne peut que préciser les colonnes à prendre en compte pour la jointure.
Pour comprendre la différence entre le prédicat de la clause de jointure et le prédicat du filtre WHERE, voyons le petit test suivant :
-- Test de jointure
CREATE TABLE TEST_JOIN1
(COL1 INT,
COL2 CHAR(2));
CREATE TABLE TEST_JOIN2
(COL1 INT,
COL2 CHAR(2));
INSERT INTO TEST_JOIN1 VALUES (101, 'AA');
INSERT INTO TEST_JOIN1 VALUES (102, 'AA');
INSERT INTO TEST_JOIN1 VALUES (103, 'BB');
INSERT INTO TEST_JOIN2 VALUES (101, 'AA');
INSERT INTO TEST_JOIN2 VALUES (102, 'AA');
INSERT INTO TEST_JOIN2 VALUES (201, 'BB');
-- L'équivalence est uniquement une apparence...
SELECT TJ1.COL1, TJ1.COL2
FROM TEST_JOIN1 TJ1
JOIN TEST_JOIN2 TJ2
ON TJ1.COL1 = TJ2.COL1
WHERE TJ1.COL2 = 'AA';
SELECT TJ1.COL1, TJ1.COL2
FROM TEST_JOIN1 TJ1
JOIN TEST_JOIN2 TJ2
ON TJ1.COL1 = TJ2.COL1 AND TJ1.COL2 = 'AA';
-- D'accord... Les résultats sont identiques.
-- On change le lien interne en lien externe...
SELECT TJ1.COL1, TJ1.COL2
FROM TEST_JOIN1 TJ1
LEFT OUTER JOIN TEST_JOIN2 TJ2
ON TJ1.COL1 = TJ2.COL1
WHERE TJ1.COL2 = 'AA';
SELECT TJ1.COL1, TJ1.COL2
FROM TEST_JOIN1 TJ1
LEFT OUTER JOIN TEST_JOIN2 TJ2
ON TJ1.COL1 = TJ2.COL1 AND TJ1.COL2 = 'AA';
-- Les résultats sont différents ! Pourquoi ?
Une chose intéressante aussi est qu'il possible d'obtenir le même résultat que la dernière en faisant :
SELECT TJ1.COL1, TJ1.COL2
FROM TEST_JOIN1 TJ1
LEFT OUTER JOIN TEST_JOIN2 TJ2
ON TJ1.COL1 = TJ2.COL1 AND TJ1.COL2 = 'taratata' AND 1 = 0
So, what the fuck?
La clause WHERE est un filtre d'élimination. Il suppose que les différentes tables sont déjà jointes en une seule et parcourt l'ensemble des résultats à la recherche des lignes correspondant aux critères donnés.
En revanche, la clause JOIN se comporte différemment. Elle agit AVANT que la jointure soit effective.
Dans le cadre d'une jointure interne le comportement est similaire à celui d'une clause WHERE.
Dans le cas d'une jointure externe il faut décomposer la logique en plusieurs étapes :
- Évaluation des éléments du prédicat
- Application de la jointure : lignes jointe en 1) + lignes sans correspondance
Reprenons la clause de jointure de la requête donnée :
Code :
TJ1.COL1 = TJ2.COL1 AND TJ1.COL2 = 'taratata' AND 1 = 0
^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^ ^^^^^
prédicat de jointure prédicats hors jointure
- 1 = 0 => aucune ligne n'est récupérée donc aucune ligne de TEST_JOIN1 n'a de correspondance avec TEST_JOIN2
Conclusion : toutes les lignes de TEST_JOIN1 seront reprises du fait de la jointure externe
- TJ1.COL2 = 'taratata' => encore une fois aucune ligne n'est récupérée donc aucune ligne de TEST_JOIN1 n'a de correspondance avec TEST_JOIN2
Conclusion : toutes les lignes de TEST_JOIN1 seront reprises du fait de la jointure externe
- TJ1.COL1 = TJ2.COL1 => 2 lignes sont en correspondance avec TEST_JOIN2
Conclusion : Ces deux lignes seront récupérées du fait de la jointure externe et la troisième ligne aussi puisqu'elle n'a aucune correspondance
…
Pour comprendre la différence, il suffit de demander à voir les colonnes de la table TEST_JOIN2 dans les différents cas :
SELECT TJ1.COL1, TJ1.COL2, TJ2.COL1, TJ2.COL2
FROM TEST_JOIN1 TJ1
LEFT OUTER JOIN TEST_JOIN2 TJ2
ON TJ1.COL1 = TJ2.COL1 AND TJ1.COL2 = 'AA'
Code :
COL1 COL2 COL1 COL2
----------- ---- ----------- ----
101 AA 101 AA
102 AA 102 AA
103 BB NULL NULL
SELECT TJ1.COL1, TJ1.COL2, TJ2.COL1, TJ2.COL2
FROM TEST_JOIN1 TJ1
LEFT OUTER JOIN TEST_JOIN2 TJ2
ON TJ1.COL1 = TJ2.COL1
WHERE TJ1.COL2 = 'AA'
Code :
COL1 COL2 COL1 COL2
----------- ---- ----------- ----
101 AA 101 AA
102 AA 102 AA
SELECT TJ1.COL1, TJ1.COL2, TJ2.COL1, TJ2.COL2
FROM TEST_JOIN1 TJ1
LEFT OUTER JOIN TEST_JOIN2 TJ2
ON TJ1.COL1 = TJ2.COL1 AND TJ1.COL2 = 'taratata' AND 1 = 0
Code :
COL1 COL2 COL1 COL2
----------- ---- ----------- ----
101 AA NULL NULL
102 AA NULL NULL
103 BB NULL NULL
Voilà.
En bref, au niveau performance, cela ne change rien. Par contre, niveau conceptuel, il est beaucoup plus intéressant de différencier ce qui appartient à la jointure et ce qui appartient à la limitation du jeu de résultat. C'est d'autant plus intéressant que c'est humainement plus maintenable et plus lisible à mesure que la requête est conséquente.
Sources :