Hive Join(翻譯自Hive wiki)


Join語法

hive表連接支持的語法如下:

join_table:
    table_reference JOIN table_factor [join_condition]
  | table_reference {LEFT|RIGHT|FULL} [OUTER] JOIN table_reference join_condition
  | table_reference LEFT SEMI JOIN table_reference join_condition
  | table_reference CROSS JOIN table_reference [join_condition] (as of Hive 0.10)

table_reference:
    table_factor
  | join_table

table_factor:
    tbl_name [alias]
  | table_subquery alias
  | ( table_references )

join_condition:
    ON equality_expression ( AND equality_expression )*

equality_expression:
    expression = expression

hive只支持等連接,外連接,左半連接。hive不支持非相等的join條件,因為它很難在map/reduce job實現這樣的條件。而且,hive可以join兩個以上的表。

 

例子

寫join查詢時,有幾個典型的點要考慮,如下:

等連接

只有等連接才允許

SELECT a.* FROM a JOIN b ON (a.id = b.id)
SELECT a.* FROM a JOIN b ON (a.id = b.id AND a.department = b.department)

這兩個是合法的連接

 

SELECT a.* FROM a JOIN b ON (a.id <> b.id)

這個是不允許的。

 

多表連接
同個查詢,可以join兩個以上的表

SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key2)

這個是合法的join。

 

join的緩存和任務轉換

hive轉換多表join時,如果每個表在join字句中,使用的都是同一個列,只會轉換為一個單獨的map/reduce。

SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key1)

這個會轉換為單獨的map/reduce任務,只有b表的key1列在join被調用。

 

另一方面

SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key2)

被轉換為兩個map/reduce任務,因為b的key1列在第一個join條件使用,而b表的key2列在第二個join條件使用。第一個map/reduce任務join a和b。第二個任務是第一個任務的結果join c。

 

在join的每個map/reduce階段,序列中的最后一個表,當其他被緩存時,它會流到reducers。所以,reducers需要緩存join關鍵字的特定值組成的行,通過組織最大的表出現在序列的最后,有助於減少reducers的內存。

SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key1)

三個表,在同一個獨立的map/reduce任務做join。a和b的key對應的特定值組成的行,會緩存在reducers的內存。然后reducers接受c的每一行,和緩存的每一行做join計算。

 

類似

SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key2)

這里有兩個map/reduce任務在join計算被調用。第一個是a和b做join,然后reducers緩存a的值,另一邊,從流接收b的值。第二個階段,reducers緩存第一個join的結果,另一邊從流接收c的值。

 

在join的每個map/reduce階段,通過關鍵字,可以指定哪個表從流接收。

SELECT /*+ STREAMTABLE(a) */ a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key1)

三個表的連接,會轉換為一個map/reduce任務,reducer會把b和c的key的特定值緩存在內存里,然后從流接收a的每一行,和緩存的行做join。

 

join的結果

LEFT,RIGHT,FULL OUTER連接存在是為了提供ON語句在沒有匹配時的更多控制。例如,這個查詢:

SELECT a.val, b.val FROM a LEFT OUTER JOIN b ON (a.key=b.key)

將會返回a的每一行。如果b.key等於a.key,輸出將是a.val,b.val,如果a沒有和b.key匹配,輸出的行將是a.val,NULL。如果b的行沒有和a.key匹配上,將被拋棄。語法"FROM a LEFT OUTER JOIN b"必須寫在一行,為了理解它如何工作——這個查詢,a是b的左邊,a的所有行會被保持;RIGHT OUTER JOIN將保持b的所有行, FULL OUTER JOIN將會保存a和b的所有行。OUTER JOIN語義應該符合標准的SQL規范。

 

join的過濾

Joins發生在where字句前,所以,如果要限制join的輸出,需要寫在where字句,否則寫在JOIN字句。現在討論的一個混亂的大點,就是分區表

SELECT a.val, b.val FROM a LEFT OUTER JOIN b ON (a.key=b.key)
WHERE a.ds='2009-07-07' AND b.ds='2009-07-07'

將會連接a和b,產生a.val和b.val的列表。WHERE字句,也可以引用join的輸出列,然后過濾他們。

