排序拿到第N个结果的问题

场景:

例如成绩,需要得到第二名的同学的名字;或者例如薪水,想要查询到薪水第三高的员工的名字;类似种种问题,做个总结。

———————————————————————————

1.Oracle语境

Oracle语境下,这里按照成绩为例先建表。

create table test_data(
       id varchar2(32),
       name varchar2(32),
       score float
)

insert into test_data values('xxx1', '小明', 97);
insert into test_data values('xxx2', '小军', 93);
insert into test_data values('xxx3', '小红', 97);
insert into test_data values('xxx4', '小黑', 95);

查询得到数据如下:

(4行被选择)

 我们想要得到目前小黑排名第二的成绩,考虑到Oracle提供一个rownum的属性,我Order by一下然后取rownum为2不就行了嘛,于是可以得到如下sql:

select name
  from (select t.* from test_data t order by t.score desc)
 where rownum = 2

得到结果:

 (0行被选择)

诶?这是为什么呢?我明明先进行了排序,然后取rownum为2不就应该查出来小黑的数据嘛?

这里我查询了一下Oracle的rownum属性,其实是一个很神奇的属性,它是对于每一条数据进行比对的,比如这里选择了rownum = 2,其实每一行的rownum都是1,所以并不会得到想要的结果。详细的解释见https://blog.csdn.net/haijiege/article/details/78856307

如何解决?——取一个别名

对没错,就是取一个别名,如下:

select name
  from (select name, rownum rn
          from (select t.* from test_data t order by t.score desc))
 where rn = 2

这样查询到的结果为:

(1行被选择)

 诶?我明明想得到小黑的名字,怎么会得到小红的呢?其实这里存在一个相同元素排序的问题,由于前面两个人成绩都是97,所以排在第二位的也是97的小红(id小明的ASCII码稍微比小红的小一些),这种情况该怎么解决呢?

这就需要对于每一行的成绩进行比对了,查询大于目前该行成绩结果的总行数作为目前的排名,可以得到如下SQL:

select name
  from (select name,
               (select count(distinct score)
                  from test_data
                 where score >= s.score) as rn
          from test_data s
         order by score desc)
 where rn = 2

查询结果:

(1行被选择)

 这样就可以得到预期结果啦!

2.Mysql语境

思路大致与上述相同,只不过语句不太一样,这里使用存储过程(函数)来说明

CREATE FUNCTION getNthScore ( n INT ) RETURNS INT 
BEGIN
SET n = n - 1;
RETURN ( 
  SELECT NAME FROM test_data WHERE score = ( SELECT DISTINCT score FROM test_data ORDER BY score DESC LIMIT n, 1 ) 
);
END

按照limit查询排名第几的成绩,然后反向去查找对应的名字

这样输入n=2,查询得到结果为:

(第1条记录,共1条)

———————————————————————————

至此!

猜你喜欢

转载自www.cnblogs.com/doona-jazen/p/12004737.html
今日推荐