最近在刷题和工做中总会遇到前n高,第n高的问题,汇总一下以便往后查看。
排名3种场景(以薪水为例):mysql
如表中所示,若是存在第N高的薪水则返回Salary
,若是不存在那么查询应该返回NULL
。sql
ORDER BY
排序加LIMIT N,M
限制(M表示在限制条数以后的offset记录,LIMIT M OFFSET N
),排名第N高意思是LIMIT N-1,1
,可是LIMIT
后面只接受正整数或者单一变量,不能用表达式,因此在函数中须要先SET N = N - 1
GROUP BY
按薪水分组后再ORDER BY
或者DISTINCT
去重。MySQL中的LIMIT用法详解函数
- 基本语法:
SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset
- LIMIT子句用于select中,对输出结果集的行数进行约束,LIMIT接受一个或两个数字参数。参数必须是一个整数常量。offset表示偏移量(指向数据记录的游标),rows表示查询限定返回的最大记录数。当offset参数省略时,默认为0,即LIMIT 3 等同于LIMIT 0,3。
SELECT * FROM table LIMIT 3, 4;
返回第4-7行SELECT * FROM table LIMIT 3;
返回前3行
CREATE FUNCTION getNHighestSalary(N INT) RETURNS INT BEGIN SET N := N-1; IF (N < 0) THEN RETURN NULL; ELSE RETURN ( SELECT DISTINCT salary FROM employee -- GROUP BY salary ORDER BY salary DESC LIMIT N, 1 ); END IF; END
COUNT(1)
累加用来判断是否有第n高的薪水 。考虑会有相等的薪水因此第一重查询用DISTINCT
去重。CREATE FUNCTION getNHighestSalary(N INT) RETURNS INT BEGIN RETURN( SELECT IF(count < N, NULL, min) AS Salary FROM ( SELECT MIN(Salary) AS min, COUNT(1) AS count FROM ( SELECT DISTINCT Salary FROM Employee ORDER BY Salary DESC LIMIT N ) a ) b ); END
N-1
个比其更高的薪水(去重前提下)。N-1
,那么返回该薪水。CREATE FUNCTION getNHighestSalary(N INT) RETURNS INT BEGIN RETURN( SELECT DISTINCT(e.salary) FROM Employee e WHERE ( SELECT COUNT(DISTINCT salary) FROM Employee e1 WHERE e1.salary > e.salary ) = N - 1 ); END
Salary
小于表2的Salary
,以表1的Salary
分组,统计表2的Salary
的去重个数Salary
为空,因此采用LEFT JOIN
,当去重个数等于N-1时就是要输出的排名(也能够用JOIN
,链接条件为<=
,COUNT(DISTINCT e2.Salary) = N)
CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT BEGIN RETURN ( SELECT e1.salary FROM employee e1 LEFT JOIN employee e2 ON e1.salary < e2.salary GROUP BY e1.salary HAVING count(DISTINCT e2.salary) = N - 1 ); END
CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT BEGIN RETURN ( SELECT e1.salary FROM employee e1, employee e2 WHERE e1.salary <= e2.salary GROUP BY e1.salary HAVING count(DISTINCT e2.salary) = N ); END
@s
存储工资,@r
存储排名,先按工资排序,查询时更新变量值,当工资相等时排名不变,不相等则排名加一CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT BEGIN RETURN ( SELECT DISTINCT salary FROM (SELECT salary, @r:=IF(@s=salary, @r, @r+1) AS rnk, @s:= salary FROM employee, (SELECT @r:=0, @s:=NULL)init ORDER BY salary DESC) tmp WHERE rnk = N ); END
ROW_NUMBER():
连续排名,同薪不一样名,3000、2000、2000、1000的排名为1-2-3-4RANK():
不连续排名,同薪同名。3000、2000、2000、1000的排名为1-2-2-4DESENSE_RANK():
连续排名,同薪同名。3000、2000、2000、1000的排名为1-2-2-3OVER()
一块儿使用,OVER()
中的参数一般是PARTITION BY
和 ORDER BY
。例题状况是第三种,因此采用DENSE_RANK()
。CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT BEGIN RETURN ( SELECT DISTINCT salary FROM (SELECT salary, dense_rank() over(ORDER BY salary DESC) AS rnk FROM employee) tmp WHERE rnk = N ); END
如表中所示,若是存在部门前N高的薪水则返回DepartmentId + Salary
,若是不存在那么查询应该返回NULL
。由于只考虑部门和薪水,因此仍是连续排名,同薪同名。
3d
有不超过N-1我的
的工资比查询结果的工资
高。例如求前三高的工资,即有不超过2我的
(查询子条件为<=2或<3)的工资比查询结果的工资
高(有0我的
比第一高工资高;有1我的
比第二高工资高;有2我的
比第三高工资高)SELECT d.Name AS 'Department', e1.Name AS 'Employee', e1.Salary FROM Employee e1 RIGHT JOIN Department d ON e1.DepartmentId = d.Id WHERE 3 > (SELECT COUNT(DISTINCT e2.Salary) FROM Employee e2 WHERE e2.Salary > e1.Salary AND e1.DepartmentId = e2.DepartmentId ) GROUP BY e1.Salary ORDER BY d.`Name`, e1.Salary DESC ;
SELECT d.name as department, e1.name as employee, e1.salary as salary FROM Department d LEFT JOIN Employee e1 on d.id = e1.departmentid LEFT JOIN Employee e2 on e1.departmentid = e2.departmentid and e1.salary<=e2.salary GROUP BY d.name, e1.Salary HAVING count(distinct e2.salary)<4 ORDER BY d.name, e1.salary DESC
@s
存储工资,@r
存储排名,@d
存储部门ID,先按部门和工资排序,查询时更新变量值。@d
相同(@d=DepartmentId
),则表明是在同一部门中进行的排名,当工资相等(@s=Salary
)时排名不变(@r:=@r
),不相等则排名加一(@r:=@r+1
);@d
不相同(@d!=DepartmentId
),则说明@d
需从新赋值(@d=DepartmentId
),排名也要从新开始,即@r:=1
。SELECT d. NAME department, t. NAME employee, salary FROM ( SELECT *, @r :=IF (DepartmentId = @d, IF (Salary = @s, @r, @r + 1), 1) AS rnk, @d := DepartmentId, @s := Salary FROM employee, (SELECT @s := NULL,@d := NULL, @r := 0 ) init ORDER BY DepartmentId, Salary DESC ) t RIGHT JOIN department d ON t.DepartmentId = d.Id WHERE t.rnk <= N OR t.rnk IS NULL GROUP BY d.`Name`, salary ORDER BY DepartmentId, Salary DESC
DENSE_RANK()
,由于求的是部门前N高薪水,因此按部门分组再按薪水排序,那么开窗函数的使用就是:DENSE_RANK() OVER(PARTITION BY departmentid ORDER BY salary DESC)
。SELECT d.`Name`, tmp.`Name`, tmp.Salary FROM( SELECT e1.DepartmentId, e1.`Name`, e1.Salary, DENSE_RANK() OVER(PARTITION BY e1.DepartmentId ORDER BY e1.Salary DESC) rnk FROM employee e1 ) tmp RIGHT JOIN department d ON d.Id = tmp.DepartmentId WHERE rnk <= N OR t.rnk IS NULL GROUP BY d.name, tmp.Salary ;