但是,無論何時JOIN的行找到a的key,但是找不到b的key時,b的所有列會置成NULL,包括ds列。這就是說,將過濾join輸出的所有行,包括沒有合法的b.key的行。然后你會在LEFT OUTER的要求撲空。

也就是說,如果你在WHERE字句引用b的任何列,LEFT OUTER的部分join結果是不相關的。所以,當外連接時,使用這個語句

SELECT a.val, b.val FROM a LEFT OUTER JOIN b
ON (a.key=b.key AND b.ds='2009-07-07' AND a.ds='2009-07-07')

join的輸出會預先過濾,然后你不用對有a.key而沒有b.key的行做過濾。RIGHT和FULL join也是一樣的邏輯。

 

join的順序

join是不可替換的,連接是從左到右,不管是LEFT或RIGHT join。

SELECT a.val1, a.val2, b.val, c.val
FROM a
JOIN b ON (a.key = b.key)
LEFT OUTER JOIN c ON (a.key = c.key)

首先,連接a和b,扔掉a和b中沒有匹配的key的行。結果表再連接c。這提供了直觀的結果,如果有一個鍵都存在於A和C,但不是B:完整行(包括a.val1,a.val2,a.key)會在"a jOIN b"步驟,被丟棄,因為它不在b中。結果沒有a.key,所以當它和c做LEFT OUTER JOIN,c.val也無法做到,因為沒有c.key匹配a.key(因為a的行都被移除了)。類似的,RIGHT OUTER JOIN(替換為LEFT),我們最終會更怪的效果,NULL, NULL, NULL, c.val。因為盡管指定了join key是a.key=c.key,我們已經在第一個JOIN丟棄了不匹配的a的所有行。

 

為了達到更直觀的效果,相反,我們應該從

FROM c LEFT OUTER JOIN a ON (c.key = a.key) LEFT OUTER JOIN b ON (c.key = b.key).

 


LEFT SEMI JOIN實現了相關的IN / EXISTS的子查詢語義的有效途徑。由於Hive目前不支持IN / EXISTS的子查詢,所以你可以用 LEFT SEMI JOIN 重寫你的子查詢語句。LEFT SEMI JOIN 的限制是, JOIN 子句中右邊的表只能在 ON 子句中設置過濾條件,在 WHERE 子句、SELECT 子句或其他地方過濾都不行。

SELECT a.key, a.value FROM a WHERE a.key in
 (SELECT b.key FROM B);

可以重寫為

SELECT a.key, a.val FROM a LEFT SEMI JOIN b on (a.key = b.key)

 

mapjoin

但如果所有被連接的表是小表,join可以被轉換為只有一個map任務。查詢是

SELECT /*+ MAPJOIN(b) */ a.key, a.value
FROM a join b on a.key = b.key

不需要reducer。對於每一個mapper,A和B已經被完全讀出。限制是a FULL/RIGHT OUTER JOIN b不能使用。

 

如果表在join的列已經分桶了,其中一張表的桶的數量,是另一個表的桶的數量的整倍,那么兩者可以做桶的連接。如果A有4個桶,表B有4個桶,下面的連接:

SELECT /*+ MAPJOIN(b) */ a.key, a.value
FROM a join b on a.key = b.key

只能在mapper工作。為了為A的每個mapper完整抽取B。對於上面的查詢,mapper處理A的桶1,只會抽取B的桶1,這不是默認行為,要使用以下參數:

set hive.optimize.bucketmapjoin = true

 

如果表在join的列經過排序,分桶,而且他們有相同數量的桶,可以使用排序-合並 join。每個mapper,相關的桶會做連接。如果A和B有4個桶,

SELECT /*+ MAPJOIN(b) */ a.key, a.value
FROM A a join B b on a.key = b.key

只能在mapper使用。使用A的桶的mapper,也會遍歷B相關的桶。這個不是默認行為,需要配置以下參數:

set hive.input.format=org.apache.hadoop.hive.ql.io.BucketizedHiveInputFormat;
set hive.optimize.bucketmapjoin = true;
set hive.optimize.bucketmapjoin.sortedmerge = true;

 

翻譯自 https://cwiki.apache.org/confluence/display/Hive/LanguageManual+Joins


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM