iT邦幫忙

9

[演算法][SQL]演算法挑戰系列(4)-Department Top Three Salaries

ㄛ,其實我本來想在這禮拜回歸JavaScript的,但是沒有想到他一般等級的題目就有些難度了,其實也是因為這禮拜沒有時間好好準備文章,真的很抱歉/images/emoticon/emoticon16.gif,不過我本來想說每五週就來一個等級Hard的題目,現在就把它提前一週吧!希望各位大大可以感受到我的誠意,然後我也不曉得他的題目難易度是怎麼做判斷的,可能我的SQL沒有很好,所以看什麼都差不多XD.那這一次的題目和前兩題的解法都有相關,我也有從之前各位的留言分享中學到幾招,所以可以找看看有沒有熟悉的寫法XD,那以下正文:

題目名稱:Department Top Three Salaries
難易度:高
題目內容:取出每個部門中領最高薪水的前三名員工,並依金額由大到小排序。
例如:
Employee表內容:

Id Name Salary DepartmentId
1 Joe 70000 1
2 Henry 80000 2
3 Sam 60000 2
4 Max 90000 1
5 Janet 69000 1
6 Randy 85000 1
Department表內容:
Id Name
------------- -------------
1 IT
2 Sales
查詢結果:
Department Employee Salary
------------- ------------- -------------
IT Max 90000
IT Randy 85000
IT Joe 70000
Sales Henry 80000
Sales Sam 60000
這次附上建立資料表及資料的SQL(用MSSQL語法,無法用再麻煩告訴我):
Create table Employee (Id int, Name varchar(255), Salary int, DepartmentId int)
Create table Department (Id int, Name varchar(255))

insert into Employee (Id, Name, Salary, DepartmentId) values ('1', 'Joe', '70000', '1')
insert into Employee (Id, Name, Salary, DepartmentId) values ('2', 'Henry', '80000', '2')
insert into Employee (Id, Name, Salary, DepartmentId) values ('3', 'Sam', '60000', '2')
insert into Employee (Id, Name, Salary, DepartmentId) values ('4', 'Max', '90000', '1')
insert into Employee (Id, Name, Salary, DepartmentId) values ('5', 'Janet', '69000', '1')
insert into Employee (Id, Name, Salary, DepartmentId) values ('6', 'Randy', '85000', '1')

insert into Department (Id, Name) values ('1', 'IT')
insert into Department (Id, Name) values ('2', 'Sales')

那以下是我的解法:

/*SELECT題目要的欄位*/
SELECT d.Name Department,Employee,Salary
FROM
/*先在內部做一個子查詢,主要是為了幫各部門的員工薪水做排名*/
(SELECT Name Employee,Salary,DepartmentId
/*這裡用DENSE_RANK()做排名 OVER()內PARTITION BY指定的欄位是群組 ORDER BY是以薪水排序*/
,DENSE_RANK() OVER (PARTITION BY e.DepartmentId ORDER BY e.Salary DESC) AS SRank
FROM Employee e) e
/*JOIN部門檔,帶出部門名稱*/
LEFT JOIN Department d ON e.DepartmentId = d.Id
/*查詢條件是名次在前三名,以及在公司部門內的人員(因為我在解題的時候有些員工會是部門檔沒有的部門Id)*/
WHERE SRank<=3 AND d.Name IS NOT NULL

其實如果剛好有解過前面兩篇文章(或是對其他高手來說XD),這一題的難度其實沒有算很高,我也不是有意要循序漸進和挑相同主題,只是就一個剛好三題的類型都差不多,將來會在提供其他不同類型的題目,讓大家和我不要再做排名了,哈哈哈,那老樣子如果以上文章有我沒有注意到的地方,或是各位有其他解法,都歡迎在底下留言分享和告訴我,如果有問題我會立馬修改!謝謝大家!

