すべてのプロダクト
Search
ドキュメントセンター

MaxCompute:MAPJOIN ヒント

最終更新日:Mar 27, 2026

SELECT 文に MAPJOIN ヒントを追加すると、結合が強制的にマップステージで実行され、シャッフルステージとリデュースステージがバイパスされます。これにより、大きいテーブルを 1 つ以上の小さいテーブルと結合する際のデータ転送のオーバーヘッドが削減され、クエリパフォーマンスが向上します。オプティマイザーがクエリにマップステージ結合を自動的に適用しない場合は、MAPJOIN を使用してください。

仕組み

MaxCompute での標準的な JOIN は、マップ、シャッフリング、リデュースの 3 つのステージを経て実行されます。実際の JOIN ロジックはリデュースステージで実行され、ノード間でデータをシャッフリングする必要があります。

MAPJOIN はこの動作を変更します。マップステージ中に、指定された小さいテーブルの全コンテンツをメモリにロードします。その後、各マッパーはメモリ内のデータに対してローカルで結合を実行し、シャッフルステージとリデュースステージを完全に排除します。

ヒントでは、メモリにロードする小さいテーブルを指定します。大きいテーブルから行を読み取るすべてのマッパーに対して、小さいテーブルはメモリから完全に読み取られます。

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;

この例では、a (sale_detail_sj のエイリアス) が小さいテーブルです。ヒントで a が指定されているため、各マッパーのメモリにロードされるのは a です。

制限事項

メモリとテーブル数

制約 制限 注意
すべての小さいテーブルの合計メモリ 512 MB MaxCompute に保存されている圧縮サイズではなく、データがメモリにロードされて解凍された後のサイズで測定されます。
小さいテーブルの最大数 128 128 を超える数を指定すると、構文エラーが返されます。

サポートされる JOIN タイプ

JOIN タイプ サポート 要件
INNER JOIN はい 左テーブルまたは右テーブルのどちらかを大きいテーブルにできます。
LEFT OUTER JOIN はい 左テーブルを大きいテーブルにする必要があります。
RIGHT OUTER JOIN はい 右テーブルを大きいテーブルにする必要があります。
FULL OUTER JOIN いいえ

注意事項

SELECT の直後に /*+ mapjoin(<table_name>) */ を追加します。以下の点にご注意ください。

  • 元のテーブル名ではなく、エイリアスを参照します。小さいテーブルまたはサブクエリにエイリアスがある場合は、ヒント内でそのエイリアスを使用します。

  • サブクエリは小さいテーブルとしてサポートされています。テーブル参照の代わりにサブクエリを使用し、ヒントでそのエイリアスを参照します。

  • 複数の小さいテーブルはカンマで区切ります: /*+ mapjoin(a,b,c) */

  • 非等値結合と OR 条件がサポートされています。標準の MaxCompute SQL では ON 条件での非等値結合や OR ロジックは許可されていませんが、MAPJOIN では許可されています。

  • デカルト積は ON 1 = 1 を使用してサポートされています (例: SELECT /*+ mapjoin(a) */ a.id FROM shop a JOIN table_name b ON 1=1) が、これにより出力データ量が大幅に増加する可能性があります。

説明

SCALAR、IN、NOT IN、EXISTS、NOT EXISTS などのサブクエリタイプは、実行時に JOIN 操作に変換される場合があります。サブクエリの結果が小さいテーブルとして適格である場合は、サブクエリ文に MAPJOIN ヒントを追加して、マップステージ結合アルゴリズムを明示的に適用します。

サンプルデータ

このトピックの例では、sale_detail テーブルと sale_detail_sj テーブルを使用します。次の文を実行してテーブルを作成し、サンプルデータを挿入します。

-- 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);

-- パーティションを追加します。
ALTER TABLE sale_detail ADD PARTITION (sale_date='2013', region='china');
ALTER TABLE sale_detail_sj ADD PARTITION (sale_date='2013', region='china');

-- サンプルデータを挿入します。
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);

sale_detail_sj (小さいテーブル、エイリアスは a) と sale_detail (大きいテーブル、エイリアスは b) を非等値条件で結合します。atotal_pricebtotal_price より小さいか、または両方の価格の合計が 500 未満である行を返します。

-- パーティションテーブルのフルスキャンを許可します。
SET odps.sql.allow.fullscan=true;

-- 非等値結合条件で MAPJOIN を使用します。
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;

クエリは次の結果を返します。

+-----------+-------------+--------------+
| 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        |
+-----------+-------------+--------------+