Spark3學習【基於Java】5. Spark-Sql聯表查詢JOIN


大數據場景下,聯表遠比微小型關系型數據庫中使用的頻繁。網上有句話:

 傳統數據庫單機模式做Join的場景畢竟有限,也建議盡量減少使用Join。
 然而大數據領域就完全不同,Join是標配,OLAP業務根本無法離開表與表之間的關聯,對Join的支持成熟度一定程度上決定了系統的性能,誇張點說,'得Join者得天下'。
 -- SparkSQL – 有必要坐下來聊聊Join – 有態度的HBase/Spark/BigData (hbasefly.com)
			

不同數據庫引擎對JOIN的實現算法一般不同,我們最常用的mysql中的join實現是Nested Loop Join ( MySQL中Join算法實現原理通俗易懂_墨卿風竹的博客-CSDN博客),Spark中支持的要更廣泛。

下面我們創造兩個DF來進行測試。

  1. private static List<Customer> getCustomers() {
  2.     List<Customer> customerList = new ArrayList<>(3);
  3.     customerList.add(new Customer(100, "張三"));
  4.     customerList.add(new Customer(101, "張四"));
  5.     customerList.add(new Customer(102, "張五"));
  6.     System.out.println("Customer: " + customerList);
  7.     return customerList;
  8. }
  9.  
  10. private static List<Payment> getPayments() {
  11.     Random random = new Random(0);
  12.     List<Payment> paymentList = new ArrayList<>(6);
  13.     for (int a = 0; a < 6; a++) {
  14.         int i = random.nextInt(10000);
  15.         Payment p = new Payment((long) (a + 1), (long) (100+ a), (double) i);
  16.         paymentList.add(p);
  17.     }
  18.     System.out.println("Payment: " + paymentList);
  19.     return paymentList;
  20. }

Inner Join 

內連接是spark默認的連接方式。通過join方法即可使用內連接:

你要這樣用的話,會發現還有一個方法不用傳入連接字段,猜一下輸出什么:

上面這種連接只能指定一個連接字段,如果需要多字段匹配呢?spark提供了另一個方法:

這個方法的第二個參數Java沒法直接提供,需要轉換一下:

left join

DF沒有直接提供leftJoin這樣的方法,只提供了join()和crossJoin()兩個。從上面的文檔截圖可以看到,通過傳第三個參數來指定不同的連接方式。

現在對Java程序員不太友好了,每次join都要先轉一次:可能這也是網上的博客、教程都用scala的原因吧。

right join

和left join類似:

outer join

全外連接是左外和右外的組合,這里不演示了。

cross join

這個上面提到了 ,有對應的方法。它產生的是笛卡爾積,會產生大量結果:

這個方法是2.1之后增加的。之前也是通過join方法實現,但是會被不小心誤用,就增加了一個明確的方法。

 

Left-Semi-Join

左半連接和左連接比較類似,差別是結果中不包含右表字段,僅包含左表字段。

左連接不是既包含左表字段,又有右表字段,右表中不匹配的字段也顯示但是為null。左半連接是右表不匹配的記錄左表就不展示了,實際更應該叫semi-inner-join。它相當於關系型SQL中的子查詢。

但是由於只返回左表,所以叫左半連接。同時並不提供右半連接操作,因為它就是內連接。

下面是連接方式映射

 

Left-anti-Join

左反連接是左半連接的取反,並不是右半連接。它展示的是左連接以后,右表是null的那些左表數據,也就是內連接以后左表的補集。相等於關系型數據庫的not in。

Self Join

自連接就是DF跟自己連接,所以需要通過別名來區分兩個DF。

自連接我們再Mysql中用的不少,一般用來查詢層級數據,比如有父子關系的記錄。為了簡單,假設Payment中兩個字段有父子關系,於是這樣查詢:

上面造的數據都不滿足,所以改成這樣:

運行輸出是

如果把第一個參數的開始值改成98,輸出就是

Null 字段匹配

假設在連接過程中(任何連接場景),連接字段出現了null會怎么樣?

假設payement記錄如下

默認情況下,spark會忽略這些記錄,如果不想忽略可以這樣:

  1. import static org.apache.spark.sql.functions.col;
  2.  
  3.  
  4. Dataset<Row> join = payment.as("c1").join(payment.as("c2"), col("c1.paymentId").eqNullSafe(col("c2.customerId")));
  5. join.show();

這里使用了方法eqNullSafe

結果如下

現在連customer也改成null看一下

兩張表內連接結果如下

改成使用方法eqNullSage,結果如下

好像看起來不錯,但你去把結果跟最早的結果(沒比較Null的時候)對比發現,這里customerId出現了兩次,而之前只出現了一次。

這里可以使用drop方法移除列:

  1. join.drop("customerId").show();
  2. join.drop(payment.col("customerId")).show();

效果可以猜一下。

JoinWith

最后說一下這個方法。從它的簽名可以猜出作用:

把前面內連接的例子改成joinWith方法:

結果中每一行是一個元組,元組的兩個元素分別是兩個表的原始記錄。

最后

已經都來到這了,你不想知道左半連接或左反連接的joinWith結果是啥嗎?


免責聲明!

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



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