Ajoutez l'indice MAPJOIN à une instruction SELECT pour forcer l'exécution d'une jointure durant la phase de map, en contournant les phases de shuffle et de reduce. Cette approche réduit la surcharge liée au transfert de données et améliore les performances des requêtes lors de la jointure d'une grande table avec une ou plusieurs petites tables. Utilisez MAPJOIN lorsque l'optimiseur n'applique pas automatiquement la jointure en phase de map à votre requête.
Fonctionnement
Une jointure standard dans MaxCompute s'exécute en trois étapes : map, shuffle et reduce. La logique de jointure proprement dite s'exécute lors de la phase de reduce, ce qui nécessite un brassage des données entre les nœuds.
MAPJOIN modifie ce processus : il charge l'intégralité du contenu de la petite table spécifiée en mémoire durant la phase de map. Chaque mappeur effectue ensuite la jointure localement sur les données en mémoire, éliminant ainsi totalement les phases de shuffle et de reduce.
L'indice désigne la petite table à charger en mémoire. Pour chaque mappeur lisant des lignes de la grande table, la petite table est intégralement lue depuis la mémoire :
SELECT /*+ mapjoin(a) */ a.shop_name, a.total_price, b.total_price
FROM sale_detail_sj a JOIN sale_detail b
ON a.total_price < b.total_price OR a.total_price + b.total_price < 500;
Dans cet exemple, a (alias de sale_detail_sj) correspond à la petite table. L'indice spécifie a ; par conséquent, c'est a qui est chargé en mémoire sur chaque mappeur.
Limites
Mémoire et nombre de tables
| Contrainte | Limite | Notes |
|---|---|---|
| Mémoire totale pour toutes les petites tables | 512 Mo | Mesurée après le chargement des données en mémoire et leur décompression, et non selon la taille compressée stockée dans MaxCompute |
| Nombre maximal de petites tables | 128 | La spécification de plus de 128 tables renvoie une erreur de syntaxe |
Types de JOIN pris en charge
| Type de JOIN | Pris en charge | Exigence |
|---|---|---|
| INNER JOIN | Oui | La table de gauche ou de droite peut être la grande table |
| LEFT OUTER JOIN | Oui | La table de gauche doit être la grande table |
| RIGHT OUTER JOIN | Oui | La table de droite doit être la grande table |
| FULL OUTER JOIN | Non | — |
Notes d'utilisation
Ajoutez /*+ mapjoin(<table_name>) */ immédiatement après SELECT. Tenez compte des points suivants :
Référencez les alias, et non les noms de table d'origine. Lorsque la petite table ou la sous-requête possède un alias, utilisez cet alias dans l'indice.
Les sous-requêtes sont prises en charge comme petites tables. Utilisez une sous-requête à la place d'une référence de table, et référencez son alias dans l'indice.
Séparez plusieurs petites tables par des virgules :
/*+ mapjoin(a,b,c) */Les jointures non équivalentes et les conditions OR sont prises en charge. Le SQL MaxCompute standard n'autorise pas les jointures non équivalentes ni la logique OR dans la condition ON, mais MAPJOIN le permet.
Les produits cartésiens sont pris en charge en utilisant
ON 1 = 1(par exemple,SELECT /*+ mapjoin(a) */ a.id FROM shop a JOIN table_name b ON 1=1), mais cela peut augmenter considérablement le volume des données de sortie.
Les types de sous-requêtes tels que SCALAR, IN, NOT IN, EXISTS et NOT EXISTS peuvent être convertis en opérations JOIN au moment de l'exécution. Si le résultat de la sous-requête est éligible en tant que petite table, ajoutez un indice MAPJOIN à l'instruction de sous-requête pour appliquer explicitement l'algorithme de jointure en phase de map.
Exemple de données
Les exemples de cette rubrique utilisent les tables sale_detail et sale_detail_sj. Exécutez les instructions suivantes pour créer les tables et insérer des exemples de données.
-- Create a partitioned table named sale_detail.
CREATE TABLE IF NOT EXISTS sale_detail
(
shop_name STRING,
customer_id STRING,
total_price DOUBLE
)
PARTITIONED BY (sale_date STRING, region STRING);
CREATE TABLE IF NOT EXISTS sale_detail_sj
(
shop_name STRING,
customer_id STRING,
total_price DOUBLE
)
PARTITIONED BY (sale_date STRING, region STRING);
-- Add partitions.
ALTER TABLE sale_detail ADD PARTITION (sale_date='2013', region='china');
ALTER TABLE sale_detail_sj ADD PARTITION (sale_date='2013', region='china');
-- Insert sample data.
INSERT INTO sale_detail PARTITION (sale_date='2013', region='china')
VALUES ('s1','c1',100.1),('s2','c2',100.2),('s3','c3',100.3);
INSERT INTO sale_detail_sj PARTITION (sale_date='2013', region='china')
VALUES ('s1','c1',100.1),('s2','c2',100.2),('s5','c2',100.2),('s2','c2',100.2);
Exemple
Joignez sale_detail_sj (petite table, alias a) à sale_detail (grande table, alias b) en utilisant une condition non équivalente. Renvoyez les lignes où soit le total_price de a est inférieur au total_price de b, soit la somme des deux prix est inférieure à 500.
-- Allow a full scan on the partitioned table.
SET odps.sql.allow.fullscan=true;
-- Use MAPJOIN with a non-equi join condition.
SELECT /*+ mapjoin(a) */
a.shop_name,
a.total_price,
b.total_price
FROM sale_detail_sj a JOIN sale_detail b
ON a.total_price < b.total_price OR a.total_price + b.total_price < 500;
La requête renvoie le résultat suivant :
+-----------+-------------+--------------+
| shop_name | total_price | total_price2 |
+-----------+-------------+--------------+
| s1 | 100.1 | 100.1 |
| s2 | 100.2 | 100.1 |
| s5 | 100.2 | 100.1 |
| s2 | 100.2 | 100.1 |
| s1 | 100.1 | 100.2 |
| s2 | 100.2 | 100.2 |
| s5 | 100.2 | 100.2 |
| s2 | 100.2 | 100.2 |
| s1 | 100.1 | 100.3 |
| s2 | 100.2 | 100.3 |
| s5 | 100.2 | 100.3 |
| s2 | 100.2 | 100.3 |
+-----------+-------------+--------------+