topN問題是SQL面試里經常考的一個問題,即如何取每組最大的N條記錄。
這里摘取leetcode上的一道題,因為主要為了說明如何選取topN的記錄,因此這里刪掉了第二張表(不需要進行兩表連接)。
題目:根據Employee表中的信息,找出每個部門工資前三高的員工信息(部門號,姓名,工資)
| Id | Name | Salary | DepartmentId | +----+-------+--------+--------------+ | 1 | Joe | 70000 | 1 | | 2 | Henry | 80000 | 2 | | 3 | Sam | 60000 | 2 | | 4 | Max | 90000 | 1 |
解法一:使用window function
因為這里需要選取的是前三高的工資,如果工資相同,那么排名並列,因此這里用的窗口函數為dense_rank()。
SELECT DepartmentId, Name, Salary, FROM (SELECT *, DENSE_RANK() OVER (PARTTITION BY DepartmentId ORDER BY Salary DESC) AS rank FROM Employee) WHERE rank<=3;
這里需要注意的是:因為where在select之前執行,因此如果直接使用rank進行條件篩選會報錯。需要再嵌套一層選擇子句,把之前的語句放入from里面,因為from是最先執行的,因此這樣就不會報錯。
使用窗口函數是比較輕松的解法,但是有些數據庫不支持窗口函數,因此很可能面試官會問你,如果不用窗口函數,這題該怎么解?
解法二:使用關聯子查詢
SELECT DepartmentId, Name, Salary FROM Employee WHERE (SELECT COUNT(DISTINCT e.Salary) FROM Employee AS e WHERE e.DepartmentId = Employee.DepartmentId AND e.Salary > Employee.Salary) < 3;
這里的解題思路是,先使用關聯子查詢把相同部門的員工歸在一起,然后條件篩選出比各個員工工資高的員工,如果這些篩選出的員工的數量小於3個,那就說明進行對比的這些員工排在前三位。比如,如果你排在你們部門第一位,那么就有0個人的工資比你高,如果你排在第三位,那么就有2個人的工資比你高。
可以看到,上面關聯的兩張表其實是相同的兩張表,因此也可以用自連接來解題,但是由於此種方式沒有關聯子查詢來得清晰,因此這里就不表了。