Tous les produits
Search
Centre de documentation

MaxCompute:Opérations JOIN dans MaxCompute SQL

Dernière mise à jour :Aug 10, 2026

Placer une condition de filtre dans la mauvaise clause d'une requête JOIN peut produire des résultats incorrects de manière silencieuse, en particulier avec les jointures externes. Cette rubrique explique comment chaque type de JOIN gère les conditions de filtre placées dans les sous-requêtes, la clause ON et la clause WHERE externe, afin que vous puissiez écrire des requêtes qui retournent exactement le résultat attendu.

Types de JOIN pris en charge

MaxCompute SQL prend en charge les opérations JOIN suivantes.

Opération Description
INNER JOIN Renvoie les lignes dont les valeurs de colonne correspondent dans les deux tables.
LEFT JOIN Renvoie toutes les lignes de la table de gauche. Pour les lignes de la table de gauche sans correspondance dans la table de droite, des valeurs NULL apparaissent dans les colonnes de la table de droite.
RIGHT JOIN Renvoie toutes les lignes de la table de droite. Pour les lignes de la table de droite sans correspondance dans la table de gauche, des valeurs NULL apparaissent dans les colonnes de la table de gauche.
FULL JOIN Renvoie toutes les lignes des deux tables. Lorsqu'une ligne n'a pas de correspondance dans l'autre table, des valeurs NULL remplissent les colonnes non appariées.
LEFT SEMI JOIN Renvoie les lignes de la table de gauche qui ont au moins une correspondance dans la table de droite. Les lignes de la table de droite ne sont pas incluses dans le résultat.
LEFT ANTI JOIN Renvoie les lignes de la table de gauche qui n'ont aucune correspondance dans la table de droite. Les lignes de la table de droite ne sont pas incluses dans le résultat. Souvent utilisé pour remplacer NOT EXISTS.

Impact du placement des conditions de filtre sur les résultats

Une seule instruction SQL peut combiner des filtres de sous-requête, une clause ON et une clause WHERE externe :

SELECT *
FROM
  (SELECT * FROM A WHERE <subquery_filter_A>) A
JOIN
  (SELECT * FROM B WHERE <subquery_filter_B>) B
ON <on_condition>
WHERE <where_condition>

MaxCompute évalue ces conditions dans l'ordre suivant :

  1. <subquery_filter> — Conditions WHERE à l'intérieur des sous-requêtes

  2. <on_condition> — La clause ON

  3. <where_condition> — La clause WHERE après le JOIN

En raison de cet ordre d'évaluation, un même filtre logique peut produire des résultats différents selon son emplacement.

Important

Pour les JOIN externes (LEFT JOIN, RIGHT JOIN, FULL JOIN), le filtrage sur les colonnes de la table fournissant les valeurs NULL dans la clause WHERE externe élimine les lignes portant des valeurs NULL — c'est-à-dire les lignes où la table préservée n'avait pas de correspondance. Cela convertit effectivement la jointure externe en INNER JOIN. Pour préserver le comportement de la jointure externe, placez ces filtres dans la clause ON ou dans une sous-requête.

Terminologie des JOIN externes utilisée dans cette rubrique :

Terme Définition Exemples
Table préservée La table dont toutes les lignes doivent apparaître dans le résultat Table de gauche dans LEFT JOIN ; table de droite dans RIGHT JOIN ; les deux tables dans FULL JOIN
Table fournissant les valeurs NULL La table qui contribue par des valeurs NULL pour les lignes non appariées Table de droite dans LEFT JOIN ; table de gauche dans RIGHT JOIN ; les deux tables dans FULL JOIN

Tables de test

Les exemples de cette rubrique utilisent deux tables, A et B.

Table A

CREATE TABLE A AS SELECT * FROM VALUES (1, 20180101),(2, 20180101),(2, 20180102) t (key, ds);
key ds
1 20180101
2 20180101
2 20180102

Table B

CREATE TABLE B AS SELECT * FROM VALUES (1, 20180101),(3, 20180101),(2, 20180102) t (key, ds);
key ds
1 20180101
3 20180101
2 20180102

Le produit cartésien de A et B comporte 9 lignes. Activez les requêtes de produit cartésien avec :

SET odps.sql.allow.cartesian=true;
SELECT * FROM A, B;

Résultat :

