1. 程式人生 > >SQL join,left join ,right join , inner join , outer join用法解析及HIVE join 優化

SQL join,left join ,right join , inner join , outer join用法解析及HIVE join 優化

Sql程式碼  收藏程式碼
  1. SELECT a.val, b.val, c.val FROM a JOIN b ON (a.key = b.key1) JOIN c ON (c.key = b.key2)  
 

join的快取和任務轉換

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

Sql程式碼  收藏程式碼
  1. 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被呼叫。

另一方面

Sql程式碼  收藏程式碼
  1. 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的記憶體。

Sql程式碼  收藏程式碼
  1. 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計算。

類似

Sql程式碼  收藏程式碼
  1. 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階段,通過關鍵字,可以指定哪個表從流接收。

Sql程式碼  收藏程式碼
  1. 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語句在沒有匹配時的更多控制。例如,這個查詢:

Sql程式碼  收藏程式碼
  1. 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字句。現在討論的一個混亂的大點,就是分割槽表

Sql程式碼  收藏程式碼
  1. SELECT a.val, b.val FROM a LEFT OUTER JOIN b ON (a.key=b.key)  
  2. 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結果是不相關的。所以,當外連線時,使用這個語句

Sql程式碼  收藏程式碼
  1. 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。

Sql程式碼  收藏程式碼
  1. SELECT a.val1, a.val2, b.val, c.val  
  2. FROM a  
  3. JOIN b ON (a.key = b.key)  
  4. 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的所有行。

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

Sql程式碼  收藏程式碼
  1. FROM c LEFT OUTER JOIN a ON (c.key = a.keyLEFT 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 子句或其他地方過濾都不行。

Sql程式碼  收藏程式碼
  1. SELECT a.key, a.value FROM a WHERE a.key in (SELECT b.key FROM B);  

可以重寫為

Sql程式碼  收藏程式碼
  1. 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個桶,下面的連線:

Sql程式碼  收藏程式碼
  1. 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,這不是預設行為,要使用以下引數:

Sql程式碼  收藏程式碼
  1. set hive.optimize.bucketmapjoin = true;  

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

Sql程式碼  收藏程式碼
  1. SELECT /*+ MAPJOIN(b) */ a.key, a.value FROM A a join B b on a.key = b.key  

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

Sql程式碼  收藏程式碼
  1. set hive.input.format=org.apache.hadoop.hive.ql.io.BucketizedHiveInputFormat;  
  2. set hive.optimize.bucketmapjoin = true;  
  3. set hive.optimize.bucketmapjoin.sortedmerge = true;  

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