大きなテーブルと複数の小さなテーブルを join する場合は、MAPJOIN ヒントを SELECT 文で明示的に指定して、クエリのパフォーマンスを向上させることができます。このトピックでは、MAPJOIN ヒント を使用してテーブルを結合する方法について説明します。
説明
JOIN 操作には、マップ、シャッフル、縮小の 3 つの段階があります。ほとんどの場合、テーブルは縮小ステージで join されます。
MAPJOIN は、縮小ステージではなくマップステージでテーブルを結合します。これにより、データ転送が高速化され、システムリソースの消費が削減され、ジョブのパフォーマンスが最適化されます。
マップステージでは、MAPJOIN は、指定された小さなテーブルのすべてのデータを、JOIN 操作を実行するプログラムのメモリにロードします。これにより、JOIN 操作の効率が向上します。
MaxCompute SQL では、ON 条件で非等値 join または OR ロジックを使用することはできません。ただし、MAPJOIN ではこれを行うことができます。
制限
MAPJOIN の使用に関する制限:
マップステージでは、
MAPJOINは、指定されたテーブルのすべてのデータを JOIN 操作を実行するプログラムのメモリにロードします。MAPJOIN に指定されたテーブルは小さなテーブルである必要があり、テーブルデータによって占有される合計メモリは 512 MB を超えることはできません。MaxCompute は、保存前にデータを圧縮します。小さなテーブルがメモリにロードされると、テーブルのデータ量は急激に増加します。512 MB は、小さなテーブルがメモリにロードされた後の最大データ量を示します。説明ここで示す 512 MB は、小さなテーブルがメモリにロードされて解凍された後に占有する最大メモリサイズを指し、MaxCompute に圧縮されて保存された後の小さなテーブルのファイルサイズではありません。
MAPJOINでのJOIN操作の制限:LEFT OUTER JOIN操作の左側のテーブルは大きなテーブルである必要があります。RIGHT OUTER JOIN操作の右側のテーブルは大きなテーブルである必要があります。FULL OUTER JOIN操作では MAPJOIN を使用できません。INNER JOIN操作の左側または右側のテーブルは大きなテーブルにすることができます。
MaxCompute では、
MAPJOINに最大 128 個の小さなテーブルを指定できます。128 個を超える小さなテーブルを指定すると、構文エラーが返されます。
使用上の注意
MAPJOIN は、/*+ mapjoin(<table_name>) */ ヒントを SELECT 文に追加した後にのみ実行できます。次の項目に注意してください。
小さなテーブルまたはサブクエリを参照する場合は、テーブルまたはサブクエリのエイリアスを参照する必要があります。
MAPJOIN では、サブクエリを小さなテーブルとして使用できます。MAPJOINでは、非等値結合を使用したり、ORを使用して条件を組み合わせたりできます。ON条件を指定せずにMAPJOIN ON 1 = 1を使用してデカルト積を計算できます(例:select /*+ mapjoin(a) */ a.id from shop a join table_name b on 1=1;)。ただし、この計算方法ではデータ量が増加する可能性があります。MAPJOINの複数の小さなテーブルはコンマ (,) で区切ります(例:/*+ mapjoin(a,b,c)*/)。
SCALAR、IN、NOT IN、EXISTS、NOT EXISTS などの一部のサブクエリは、実行中に JOIN 操作に変換できます。MAPJOIN は効率的な JOIN アルゴリズムです。SUBQUERY の結果が小さなテーブルの場合は、サブクエリ文で HINT を使用して MAPJOIN アルゴリズムを明示的に指定できます。
サンプルデータ
このトピックの例をよりよく理解するために、サンプルのソースデータが提供されています。この例では、sale_detail テーブルと sale_detail_sj テーブルが作成され、データがテーブルに挿入されます。
-- sale_detail という名前のパーティションテーブルを作成します。
/* 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);
-- sale_detail テーブルと sale_detail_sj テーブルにパーティションを追加します。
/* sale_detail テーブルと sale_detail_sj テーブルにパーティションを追加します。 */
alter table sale_detail add partition (sale_date='2013', region='china');
alter table sale_detail_sj add partition (sale_date='2013', region='china');
-- sale_detail テーブルと sale_detail_sj テーブルにデータを挿入します。
/* sale_detail テーブルと sale_detail_sj テーブルにデータを挿入します。 */
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 テーブルと sale_detail_sj テーブルで JOIN 操作を実行します。操作は、次の条件のいずれかを満たす必要があります。1. sale_detail_sj テーブルの total_price 列の合計値が、sale_detail テーブルの total_price 列の合計値よりも小さい。2. sale_detail_sj テーブルの total_price 列の値と sale_detail テーブルの total_price 列の値の合計が 500 未満である。サンプル文:
-- パーティションテーブルのフルスキャンを許可します。
/* パーティションテーブルのフルスキャンを許可します。 */
SET odps.sql.allow.fullscan=true;
-- MAPJOIN を使用してクエリを実行します。
/* 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 |
+-----------+-------------+--------------+