+------+----------+------+----------+
| key  | ds       | key2 | ds2      |
+------+----------+------+----------+
| 1    | 20180101 | 1    | 20180101 |
| 2    | 20180101 | 1    | 20180101 |
| 2    | 20180102 | 1    | 20180101 |
| 1    | 20180101 | 3    | 20180101 |
| 2    | 20180101 | 3    | 20180101 |
| 2    | 20180102 | 3    | 20180101 |
| 1    | 20180101 | 2    | 20180102 |
| 2    | 20180101 | 2    | 20180102 |
| 2    | 20180102 | 2    | 20180102 |
+------+----------+------+----------+

INNER JOIN

Un INNER JOIN renvoie les lignes du produit cartésien qui satisfont la condition de jointure.

Résultat : Le placement de la condition de filtre n'affecte pas le résultat.

Cas 1 — filtre dans la sous-requête :

SELECT A.*, B.*
FROM
  (SELECT * FROM A WHERE ds='20180101') A
JOIN
  (SELECT * FROM B WHERE ds='20180101') B
ON a.key = b.key;

Cas 2 — filtre dans la clause ON :

SELECT A.*, B.*
FROM A JOIN B
ON a.key = b.key AND A.ds='20180101' AND B.ds='20180101';

Cas 3 — filtre dans la clause WHERE externe :

SELECT A.*, B.*
FROM A JOIN B
ON a.key = b.key
WHERE A.ds='20180101' AND B.ds='20180101';

Les trois cas renvoient :

+------+----------+------+----------+
| key  | ds       | key2 | ds2      |
+------+----------+------+----------+
| 1    | 20180101 | 1    | 20180101 |
+------+----------+------+----------+

Pour INNER JOIN, le moteur de requête applique d'abord la condition de jointure (produisant 3 lignes correspondantes à partir des 9 lignes du produit cartésien), puis applique le filtre WHERE externe. Comme les deux étapes doivent être satisfaites, le résultat final est identique quel que soit l'emplacement.

LEFT JOIN

Un LEFT JOIN renvoie toutes les lignes de la table de gauche (table préservée). Les lignes de la table de gauche sans correspondance dans la table de droite apparaissent avec des valeurs NULL dans les colonnes de la table de droite.

Résultat : Le placement de la condition de filtre affecte le résultat.

Attention : Le filtrage sur une colonne de la table de droite (fournissant les valeurs NULL) dans la clause WHERE externe élimine toutes les lignes portant des valeurs NULL — les lignes où la table de gauche n'avait pas de correspondance. Cela convertit le LEFT JOIN en un INNER JOIN effectif.
  • Pour les filtres sur la table de gauche : un filtre de sous-requête et un filtre WHERE externe produisent le même résultat.

  • Pour les filtres sur la table de droite : un filtre de sous-requête et un filtre de clause ON produisent le même résultat.

Cas 1 — filtre dans la sous-requête :

SELECT A.*, B.*
FROM
  (SELECT * FROM A WHERE ds='20180101') A
LEFT JOIN
  (SELECT * FROM B WHERE ds='20180101') B
ON a.key = b.key;

Résultat (2 lignes — lignes de la table de gauche avec ds='20180101', correspondance dans la table de droite ou NULL) :

+------+----------+------+----------+
| key  | ds       | key2 | ds2      |
+------+----------+------+----------+
| 1    | 20180101 | 1    | 20180101 |
| 2    | 20180101 | NULL | NULL     |
+------+----------+------+----------+

Cas 2 — filtre dans la clause ON :

SELECT A.*, B.*
FROM A LEFT JOIN B
ON a.key = b.key AND A.ds='20180101' AND B.ds='20180101';

La clause ON filtre les lignes de B pouvant correspondre, mais toutes les lignes de A sont toujours préservées. Le produit cartésien de 9 lignes contient 1 paire correspondante. Les 2 lignes A non appariées renvoient NULL pour les colonnes B.

Résultat (3 lignes — toutes les lignes de A, correspondance ou NULL) :

+------+----------+------+----------+
| key  | ds       | key2 | ds2      |
+------+----------+------+----------+
| 1    | 20180101 | 1    | 20180101 |
| 2    | 20180101 | NULL | NULL     |
| 2    | 20180102 | NULL | NULL     |
+------+----------+------+----------+

Cas 3 — filtre dans la clause WHERE externe :

SELECT A.*, B.*
FROM A LEFT JOIN B
ON a.key = b.key
WHERE A.ds='20180101' AND B.ds='20180101';

La jointure produit des lignes avec NULL dans B.ds pour les lignes A non appariées. Le filtre WHERE B.ds='20180101' ne peut pas correspondre à NULL, donc ces lignes sont éliminées.

Résultat (1 ligne — identique à INNER JOIN) :

+------+----------+------+----------+
| key  | ds       | key2 | ds2      |
+------+----------+------+----------+
| 1    | 20180101 | 1    | 20180101 |
+------+----------+------+----------+

