天天看點

sql leftjoin 如果為空不關聯_一句SQL,我有6種寫法

導讀

最近在刷LeetCode中資料庫題目時,有一道排名題目,用了6種寫法分别代表6種SQL思維來實作,想想也算是有趣。

sql leftjoin 如果為空不關聯_一句SQL,我有6種寫法

題目描述:

sql leftjoin 如果為空不關聯_一句SQL,我有6種寫法

題意了解不難,無非就是查找排名為N的記錄,但常用SQL的都知道這裡存在一個歧義,即排名是否存在相同和是否跳級的問題。經測試,這裡的排名是"緻密"排名(dense_rank),即同薪同名且不跳級那種。例如對于薪水3000/2000/2000/1000排名之後為1、2、2、3,若取N=3,則傳回結果1000。另外,題目形式是一個自定義函數,但本質仍是一個SQL查詢。

面對這樣的一道題,你能迅速想到幾種SQL寫法呢?

解法1 limit+offset 由于這裡題目需求很簡單,僅僅是傳回全局的第N高薪水,而不存在分組排名或其他需求,是以最簡單的辦法就是用limit+offset關鍵字直接擷取。 SQL語句: 執行效率:

sql leftjoin 如果為空不關聯_一句SQL,我有6種寫法

由于隻進行單表查詢+單字段排序,對salary字段建立索引時查詢效率會非常高。

解法2 子查詢

既然是排名為N,那麼就意味着大于等于目标薪水的記錄數為N,更準确的說這裡是去重後的記錄數為N。基于此想法,很快可以寫出相應SQL:

SQL語句: 執行效率:

sql leftjoin 如果為空不關聯_一句SQL,我有6種寫法

這個子查詢效率要低不少,因為每條記錄都要執行一條子查詢判斷聚合次數是否等于N。 解法3 連接配接查詢

個人認為,SQL最強大也最有代表性的操作在于多表關聯,這個問題自然也可以用連接配接查詢。MySQL中主要支援join、left join和right join三種連接配接方式。具體到這一題,可以選用任何一種。例如,如果限定連接配接條件是薪水大于等于(含等于),則可直接用join實作兩表自連接配接,然後對另一個計數即可;而如果限定連接配接條件是薪水大于(不含等于),則必須用left join,避免N取特殊值1時出現關聯結果為空而查詢失敗的情況。具體來說:

應用join的SQL語句: 執行效率:

sql leftjoin 如果為空不關聯_一句SQL,我有6種寫法

應用left join的SQL語句: 另外,right join本質上和left join是一緻的,簡單交換兩表順序可以很容實作right join寫法。 執行效率:

sql leftjoin 如果為空不關聯_一句SQL,我有6種寫法

可見,無論是用内連接配接還是外連接配接,效率都不是太高,與子查詢效率相當。 解法4 笛卡爾積

用join連接配接方式實作的SQL,都能用笛卡爾積實作,且一般來說笛卡爾效率要略低于連接配接查詢,但很多情況下MySQL優化器會将笛卡爾積形式的查詢優化成join形式,此時二者執行過程是一緻的。可以很容易将解法3中的形式改成笛卡爾積形式的寫法。

SQL語句: 執行效率:

sql leftjoin 如果為空不關聯_一句SQL,我有6種寫法

這個查詢的效率相比連接配接查詢和子查詢又要略低一些。 解法5 自定義變量

前面已經介紹了4種解法,對比來看:解法2-4中都存在兩表關聯的問題,而解法1因為僅涉及到單表排序,是以效率相比之下更高;另一方面,解法2-4功能更具擴充性:例如可以很容易實作分組查詢排名第N高,而這是簡單的limit+offset寫法所不能實作的。那麼,有沒有既能拓展到分組查詢、同時又具有單表查詢的高效呢?答案是肯定的,例如下面的自定義變量寫法,通過設定一個自變量,擷取每個薪水的排名資訊,然後篩選排名為N的薪水即可。

SQL語句: 執行效率:

sql leftjoin 如果為空不關聯_一句SQL,我有6種寫法

因為僅涉及到單表查詢,是以效率更高,與直接用limit+offset效率相當。 解法6 視窗函數

實際上,解法5中的自定義變量查詢寫法在MySQL8.0以後有相應的視窗函數可以實作。視窗函數在MySQL8.0版本首次引進,而其他很多SQL語言則早已内置。具體而言,對于本題擷取"緻密"排名的薪水,用到的視窗函數就是dense_rank()。

SQL語句: 實際執行過程和解法5是一樣的,隻是調用内置函數寫法更加簡潔,效率也與解法5相當并略高于後者。因為目前OJ系統應用MySQL5.6版本,是以無法測試效率。 對比總結

以上用6種寫法實作同一需求,實際上這應該也代表了絕大多數寫SQL查詢的一般性思路:

  • 能用單表優先用單表,即便是需要用group by、order by、limit等,效率一般也比多表高
  • 不能用單表時優先用連接配接,連接配接是SQL中非常強大的用法,小表驅動大表+建立合适索引+合理運用連接配接條件,基本上連接配接可以解決絕大部分問題。但join級數不宜過多,畢竟是一個接近指數級增長的關聯效果
  • 能不用子查詢、笛卡爾積盡量不用,雖然很多情況下MySQL優化器會将其優化成連接配接方式的執行過程,但效率仍然難以保證
  • 自定義變量在複雜SQL實作中會很有用,例如LeetCode中困難級别的資料庫題目很多都需要借助自定義變量實作
  • 如果MySQL版本允許,視窗函數是一個最優選擇,除了經典的擷取3種排名資訊,還有聚合函數、向前向後取值、百分位等,具體可參考官方指南(本号回複關鍵字"教程"提供網盤下載下傳)
sql leftjoin 如果為空不關聯_一句SQL,我有6種寫法

MySQL8.0内置視窗函數

sql leftjoin 如果為空不關聯_一句SQL,我有6種寫法

相關閱讀:

  • 分享幾道LeetCode中的MySQL題目解法
  • MySQL中查詢中位數?
  • MySQL查詢連續打卡資訊?
  • 劃重點!你還在困惑MySQL中的"鎖"嗎?
  • 一文解決所有MySQL分類排名問題
  • MySQL模糊搜尋的幾種姿勢