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部落格
下面我們創造兩個DF來進行測試。
- private static List<Customer> getCustomers() {
- List<Customer> customerList = new ArrayList<>(3);
- customerList.add(new Customer(100, "張三"));
- customerList.add(new Customer(101, "張四"));
- customerList.add(new Customer(102, "張五"));
-
System.out.println("Customer:
- return customerList;
- }
- private static List<Payment> getPayments() {
- Random random = new Random(0);
- List<Payment> paymentList = new ArrayList<>(6);
- for (int a = 0; a < 6; a++) {
- int i = random.nextInt(10000);
-
Payment p = new Payment((long) (a + 1), (long
- paymentList.add(p);
- }
- System.out.println("Payment: " + paymentList);
- return paymentList;
- }
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會忽略這些記錄,如果不想忽略可以這樣:
- import static org.apache.spark.sql.functions.col;
- Dataset<Row> join = payment.as("c1").join(payment.as("c2"), col("c1.paymentId").eqNullSafe(col("c2.customerId")));
- join.show();
這裡使用了方法eqNullSafe
結果如下
現在連customer也改成null看一下
兩張表內連線結果如下
改成使用方法eqNullSage,結果如下
好像看起來不錯,但你去把結果跟最早的結果(沒比較Null的時候)對比發現,這裡customerId出現了兩次,而之前只出現了一次。
這裡可以使用drop方法移除列:
- join.drop("customerId").show();
- join.drop(payment.col("customerId")).show();
效果可以猜一下。
JoinWith
最後說一下這個方法。從它的簽名可以猜出作用:
把前面內連線的例子改成joinWith方法:
結果中每一行是一個元組,元組的兩個元素分別是兩個表的原始記錄。
最後
已經都來到這了,你不想知道左半連線或左反連線的joinWith結果是啥嗎?