前言
前一篇介紹簡單介紹為什麼要用資料庫來存取資料,簡單的比較SQL跟NoSQL的差異,也提供了Mac安裝兩種資料庫的資源,還沒閱讀前一篇,可以手刀前往閱讀🚪DB學習筆記 — 基本概念/安裝資源。
接下來的一系列就是透過Leetcode及HackerRank來刷題,並把解題的流程記錄下來,歡迎大家一起分享與討論。
另外,因為Leetcode其實是需要收費才能進行大部分題目的練習,網路上有大量的題目可以看,這時候就可以在自己本機搭建PostgreSQL,進行刷題的練習了,事不宜遲,我們馬上開始。
目錄
- Leetcode 175 — Combine Two Tables⭐️
- Leetcode 176 — Second Highest Salary⭐️⭐️
- Leetcode 176(改) — 10th Highest Salary⭐️⭐️⭐️
可以透過Ctrl / Command + F 來進行搜尋,找到自己有興趣的題目去練習。
Leetcode 175 — Combine Two Tables⭐️
題目敘述:
Write a SQL query for a report that provides the following information for each person in the Person table, regardless if there is an address for each of those people.
FirstName, LastName, City, State
解題思路:
首先,我們可以看到這次有兩個表格,分別是Person
及Address
:Person
表中有PersonId, FirstName, LastName
Address
表中則是AddressId, PersonId, City, State
這兩個表格的關聯性是在Person
表的PersonId
跟Address
表的PersonId
,
上面粗體的地方有提到要把Person
表中的全部資料都讀出來,不管在Address
表中有沒有對應的資料。
就這樣的解題思路我們可以知道待會是要透過Person LEFT JOIN Address
來進行兩個表格的串接,那這題的答案就呼之欲出了!
SELECT FirstName, LastName, City, State
FROM Person LEFT JOIN Address #題目要在Person表中全部的PersonId
ON Person.PersonId = Address.PersonId
另外,如果這兩個表格要進行JOIN的時候,他們的欄位名稱是相同的時候,我們可以透過USING(欄位名稱)
這個方式,這樣就不用重複寫出表格的名稱!
SELECT FirstName, LastName, City, State
FROM Person LEFT JOIN Address
USING (PersonId)
這兩種方式都可以順利的得到正確的答案喔!
Leetcode 176 — Second Highest Salary⭐️⭐️
題目敘述:
Write a SQL query to get the second highest salary from the Employee
table.
For example, given the above Employee table, the query should return 200
as the second highest salary. If there is no second highest salary, then the query should return null
.
解題思路:
從題目可以很明顯的知道,是要找第二高的薪水,且如果沒有第二高的薪水的話就回傳一個null
值。這題是個可以好好深入探討的題目,我想到了不少解法,這邊來跟大家討論一下!
📌解法一:首先可以先找出最高的薪水,然後再找出比最高薪水的小的最大值,這樣就會是第二高的薪水。
SELECT MAX(Salary) as SecondHighestSalary
FROM Employee
WHERE salary < (
SELECT MAX(Salary)
FROM Employee
)
📌解法二:基本上跟解法一的邏輯差不多,只是用NOT IN
取代小於。
SELECT MAX(Salary) as SecondHighestSalary
FROM Employee
WHERE salary NOT IN (
SELECT MAX(Salary)
FROM Employee
)
📌解法三:是使用LIMIT
跟OFFSET
這兩個語法直接進行LIMIT
就是取幾個數字,那OFFSET
就是按照排序後刪除前N個,所以假如要找到第二個,就是透過LIMIT 1 OFFSET 1
的方式來找。
SELECT(
SELECT DISTINCT Salary
FROM Employee
ORDER BY Salary DESC
LIMIT 1 OFFSET 1
) as SecondHighestSalary
這邊外面再包一層SELECT並將結果改名成SecondHighestSalary的目的是,因為題目要求,如果沒有第二高的薪水的話就回傳一個null值,但如果沒有外面那層,答案就不會回傳null
,而是什麼都沒有。那這邊也可以透過IFNULL
的方式來解決這個問題!
首先解釋一下IFNULL(X,Y)
的意思,當X為null的話,就回傳Y,如果不是null的話,就回傳原本的X!
SELECT IFNULL(null,'ABC')
-> 'ABC'
SELECT IFNULL(12,555)
-> 12
看懂這邊範例的話,那剛剛那邊就可以改寫成下方這樣,會有一樣的結果。
SELECT IFNULL(
(SELECT DISTINCT Salary
FROM Employee
ORDER BY Salary DESC
LIMIT 1 OFFSET 1)
, NULL
) AS SecondHighestSalary
📌解法四:使用window function的dense_rank()
或是rank()
來解
首先我們知道透過dense_rank()
或rank()
這兩個語法,可以找到比大小後的排序,那我們透過CTE的方式,將Salary, Rank
建立成一個新的表格,然後在篩選的時候就可以設定取第二名的薪水出來,並且改名為題目所要求的。
with ranked as (
SELECT DISTINCT Salary,
RANK() over (ORDER BY Salary DESC) r
FROM employee
)
#透過CTE將薪水跟排名建立成ranked的表格,再讀取該表格
SELECT (
SELECT Salary
FROM ranked
WHERE r = 2
) as SecondHighestSalary
之所以要去解法2.3.4去寫的原因是,假如今天不是問第二高的薪水,就不能用現在的方式去找,今天如果題目是問第十高的薪水?我的解法三四都可以很順利的去做更改就可以找到答案,下面附上參考答案~
Leetcode 176(改) — 10th Highest Salary⭐️⭐️⭐️
題目:找出第10高的薪水
📌解法一:只要將原解法三的粗體的地方稍作更改,就可以找到答案
SELECT(
SELECT DISTINCT Salary
FROM Employee
ORDER BY Salary DESC
LIMIT 1 OFFSET 9
) as SecondHighestSalary
📌解法二:只要將原解法四粗體的地方稍作更改,就可以找到答案
with ranked as (
SELECT DISTINCT Salary,
RANK() over (ORDER BY Salary DESC) r
FROM employee
)
#透過CTE將薪水跟排名建立成ranked的表格,再讀取該表格
SELECT (
SELECT Salary
FROM ranked
WHERE r = 10
) as SecondHighestSalary·
當然原解法一二做一些調整也可以達到一樣的結果,但是就沒有像是原解法三四有這樣的易維護性。
小結
SQL刷題001就先到這邊,雖然只有2+1題,但是其實可以思考的地方可說是非常多,敬請期待SQL刷題002,會慢慢把更複雜的題目加進來,也希望把題目做更全面的分析,勁量每題有更多種的解法。
我是尚恩,一個從商學院畢業的菜鳥工程師,有任何想從商轉換跑道的問題都可以找我聊聊,歡迎透過Linkedin@SeanChien聯絡我~
如果喜歡我的文章可以幫我鼓掌、訂閱、分享謝謝😇!