MaxCompute支持通過JOIN
操作連接表并返回符合連接條件和查詢條件的數據。本文為您介紹左連接、右連接、全連接、內連接、自然連接、隱式連接和多路連接的使用方法。
功能介紹
MaxCompute支持如下JOIN
操作:
左連接(
LEFT OUTER JOIN
)可簡寫為
LEFT JOIN
。返回左表中的所有記錄,即使右表中沒有與之匹配的記錄。說明通常,
JOIN
操作的左邊為大表,右表為小表,如果右表值不唯一,建議不要連續使用過多LEFT JOIN
,以免在JOIN
過程中產生數據膨脹,導致作業停滯。右連接(
RIGHT OUTER JOIN
)可簡寫為
RIGHT JOIN
。返回右表中的所有記錄,即使左表中沒有與之匹配的記錄。全連接(
FULL OUTER JOIN
)可簡寫為
FULL JOIN
。返回左右表中的所有記錄。內連接(
INNER JOIN
)關鍵字
INNER
可以省略。左右表中至少存在一個匹配行時,INNER JOIN
返回數據行。自然連接(
NATURAL JOIN
)參與
JOIN
的兩張表根據字段名稱自動決定連接字段。支持OUTER NATURAL JOIN
,支持使用USING
子句執行JOIN
,輸出字段中公共字段只出現一次。隱式連接
即不指定
JOIN
關鍵字執行連接。多路連接
多路
JOIN
連接。支持通過括號指定JOIN
的優先級,括號內的JOIN
優先級較高。
如果SQL語句中包含
WHERE
過濾條件,且JOIN
在WHERE
條件之前,先進行JOIN
操作,然后對JOIN
的結果執行WHERE
條件過濾,獲取的結果是兩個表的交集,而不是全表。odps.task.sql.outerjoin.ppd參數可以控制
OUTER JOIN ON
條件中的非關聯過濾條件是否會下推到JOIN
的輸入中,該參數支持Project或Session級別設置。當參數值為
FALSE
時,會把寫在ON
中的非關聯條件當作關聯對應子查詢中的WHERE
條件,這是一個非標準的行為,建議將非關聯條件移到WHERE
子句中。當參數值為
FALSE
時,如下兩個SQL語句等價;當參數值為TRUE
時,兩者不等價。
SELECT A.*, B.* FROM A LEFT JOIN B ON A.c1 = B.c1 and A.c2='xxx'; SELECT A.*, B.* FROM (SELECT * FROM A WHERE c2='xxx') A LEFT JOIN B ON A.c1 = B.c1;
注意事項
使用JOIN
時,會在計算中自動加入JOIN
的key is not null
的過濾條件,去除關聯鍵為NULL的值所在行。
使用限制
JOIN
操作的使用限制如下:
MaxCompute不支持
CROSS JOIN
,即無ON
條件的連接。只允許出現
AND
連接的等值條件。您可以通過MAPJOIN
操作使用不等值連接或OR
連接多個條件,詳情請參見MAPJOIN。
命令格式
<table_reference> JOIN <table_factor> [<join_condition>]
| <table_reference> {LEFT OUTER|RIGHT OUTER|FULL OUTER|INNER|NATURAL} JOIN <table_reference> <join_condition>
table_reference:必填。待執行
JOIN
操作的左表查詢語句。格式為table_name [alias] | table_query [alias] |...
。table_factor:必填。待執行
JOIN
操作的右表或表查詢語句。格式為table_name [alias] | table_subquery [alias] |...
。join_condition:可選。
JOIN
連接條件,是一個或多個等式表達式組合。格式為on equality_expression [and equality_expression]...
,equality_expression
為等式表達式。
如果分區裁剪條件置于WHERE
子句中,分區裁剪會生效;如果置于ON
子句中,從表的分區裁剪會生效,主表的分區剪裁不會生效即會全表掃描。詳情請參見分區剪裁合理性評估。
示例數據
為便于理解,本文為您提供源數據,基于源數據提供相關示例。創建表sale_detail和sale_detail_jt,并添加數據,命令示例如下:
--創建分區表sale_detail和sale_detail_jt。
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_jt
(
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') PARTITION (sale_date='2014', region='shanghai');
ALTER TABLE sale_detail_jt 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 PARTITION (sale_date='2014', region='shanghai') VALUES ('null','c5',null),('s6','c6',100.4),('s7','c7',100.5);
INSERT INTO sale_detail_jt PARTITION (sale_date='2013', region='china') VALUES ('s1','c1',100.1),('s2','c2',100.2),('s5','c2',100.2);
--查詢表sale_detail和sale_detail_jt中的數據,命令示例如下:
SET odps.sql.allow.fullscan=true;
SELECT * FROM sale_detail;
--返回結果
+------------+-------------+-------------+------------+------------+
| shop_name | customer_id | total_price | sale_date | region |
+------------+-------------+-------------+------------+------------+
| s1 | c1 | 100.1 | 2013 | china |
| s2 | c2 | 100.2 | 2013 | china |
| s3 | c3 | 100.3 | 2013 | china |
| null | c5 | NULL | 2014 | shanghai |
| s6 | c6 | 100.4 | 2014 | shanghai |
| s7 | c7 | 100.5 | 2014 | shanghai |
+------------+-------------+-------------+------------+------------+
SET odps.sql.allow.fullscan=true;
SELECT * FROM sale_detail_jt;
-- 返回結果
+------------+-------------+-------------+------------+------------+
| shop_name | customer_id | total_price | sale_date | region |
+------------+-------------+-------------+------------+------------+
| s1 | c1 | 100.1 | 2013 | china |
| s2 | c2 | 100.2 | 2013 | china |
| s5 | c2 | 100.2 | 2013 | china |
+------------+-------------+-------------+------------+------------+
--創建做關聯的表。
SET odps.sql.allow.fullscan=true;
CREATE TABLE shop AS SELECT shop_name, customer_id, total_price FROM sale_detail;
使用示例
下述示例均基于示例數據為您展示JOIN的相關用法。
示例1:左連接。命令示例如下:
--分區表需要開啟全表掃描功能,否則join操作會執行失敗。 SET odps.sql.allow.fullscan=true; --由于表sale_detail_jt及sale_detail中都有shop_name列,因此需要在select子句中使用別名進行區分。 SELECT a.shop_name AS ashop, b.shop_name AS bshop FROM sale_detail_jt a LEFT OUTER JOIN sale_detail b ON a.shop_name=b.shop_name;
返回結果如下:
+------------+------------+ | ashop | bshop | +------------+------------+ | s2 | s2 | | s1 | s1 | | s5 | NULL | +------------+------------+
示例2:右連接。命令示例如下:
--分區表需要開啟全表掃描功能,否則join操作會執行失敗。 SET odps.sql.allow.fullscan=true; --由于表sale_detail_jt及sale_detail中都有shop_name列,因此需要在select子句中使用別名進行區分。 SELECT a.shop_name AS ashop, b.shop_name AS bshop FROM sale_detail_jt a RIGHT OUTER JOIN sale_detail b ON a.shop_name=b.shop_name;
返回結果如下:
+------------+------------+ | ashop | bshop | +------------+------------+ | s1 | s1 | | s2 | s2 | | NULL | s3 | | NULL | null | | NULL | s6 | | NULL | s7 | +------------+------------+
示例3:全連接。命令示例如下:
--分區表需要開啟全表掃描功能,否則join操作會執行失敗。 SET odps.sql.allow.fullscan=true; --由于表sale_detail_jt及sale_detail中都有shop_name列,因此需要在select子句中使用別名進行區分。 SELECT a.shop_name AS ashop, b.shop_name AS bshop FROM sale_detail_jt a FULL OUTER JOIN sale_detail b ON a.shop_name=b.shop_name;
返回結果如下:
+------------+------------+ | ashop | bshop | +------------+------------+ | NULL | s3 | | NULL | s6 | | s2 | s2 | | NULL | null | | NULL | s7 | | s1 | s1 | | s5 | NULL | +------------+------------+
示例4:內連接。命令示例如下:
--分區表需要開啟全表掃描功能,否則join操作會執行失敗。 SET odps.sql.allow.fullscan=true; --由于表sale_detail_jt及sale_detail中都有shop_name列,因此需要在select子句中使用別名進行區分。 SELECT a.shop_name AS ashop, b.shop_name AS bshop FROM sale_detail_jt a INNER JOIN sale_detail b ON a.shop_name=b.shop_name;
返回結果如下:
+------------+------------+ | ashop | bshop | +------------+------------+ | s2 | s2 | | s1 | s1 | +------------+------------+
示例5:自然連接。命令示例如下:
--分區表需要開啟全表掃描功能,否則join操作會執行失敗。 SET odps.sql.allow.fullscan=true; --自然連接。 SELECT * FROM sale_detail_jt NATURAL JOIN sale_detail; --等效于如下語句。 SELECT sale_detail_jt.shop_name AS shop_name, sale_detail_jt.customer_id AS customer_id, sale_detail_jt.total_price AS total_price, sale_detail_jt.sale_date as sale_date, sale_detail_jt.region as region from sale_detail_jt INNER JOIN sale_detail ON sale_detail_jt.shop_name=sale_detail.shop_name AND sale_detail_jt.customer_id=sale_detail.customer_id and sale_detail_jt.total_price=sale_detail.total_price AND sale_detail_jt.sale_date=sale_detail.sale_date AND sale_detail_jt.region=sale_detail.region;
返回結果如下:
+------------+-------------+-------------+------------+------------+ | shop_name | customer_id | total_price | sale_date | region | +------------+-------------+-------------+------------+------------+ | s1 | c1 | 100.1 | 2013 | china | | s2 | c2 | 100.2 | 2013 | china | +------------+-------------+-------------+------------+------------+
示例6:隱式連接。命令示例如下:
--分區表需要開啟全表掃描功能,否則join操作會執行失敗。 SET odps.sql.allow.fullscan=true; --隱式連接。 SELECT * FROM sale_detail_jt, sale_detail WHERE sale_detail_jt.shop_name = sale_detail.shop_name; --等效于如下語句。 SELECT * FROM sale_detail_jt JOIN sale_detail ON sale_detail_jt.shop_name = sale_detail.shop_name;
返回結果如下:
+------------+-------------+-------------+------------+------------+------------+--------------+--------------+------------+------------+ | shop_name | customer_id | total_price | sale_date | region | shop_name2 | customer_id2 | total_price2 | sale_date2 | region2 | +------------+-------------+-------------+------------+------------+------------+--------------+--------------+------------+------------+ | s2 | c2 | 100.2 | 2013 | china | s2 | c2 | 100.2 | 2013 | china | | s1 | c1 | 100.1 | 2013 | china | s1 | c1 | 100.1 | 2013 | china | +------------+-------------+-------------+------------+------------+------------+--------------+--------------+------------+------------+
示例7:多路連接,不指定優先級。命令示例如下:
--分區表需要開啟全表掃描功能,否則join操作會執行失敗。 SET odps.sql.allow.fullscan=true; --由于表sale_detail_jt及sale_detail中都有shop_name列,因此需要在select子句中使用別名進行區分。 SELECT a.* FROM sale_detail_jt a FULL OUTER JOIN sale_detail b ON a.shop_name=b.shop_name FULL OUTER JOIN sale_detail c ON a.shop_name=c.shop_name;
返回結果如下:
+------------+-------------+-------------+------------+------------+ | shop_name | customer_id | total_price | sale_date | region | +------------+-------------+-------------+------------+------------+ | s5 | c2 | 100.2 | 2013 | china | | NULL | NULL | NULL | NULL | NULL | | NULL | NULL | NULL | NULL | NULL | | NULL | NULL | NULL | NULL | NULL | | NULL | NULL | NULL | NULL | NULL | | NULL | NULL | NULL | NULL | NULL | | NULL | NULL | NULL | NULL | NULL | | s1 | c1 | 100.1 | 2013 | china | | NULL | NULL | NULL | NULL | NULL | | s2 | c2 | 100.2 | 2013 | china | | NULL | NULL | NULL | NULL | NULL | +------------+-------------+-------------+------------+------------+
示例7:多路連接,通過括號指定優先級。命令示例如下:
--分區表需要開啟全表掃描功能,否則join操作會執行失敗。 SET odps.sql.allow.fullscan=true; --多路連接,通過括號指定優先級。 SELECT * FROM shop JOIN (sale_detail_jt JOIN sale_detail ON sale_detail_jt.shop_name = sale_detail.shop_name) ON shop.shop_name=sale_detail_jt.shop_name;
返回結果如下:
+------------+-------------+-------------+------------+------------+------------+--------------+--------------+------------+------------+------------+--------------+--------------+------------+------------+ | shop_name | customer_id | total_price | sale_date | region | shop_name2 | customer_id2 | total_price2 | sale_date2 | region2 | shop_name3 | customer_id3 | total_price3 | sale_date3 | region3 | +------------+-------------+-------------+------------+------------+------------+--------------+--------------+------------+------------+------------+--------------+--------------+------------+------------+ | s2 | c2 | 100.2 | 2013 | china | s2 | c2 | 100.2 | 2013 | china | s2 | c2 | 100.2 | 2013 | china | | s1 | c1 | 100.1 | 2013 | china | s1 | c1 | 100.1 | 2013 | china | s1 | c1 | 100.1 | 2013 | china | +------------+-------------+-------------+------------+------------+------------+--------------+--------------+------------+------------+------------+--------------+--------------+------------+------------+
示例8:
join
與where
相結合,查詢兩表中region為china且shop_name一致的記錄數,保留sale_detail表的全部記錄。命令示例如下:--分區表需要開啟全表掃描功能,否則join操作會執行失敗。 SET odps.sql.allow.fullscan=true; --執行SQL語句。 SELECT a.shop_name ,a.customer_id ,a.total_price ,b.total_price FROM (SELECT * FROM sale_detail WHERE region = "china") a LEFT JOIN (SELECT * FROM sale_detail_jt WHERE region = "china") b ON a.shop_name = b.shop_name;
返回結果如下:
+------------+-------------+-------------+--------------+ | shop_name | customer_id | total_price | total_price2 | +------------+-------------+-------------+--------------+ | s1 | c1 | 100.1 | 100.1 | | s2 | c2 | 100.2 | 100.2 | | s3 | c3 | 100.3 | NULL | +------------+-------------+-------------+--------------+
錯誤命令示例如下:
SELECT a.shop_name ,a.customer_id ,a.total_price ,b.total_price FROM sale_detail a LEFT JOIN sale_detail_jt b ON a.shop_name = b.shop_name WHERE a.region = "china" AND b.region = "china";
返回結果如下:
+------------+-------------+-------------+--------------+ | shop_name | customer_id | total_price | total_price2 | +------------+-------------+-------------+--------------+ | s1 | c1 | 100.1 | 100.1 | | s2 | c2 | 100.2 | 100.2 | +------------+-------------+-------------+--------------+
從返回結果可看到,獲取的結果是兩個表的交集,非sale_detail表的全部記錄。