RIGHT JOIN

RIGHT JOIN est le miroir de LEFT JOIN avec les tables inversées. Toutes les lignes de la table de droite (table préservée) sont renvoyées, et les colonnes de la table de gauche contiennent NULL lorsqu'il n'y a pas de correspondance.

Résultat : Le placement de la condition de filtre affecte le résultat.

  • Pour les filtres sur la table de droite : un filtre de sous-requête et un filtre WHERE externe produisent le même résultat.

  • Pour les filtres sur la table de gauche : un filtre de sous-requête et un filtre de clause ON produisent le même résultat.

Le même piège s'applique : le filtrage sur une colonne de la table de gauche (fournissant les valeurs NULL) dans la clause WHERE externe élimine les lignes portant des valeurs NULL, convertissant effectivement le RIGHT JOIN en INNER JOIN.

FULL JOIN

Un FULL JOIN renvoie toutes les lignes des deux tables. Les deux tables sont simultanément des tables préservées et des tables fournissant les valeurs NULL. Lorsqu'une ligne n'a pas de correspondance, des valeurs NULL remplissent les colonnes non appariées.

Résultat : Le placement de la condition de filtre affecte le résultat.

Attention : Pour FULL JOIN, les filtres placés dans la clause ON ou la clause WHERE externe ne restreignent pas les lignes renvoyées de chaque table — ils affectent uniquement la détection des correspondances. Seuls les filtres de sous-requête permettent de restreindre fiablement les données avant la jointure.

Cas 1 — filtre dans la sous-requête :

SELECT A.*, B.*
FROM
  (SELECT * FROM A WHERE ds='20180101') A
FULL JOIN
  (SELECT * FROM B WHERE ds='20180101') B
ON a.key = b.key;

Résultat (3 lignes) :

+------+----------+------+----------+
| key  | ds       | key2 | ds2      |
+------+----------+------+----------+
| 2    | 20180101 | NULL | NULL     |
| 1    | 20180101 | 1    | 20180101 |
| NULL | NULL     | 3    | 20180101 |
+------+----------+------+----------+

Cas 2 — filtre dans la clause ON :

SELECT A.*, B.*
FROM A FULL JOIN B
ON a.key = b.key AND A.ds='20180101' AND B.ds='20180101';

Seule 1 ligne du produit cartésien de 9 lignes satisfait la condition de jointure. Les 2 lignes A non appariées obtiennent NULL pour les colonnes B, et les 2 lignes B non appariées obtiennent NULL pour les colonnes A.

Résultat (5 lignes) :

+------+----------+------+----------+
| key  | ds       | key2 | ds2      |
+------+----------+------+----------+
| NULL | NULL     | 2    | 20180102 |
| 2    | 20180101 | NULL | NULL     |
| 2    | 20180102 | NULL | NULL     |
| 1    | 20180101 | 1    | 20180101 |
| NULL | NULL     | 3    | 20180101 |
+------+----------+------+----------+

Cas 3 — filtre dans la clause WHERE externe :

SELECT A.*, B.*
FROM A FULL JOIN B
ON a.key = b.key
WHERE A.ds='20180101' AND B.ds='20180101';

La jointure produit 4 lignes (3 correspondantes + 1 ligne B non appariée avec des colonnes A NULL). Le filtre WHERE élimine les lignes où l'une des colonnes est NULL.

Résultat (1 ligne) :

+------+----------+------+----------+
| key  | ds       | key2 | ds2      |
+------+----------+------+----------+
| 1    | 20180101 | 1    | 20180101 |
+------+----------+------+----------+

LEFT SEMI JOIN

Un LEFT SEMI JOIN renvoie les lignes de la table de gauche qui ont au moins une correspondance dans la table de droite. Les lignes de la table de droite ne sont pas incluses dans le résultat, de sorte que les colonnes de la table de droite ne peuvent pas être référencées dans la clause WHERE externe.

Résultat : Le placement de la condition de filtre n'affecte pas le résultat.

Cas 1 — filtre dans la sous-requête :

SELECT A.*
FROM
  (SELECT * FROM A WHERE ds='20180101') A
LEFT SEMI JOIN
  (SELECT * FROM B WHERE ds='20180101') B
ON a.key = b.key;

Cas 2 — filtre dans la clause ON :

SELECT A.*
FROM A LEFT SEMI JOIN B
ON a.key = b.key AND A.ds='20180101' AND B.ds='20180101';

Cas 3 — filtre dans la clause WHERE externe (le filtre de la table de droite doit être dans la sous-requête) :

