1. 程式人生 > 其它 >Spark3學習【基於Java】5. Spark-Sql聯表查詢JOIN

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. private static List<Payment> getPayments() {
  10. Random random = new Random(0);
  11. List<Payment> paymentList = new ArrayList<>(6);
  12. for (int a = 0; a < 6; a++) {
  13. int i = random.nextInt(10000);
  14. Payment p = new Payment((long) (a + 1), (long
    ) (100+ a), (double) i);
  15. paymentList.add(p);
  16. }
  17. System.out.println("Payment: " + paymentList);
  18. return paymentList;
  19. }

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. Dataset<Row> join = payment.as("c1").join(payment.as("c2"), col("c1.paymentId").eqNullSafe(col("c2.customerId")));
  3. join.show();

這裡使用了方法eqNullSafe

結果如下

現在連customer也改成null看一下

兩張表內連線結果如下

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

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

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

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

效果可以猜一下。

JoinWith

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

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

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

最後

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