啊啊再讓我PS一下,下禮拜我一定會回歸JacaScript的,不過一般程式的題目對我來說真的太燒腦,所以小的還是會和SQL的題目交錯分享/images/emoticon/emoticon41.gif


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

2
小碼農米爾
iT邦高手 1 級 ‧ 2018-05-26 20:07:52

期待下禮拜的 Javascript,終於不用排名了。
/images/emoticon/emoticon01.gif

SELECT B.Name AS Department,
       A.Name AS Employee,
	   A.Salary AS Salary
FROM (
	SELECT A.*,
	       DENSE_RANK() OVER (PARTITION BY DepartmentId ORDER BY Salary DESC) AS SRank
	FROM Employee AS A
) AS A
LEFT JOIN Department AS B ON B.Id=A.DepartmentId
WHERE A.SRank<=3
ORDER BY B.Name, A.Salary DESC
神Q超人 iT邦研究生 5 級 ‧ 2018-05-26 21:15:03 檢舉

哈哈哈,回到JS不知道我能不能hold住/images/emoticon/emoticon56.gif

3
小魚
iT邦大師 1 級 ‧ 2018-05-26 22:27:01

是有稍微麻煩一點啦,
不過這算是高級的了嗎?
JavaScript也不錯啊,
感謝 神Q 大大的分享,

SELECT * FROM (
SELECT b.[Name] AS [Department], a.[Name] AS [Employee], a.[Salary],
DENSE_RANK() OVER (PARTITION BY b.[Id] ORDER BY a.[Salary] DESC)  AS rank
FROM Employee AS a
INNER JOIN Department AS b ON a.[Department] = b.[Id]
) AS root
WHERE rank <= 3
ORDER BY [Department], [Salary] DESC;
看更多先前的回應...收起先前的回應...
小魚 iT邦大師 1 級 ‧ 2018-05-26 22:28:10 檢舉

不過我覺得你既然要 LEFT JOIN了,
為什麼還要 d.Name IS NOT NULL 呢?

小魚 iT邦大師 1 級 ‧ 2018-05-26 22:33:45 檢舉

不過看來這次大家都愛上了 DENSE_RANK

神Q超人 iT邦研究生 5 級 ‧ 2018-05-27 18:45:36 檢舉

不會啦!我才感謝你們分享/images/emoticon/emoticon37.gif
條件會加d.Name IS NOT NULL的原因是,因為我是用LEFT JOIN讓員工主檔Employee變成主要table,所以如果以上面的部門主檔Department來說,假設有個員工的部門id值是3那他在Department撈不到資料,那那個欄位就會是null,除非我是以Department當主要的table他才會只撈出現有部門檔有的資料/images/emoticon/emoticon13.gif
對啊!要感謝fysh711426大大分享/images/emoticon/emoticon32.gif

小魚 iT邦大師 1 級 ‧ 2018-05-27 21:19:00 檢舉

我是想問說,
這跟直接用INNER JOIN有什麼差別呢?

INNER JOIN 其實就會把 LEFT JOIN 的 NULL 部分過濾掉,
結果是一樣的,不過我也蠻想知道差別的。
/images/emoticon/emoticon19.gif

神Q超人 iT邦研究生 5 級 ‧ 2018-05-28 18:31:41 檢舉

對耶!如果用INNER JOIN就可以只把兩邊都有的資料取出來了,因為平常用LEFT JOIN太多了,沒有注意到其他JOIN的用法/images/emoticon/emoticon16.gif,不過以這題來看,小魚大大應該是最佳解。
因為我的做法會把是NULL的資料也撈出來,之後再用WHERE過濾掉,但是如果用INNER JOIN的話,一開始就不會出現NULL的資料了,這樣就穩穩的少了一個步驟,在NULL的資料多的情況下,效能應該會好很多,以後我也會多注意JOIN的用法,感謝小魚大大!/images/emoticon/emoticon42.gif

我要留言

立即登入留言