還記得在一次面試中,被面試官問了這樣一個問題

當連接兩個表時,如果連接鍵有NULL值,最後返回的結果會是什麼樣?

實話實說,當時我連問題都沒聽懂。。。直到有一天,我看了《SQL基礎教程》。

好吧,是的,我又自來水了一波。

但這是真事。當時我已經刷了好幾遍《必知必會》(又吐槽),並且在實習中也寫過一定量的SQL,但是對於這個問題,我真的是想都沒想過,或者說當時對於數據結構的理解還非常模糊,我認為這就是入門不當,導致基礎沒有打牢的後果。

有興趣的同學可以在這裡停下來,思考這個問題幾分鐘,自測一下自己的SQL水平。在最後我會公佈我的答案。

接下來,先放上我對關於NULL的總結:

  1. 所有包含NULL的算術運算結果都為NULL

2. 不要對NULL使用比較運算符,因為「<>NULL」和「=NULL」等等,得到的真值為unknown,所以查詢不出任何記錄。如果想要查詢包含NULL的記錄,必須使用IS NULL和IS NOT NULL

3. 使用group by進行分組時,如果聚合鍵(分組鍵)包含NULL,那麼會將NULL單獨作為一組返回

4. 除了COALESCE函數,算術函數、字元串函數、日期函數、轉換函數對NULL都返回NULL

5. 所有聚合函數,如果以列作為參數,那麼會先把NULL排除在外再進行聚合計算

6. COUNT(*)可以得到包含NULL值行在內的所有行數

7. 使用order by不能對NULL排序,NULL會集中出現在返回結果的開頭或結尾(與RDBMS有關)

看出來了嗎?其實上面每一條總結都圍繞一個點,那就是:NULL值無法和其他值進行比較!

第1條,數字和NULL做加減乘除的時候,程序不認識NULL,所以就不知道這個運算結果是什麼,所以最後只能返回NULL。

第2條,當我們拿數字、字元串或日期和NULL進行比較的時候,程序不知道它們和NULL誰大誰小,或者是不是同一個值,所以程序只能告訴你,I dont know,而程序只會返回比較結果是True的數據。所以,這裡你就知道了IS NULL和IS NOT NULL的存在意義,這兩個謂詞是專門負責處理NULL值的。

第3條,我們可以這樣理解group by的執行邏輯,比如聚合鍵裏有a、b、c這三個非NULL值,然後自動循環每一行,拿每一行的值和a、b、c進行比較,如果相等就歸為一類,結果當碰到NULL值的時候,程序就沒法比較了,所以沒辦法,最終就單獨把NULL分為一類。

第4條,同樣的道理,當把NULL作為參數傳給算術函數、字元串函數、日期函數、轉換函數的時候,程序不認識NULL,不知道怎麼處理,所以結果只能返回NULL。

第5條,根據我們上面說的,NULL值沒辦法進行比較,也沒辦法參與計算,所以聚合函數就比較智能了,它會自動先把NULL值排除掉,只對剩下的值進行運算,這樣就避免了一碰到NULL值就沒辦法使用的尷尬。

第6條,COUNT(某一列),如果這一列包含有NULL值,那麼同樣它也會先把NULL值的數據去掉,這樣只返回非NULL值的行數,所以如果想要得到包含NULL值在內的所有行數,就必須使用COUNT(*)。

第7條,order by的執行邏輯也有一個比較的過程,通過比較大小來把所有的值進行排序,那麼NULL值沒辦法和其他值比較,所以設計order by的時候乾脆就單獨把NULL剔出來,要麼全部放最前面,要麼全部放最後面,各家RDBMS設計不同,至於NULL值和NULL值之間的排序則是隨機的。

看到這裡,你是否和我一樣對一開始提出的那個問題有了答案呢?

當連接兩個表時,如果連接鍵有NULL值,最後返回的結果會是什麼樣?

首先,要明確表連接是內連還是外連。

  • 假設是內連:

A inner join B on A.c = B.c

由於NULL值無法進行比較,所以,A表c列是NULL值的數據、以及B表c列是NULL值的數據,全都會被排除掉。也就是說,A表和B表的c列所有是NULL值的記錄都不會被返回出來。

  • 假設是外連:

A left join B on A.c = B.c

首先要知道,外連的主表(A表)的所有記錄會全部被返回,包括A表c列是NULL值的數據。但是,B表c列的NULL值沒法進行比較,所以B表c列是NULL值的記錄會全部被排除。最終兩張表連接後返回的結果就是,A表c列是NULL值的記錄返回出來了,但旁邊B表所有列的值全為NULL。

以上就是我的答案,你和我想法一樣嗎?歡迎和我討論噢~

最後我想說:面試官我懂了,你別走,回來好嗎!!!(淚目)


推薦閱讀:
相關文章