SELECT A.*
FROM A LEFT SEMI JOIN
  (SELECT * FROM B WHERE ds='20180101') B
ON a.key = b.key
WHERE A.ds='20180101';

Les trois cas renvoient :

+------+----------+
| key  | ds       |
+------+----------+
| 1    | 20180101 |
+------+----------+

LEFT ANTI JOIN

Un LEFT ANTI JOIN renvoie les lignes de la table de gauche qui n'ont aucune correspondance dans la table de droite. Les lignes de la table de droite ne sont pas incluses dans le résultat, de sorte que les colonnes de la table de droite ne peuvent pas être référencées dans la clause WHERE externe.

Résultat : Le placement de la condition de filtre affecte le résultat.

  • Pour les filtres sur la table de gauche : un filtre de sous-requête et un filtre WHERE externe produisent le même résultat.

  • Pour les filtres sur la table de droite : un filtre de sous-requête et un filtre de clause ON produisent le même résultat.

Cas 1 — filtre dans la sous-requête :

SELECT A.*
FROM
  (SELECT * FROM A WHERE ds='20180101') A
LEFT ANTI JOIN
  (SELECT * FROM B WHERE ds='20180101') B
ON a.key = b.key;

Résultat (1 ligne) :

+------+----------+
| key  | ds       |
+------+----------+
| 2    | 20180101 |
+------+----------+

Cas 2 — filtre dans la clause ON :

SELECT A.*
FROM A LEFT ANTI JOIN B
ON a.key = b.key AND A.ds='20180101' AND B.ds='20180101';

La clause ON restreint les lignes de B pouvant servir de correspondance. Avec la condition de jointure plus restrictive, davantage de lignes A n'ont pas de correspondance et sont renvoyées.

Résultat (2 lignes) :

+------+----------+
| key  | ds       |
+------+----------+
| 2    | 20180101 |
| 2    | 20180102 |
+------+----------+

Cas 3 — filtre dans la clause WHERE externe (le filtre de la table de droite doit être dans la sous-requête) :

SELECT A.*
FROM A LEFT ANTI JOIN
  (SELECT * FROM B WHERE ds='20180101') B
ON a.key = b.key
WHERE A.ds='20180101';

La jointure renvoie 2 lignes, puis le filtre WHERE externe en conserve 1.

Résultat (1 ligne) :

+------+----------+
| key  | ds       |
+------+----------+
| 2    | 20180101 |
+------+----------+

Référence du placement des filtres

Comportement par type de JOIN

Type de JOIN Filtre table de gauche Filtre table de droite
INNER JOIN Tout emplacement donne le même résultat Tout emplacement donne le même résultat
LEFT SEMI JOIN Tout emplacement donne le même résultat Tout emplacement donne le même résultat
LEFT JOIN Sous-requête = WHERE externe Sous-requête = clause ON
LEFT ANTI JOIN Sous-requête = WHERE externe Sous-requête = clause ON
RIGHT JOIN Sous-requête = clause ON Sous-requête = WHERE externe
FULL JOIN Sous-requête uniquement Sous-requête uniquement

Recommandations

Utilisez des filtres de sous-requête pour les JOIN externes. Les filtres de sous-requête restreignent les données d'entrée avant l'exécution de la jointure, de sorte que la sémantique de la jointure n'est jamais affectée. C'est l'approche la plus sûre pour tous les types de JOIN.

Si vous utilisez la clause ON pour les filtres de la table fournissant les valeurs NULL dans un LEFT JOIN ou un RIGHT JOIN, la sémantique de la jointure externe est préservée. Placer ces filtres dans la clause WHERE externe élimine les lignes portant des valeurs NULL et convertit effectivement la jointure externe en INNER JOIN.

Pour FULL JOIN, les filtres de sous-requête sont la seule option qui restreint fiablement les données d'entrée — les filtres dans la clause ON ou la clause WHERE externe modifient les lignes correspondantes mais n'excluent pas les lignes non appariées de l'une ou l'autre table.

Étapes suivantes

  • JOIN — référence syntaxique des opérations JOIN standard dans MaxCompute SQL

  • SEMI JOIN — référence syntaxique pour LEFT SEMI JOIN et LEFT ANTI JOIN

  • MAPJOIN HINT — améliorez les performances lors de la jointure d'une grande table avec une petite table

  • DISTRIBUTED MAPJOIN — améliorez les performances lors de la jointure d'une grande table avec une table de taille moyenne

  • SKEWJOIN HINT — gérez les valeurs de clé populaires provoquant des problèmes de longue traîne dans les opérations JOIN