经典SQL题目-求第N高的薪水的解法汇总及知识点复习

2021-09-30  本文已影响0人  Java弟中弟

这几天在看Leetcode的时候逐步开始留意SQL题目,不做不知道,一做才感觉自己的SQL太弱了,因此将一道经典题目:求第N高的薪水的解法进行汇总(MySQL)。相关解法的原文链接已标注在文末~

题目的链接为:第N高的薪水

一、题干

第N高的薪水:编写一个 SQL 查询,获取 Employee 表中第 n 高的薪水(Salary)。

+----+--------+
| Id | Salary |
+----+--------+
| 1  | 100    |
| 2  | 200    |
| 3  | 300    |
+----+--------+

例如上述 Employee 表,n = 2 时,应返回第二高的薪水 200。如果不存在第 n 高的薪水,那么查询应返回 null。

+------------------------+
| getNthHighestSalary(2) |
+------------------------+
| 200                    |
+------------------------+

初始化代码片段。

CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
  RETURN (
      # Write your MySQL query statement below.

  );
END

二、前置知识碎片

简单总结,有时间再详细补充知识点~

1. limit用法

limit子句用于限制查询结果返回的数量。

用法:[select * from tableName limit i,n]

参数

2. order by

order by为排序,ASC(默认升序)和DESC`(降序)

适用于单列升序、单列降序和多列排序,示例:

SELECT * FROM Websites
ORDER BY alexa DESC;

教程链接

3. declare 和 set

[declare 字段名 字段类型]

[set 赋值表达式]

4. if和ifnull

f(true,a,b), if(false,a,b) 这个就是第一个如果是true,就等于a,false就等于b,有点像三元表达式;

ifnull(a, b) ifnull里有两个值,如果a不是null,则返回a, 如果a=null,则返回b。

5. 窗口函数

实际上,在mysql8.0中有相关的内置函数,而且考虑了各种排名问题:

显然,本题是要用第三个函数。另外这三个函数必须要要与其搭档over()配套使用,over()中的参数常见的有两个,分别是

三、解法

1. 解法一:set赋值+distinct去重+limit取值的单表查询

题目要求

显示没有固定的N高薪水,N的确定由自定义函数传入。

解题思路

CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
    declare m INT;
    set m=N-1; 
    RETURN (
        # Write your MySQL query statement below.
        select ifnull(
          (
            select distinct Salary 
            from Employee 
            order by Salary 
            desc limit m,1
          ),
          null
        )
    );
END

注意:此处的set,可以直接set N=N-1,而不用declare m,但声明m会更加明晰一些~

CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
    set N=N-1; 
    RETURN (
        # Write your MySQL query statement below.
        select ifnull(
          (
            select distinct Salary 
            from Employee 
            order by Salary 
            desc limit N,1
          ),
          null
        )
    );
END

2. 解法二:set赋值+group by去重 + limit取值

主体思路同方法一,group by同样可以起到分组去重的效果,用以代替distinct

CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
    set N=N-1; 
    RETURN (
        # Write your MySQL query statement below.
        select ifnull(
          (
            select Salary 
            from Employee 
            group by Salary
            order by Salary 
            desc limit N,1
          ),
          null
        )
    );
END

解法一和解法二最为简洁直观,但仅适用于查询全局排名问题,如果要求各分组的每个第N名,则该方法不适用;而且也不能处理存在重复值的情况。

3. 解法三:连续排名解法

CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
    RETURN (
        # Write your MySQL query statement below.
        select min(a.salary)
        from employee a 
        where (
            select count(*)+1 
            from  (select distinct salary from employee) b 
            where b.salary>a.salary
        )=N
    );
END

4. 解法四:使用子查询&笛卡尔积

CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
    RETURN (
        # Write your MySQL query statement below.
        SELECT DISTINCT e.salary
        FROM employee e
        WHERE (
            SELECT count(DISTINCT salary) 
            FROM employee 
            WHERE salary>e.salary
        ) = N-1
    );
END

当然,可以很容易将上面的代码改为笛卡尔积连接形式,其执行过程实际上一致的,甚至MySQL执行时可能会优化成相同的查询语句。

CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
    RETURN (
        # Write your MySQL query statement below.
        SELECT e1.salary
        FROM employee e1, employee e2 
        WHERE e1.salary <= e2.salary
        GROUP BY e1.salary
        HAVING count(DISTINCT e2.salary) = N
    );
END

5. 解法五:使用自连接

一般来说,能用子查询解决的问题也能用连接解决。具体到本题:

CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
    RETURN (
        # Write your MySQL query statement below.
        SELECT e1.salary
        FROM  employee e1 JOIN employee e2 
        ON e1.salary <= e2.salary
        GROUP BY e1.salary
        HAVING count(DISTINCT e2.salary) = N
    );
END

但需要注意的是在题目测试时候,这种解法户报错,因为是自连接两个数比较。如果求第一个数,其中有一个必为空,会出错 ~

6. 解法六:使用窗口函数

窗口函数用法具体见2.5

CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT
BEGIN
  RETURN (
      # Write your MySQL query statement below.
        SELECT DISTINCT salary
        FROM (
            SELECT salary, dense_rank() over(ORDER BY salary DESC) AS rnk
            FROM employee
        ) tmp
        WHERE rnk = N
  );
END

四、总结

至此,可以总结MySQL查询的一般性思路是:

五、参考链接

上一篇下一篇

猜你喜欢

热点阅读