Hive SQL查詢left join下on條件和where條件區別


先給個結論:

總結:Hive SQL 中 LEFT JOIN 單獨針對左表的過濾條件必須放在WHERE上,放在ON上的效果是不可預期的,單獨針對右表的查詢條件放在ON上是先過濾右表,再和左表聯表,放在WHERE條件上則是先聯表再過濾,語義上存在差別。

補充:對於MYSQL中的LEFT JOIN和Hive中表現相似,更深層次分析可以看看 https://developer.aliyun.com/article/718897

 

 

----------------

現象

left join在我們使用mysql查詢的過程中可謂非常常見,比如博客里一篇文章有多少條評論、商城里一個貨物有多少評論、一條評論有多少個贊等等。但是由於對joinonwhere等關鍵字的不熟悉,有時候會導致查詢結果與預期不符,所以今天我就來總結一下,一起避坑。

這里我先給出一個場景,並拋出兩個問題,如果你都能答對那這篇文章就不用看了。

假設有一個班級管理應用,有一個表classes,存了所有的班級;有一個表students,存了所有的學生,具體數據如下(感謝廖雪峰的在線SQL):

SELECT * FROM classes;

id name 1 一班 2 二班 3 三班 4 四班 

SELECT * FROM students;

id  class_id  name   gender
1 1 小明 M 2 1 小紅 F 3 1 小軍 M 4 1 小米 F 5 2 小白 F 6 2 小兵 M 7 2 小林 M 8 3 小新 F 9 3 小王 M 10 3 小麗 F 

那么現在有兩個需求:

  1. 找出每個班級的名稱及其對應的女同學數量
  2. 找出一班的同學總數

對於需求1,大多數人不假思索就能想出如下兩種sql寫法,請問哪種是對的?

SELECT c.name, count(s.name) as num FROM classes c left join students s on s.class_id = c.id and s.gender = 'F' group by c.name

或者

SELECT c.name, count(s.name) as num FROM classes c left join students s on s.class_id = c.id where s.gender = 'F' group by c.name 

對於需求2,大多數人也可以不假思索的想出如下兩種sql寫法,請問哪種是對的?

SELECT c.name, count(s.name) as num FROM classes c left join students s on s.class_id = c.id where c.name = '一班' group by c.name 

或者

SELECT c.name, count(s.name) as num FROM classes c left join students s on s.class_id = c.id and c.name = '一班' group by c.name 

請不要繼續往下翻 !!先給出你自己的答案,正確答案就在下面。
.
.
.
.
.
.
.
.
答案是兩個需求都是第一條語句是正確的,要搞清楚這個問題,就得明白mysql對於left join的執行原理,下節進行展開。

根源

mysql 對於left join的采用類似嵌套循環的方式來進行從處理,以下面的語句為例:

SELECT * FROM LT LEFT JOIN RT ON P1(LT,RT)) WHERE P2(LT,RT) 

其中P1on過濾條件,缺失則認為是TRUEP2where過濾條件,缺失也認為是TRUE,該語句的執行邏輯可以描述為:

FOR each row lt in LT {// 遍歷左表的每一行 BOOL b = FALSE; FOR each row rt in RT such that P1(lt, rt) {// 遍歷右表每一行,找到滿足join條件的行 IF P2(lt, rt) {//滿足 where 過濾條件 t:=lt||rt;//合並行,輸出該行 } b=TRUE;// lt在RT中有對應的行 } IF (!b) { // 遍歷完RT,發現lt在RT中沒有有對應的行,則嘗試用null補一行 IF P2(lt,NULL) {// 補上null后滿足 where 過濾條件 t:=lt||NULL; // 輸出lt和null補上的行 } } } 

當然,實際情況中MySQL會使用buffer的方式進行優化,減少行比較次數,不過這不影響關鍵的執行流程,不在本文討論范圍之內。

從這個偽代碼中,我們可以看出兩點:

  1. 如果想對右表進行限制,則一定要在on條件中進行,若在where中進行則可能導致數據缺失,導致左表在右表中無匹配行的行在最終結果中不出現,違背了我們對left join的理解。因為對左表無右表匹配行的行而言,遍歷右表后b=FALSE,所以會嘗試用NULL補齊右表,但是此時我們的P2對右表行進行了限制,NULL若不滿足P2(NULL一般都不會滿足限制條件,除非IS NULL這種),則不會加入最終的結果中,導致結果缺失。
  2. 如果沒有where條件,無論on條件對左表進行怎樣的限制,左表的每一行都至少會有一行的合成結果,對左表行而言,若右表若沒有對應的行,則右表遍歷結束后b=FALSE,會用一行NULL來生成數據,而這個數據是多余的。所以對左表進行過濾必須用where。

下面展開兩個需求的錯誤語句的執行結果和錯誤原因:
需求1

name    num
一班    2 二班 1 三班 2 

需求2

name    num
一班    4 二班 0 三班 0 四班 0 
  1. 需求1由於在where條件中對右表限制,導致數據缺失(四班應該有個為0的結果)
  2. 需求2由於在on條件中對左表限制,導致數據多余(其他班的結果也出來了,還是錯的)

總結

通過上面的問題現象和分析,可以得出了結論:在left join語句中,左表過濾必須放where條件中,右表過濾必須放on條件中,這樣結果才能不多不少,剛剛好。

SQL 看似簡單,其實也有很多細節原理在里面,一個小小的混淆就會造成結果與預期不符,所以平時要注意這些細節原理,避免關鍵時候出錯。

-------------------------

轉自  https://developer.aliyun.com/article/718897

 

 

 

 

 

 

我們通常的認知是聯表查詢時ON是作用於聯表前(時)的過濾條件,WHERE是對聯表后的數據的過濾條件,但hive sql查詢有個特殊點需要關注,那就是對於left join中的單獨針對左表的ON過濾條件有可能會被忽略,比如

SELECT DISTINCT a.dt FROM
wlxdw.dws_tuid_cheat_di a
LEFT JOIN
wlxdw.apps_tuid_device_map_ds b
ON a.tuid = b.tuid AND a.dt='2021-10-18' AND b.dt='2021-10-19'
1
2
3
4
5
查詢結果


可以看到a.dt='2021-10-18’這個過濾條件並沒有生效,有人可能覺得b.dt='2021-10-19’是不是也沒有生效,把上述SQL稍作修改來驗證一下

SELECT DISTINCT b.dt FROM
wlxdw.dws_tuid_cheat_di a
LEFT JOIN
wlxdw.apps_tuid_device_map_ds b
ON a.tuid = b.tuid AND a.dt='2021-10-18' AND b.dt='2021-10-19'
1
2
3
4
5

 

顯而易見,右表上的ON條件 b.dt=xxxx 生效了,那如果想要左表的 a.dt=‘2021-10-18’ 生效,可以放在WHERE條件上


而如果把右表的b.dt=‘2021-10-19’ 放到WHERE條件,語義則變的不一樣,由於WHERE是先聯表再進行過濾,這時查詢結果中b.dt就不存在值為NULL的情況了,LEFT JOIN 相當於變成了 INNER JOIN

 

 

 


從上面的結果可以看出ON和WHERE查詢條件語義上的差別

再來看一組SQL對比

 


神奇的一幕出現了,此時ON中的過濾條件a.dt=‘2021-10-18’ 時而生效時而不生效

總結:Hive SQL 中 LEFT JOIN 單獨針對左表的過濾條件必須放在WHERE上,放在ON上的效果是不可預期的,單獨針對右表的查詢條件放在ON上是先過濾右表,再和左表聯表,放在WHERE條件上則是先聯表再過濾,語義上存在差別。

補充:對於MYSQL中的LEFT JOIN和Hive中表現相似,更深層次分析可以看看 https://developer.aliyun.com/article/718897
————————————————
版權聲明:本文為CSDN博主「代碼一天不寫我渾森藍廋」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/zhang197093/article/details/120874962

 


免責聲明!

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



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