文章目录
在如今的程序员面试过程中,考察SQL部分能力,虽不是难点,但几乎是必考.
为检查思路盲点,避免翻船,各位看官也可以复盘下,如对你有一丝的帮助,欢迎给个赞叭~
ps: 数据开发者,建议可以自己加难度进行考察
leetcode难度:中等
面试频率:中 (遇到过类似题型1次+)
1. 题目
1.1 表
Employee 表:
+-------------+------+
| Column Name | Type |
+-------------+------+
| id | int |
| salary | int |
+-------------+------+
id 是这个表的主键。
表的每一行包含员工的工资信息。
1.2 需求
编写一个 SQL 查询,获取并返回 Employee 表中第二高的薪水 。如果不存在第二高的薪水,查询应该返回 null 。
查询结果如下例所示。
输入:
Employee 表:
+----+--------+
| id | salary |
+----+--------+
| 1 | 100 |
| 2 | 200 |
| 3 | 300 |
+----+--------+
输出:
+---------------------+
| SecondHighestSalary |
+---------------------+
| 200 |
+---------------------+
输入:
Employee 表:
+----+--------+
| id | salary |
+----+--------+
| 1 | 100 |
+----+--------+
输出:
+---------------------+
| SecondHighestSalary |
+---------------------+
| null |
+---------------------+
2. 考点分析
第一层解法
这道题很简单,主要考察排序+limit+偏移量的应用,很多新手可能对偏移量OFFSET 不熟,导致不好下手
第二次解法
pass
第三层解法
pass
更多思路
3. 开撸
1. 第一层解法
limit n子句表示查询结果返回前n条数据
offset n表示跳过x条语句
limit y offset x 分句表示查询结果跳过 x 条数据,读取前 y 条数据
使用limit和offset,降序排列再返回第二条记录可以得到第二大的值。
SELECT
IFNULL(
(SELECT DISTINCT Salary
FROM Employee
ORDER BY Salary DESC
LIMIT 1 OFFSET 1),
NULL) AS SecondHighestSalary
PS: 注意sql的健壮性,不要忘记IFNULL 空值的判断
4. 扩展:第N高的薪水
编写一个SQL查询来报告 Employee 表中第 n 高的工资。如果没有第 n 个最高工资,查询应该报告为 null 。
输入:
Employee table:
+----+--------+
| id | salary |
+----+--------+
| 1 | 100 |
| 2 | 200 |
| 3 | 300 |
+----+--------+
n = 2
输出:
+------------------------+
| getNthHighestSalary(2) |
+------------------------+
| 200 |
+------------------------+
输入:
Employee 表:
+----+--------+
| id | salary |
+----+--------+
| 1 | 100 |
+----+--------+
n = 2
输出:
+------------------------+
| getNthHighestSalary(2) |
+------------------------+
| null |
+------------------------+
4.1 说明
排名是数据库中的一个经典题目,实际上又根据排名的具体细节可分为3种场景:
连续排名,例如薪水3000、2000、2000、1000排名结果为1-2-3-4,体现同薪不同名,排名类似于编号
同薪同名但总排名不连续,例如同样的薪水分布,排名结果为1-2-2-4
同薪同名且总排名连续,同样的薪水排名结果为1-2-2-3
不同的应用场景可能需要不同的排名结果,也意味着不同的查询策略。本题的目标是实现第三种排名方式下的第N个结果,且是全局排名,不存在分组的问题,实际上还要相对简单一些。
值得一提的是:在Oracle等数据库中有窗口函数,可非常容易实现这些需求,而MySQL直到8.0版本也引入相关函数。最新OJ环境已更新至8.0版本,可直接使用窗口函数。
4.2 思路
这种题型最简单的就是用窗口函数来撸,当然如果为了挑战自己可以采用更多解法来玩下。
4.3 窗口函数的解法
实际上,在mysql8.0中有相关的内置函数,而且考虑了各种排名问题:
- row_number(): 同薪不同名,相当于行号,例如3000、2000、2000、1000排名后为1、2、3、4
- rank(): 同薪同名,有跳级,例如3000、2000、2000、1000排名后为1、2、2、4
- dense_rank(): 同薪同名,无跳级,例如3000、2000、2000、1000排名后为1、2、2、3
- ntile(): 分桶排名,即首先按桶的个数分出第一二三桶,然后各桶内从1排名,实际不是很常用
显然,本题是要用第三个函数。
另外这三个函数必须要要与其搭档over()配套使用,over()中的参数常见的有两个,分别是
- partition by,按某字段切分
- order by,与常规order by用法一致,也区分ASC(默认)和DESC,因为排名总得有个依据
注:下面代码仅在mysql8.0以上版本可用,最新OJ已支持。
SELECT
DISTINCT salary
FROM
(SELECT
salary, dense_rank() over(ORDER BY salary DESC) AS rnk
FROM
employee) tmp
WHERE rnk = N
作业
关于排名的问题,你还有更好的解法吗?