简体   繁体   中英

oracle sql select maximum top x rows

This is my table structure:

ID   points
-----------
1        20
2        30
3        20
4        30
5        20

I'm going to reward top 3 persons with highest points. If multiple persons have same number of points either all get a reward or no one.

What should a select looks like? I have tried to use ROW_NUMBER() :

select * from (
    select id, row_number() over (order by points desc) as rn from test3
) tab where rn <= 3;

Which returns 3 rows - it's breaking the condition posted above. It should only returns 2 rows in this case (since there are too many 20 values). I have also tried to use RANK() but it's also wrong, since it returns all rows.

In addition to row_number() use cumulative count() and don't reward persons if number exceeds three:

with test3(id, points) as (
    select 1, 20 from dual union all
    select 2, 30 from dual union all
    select 3, 20 from dual union all
    select 4, 30 from dual union all
    select 5, 20 from dual )
select * 
  from (select t.*, row_number() over (order by points desc) rnk, 
               count(1) over (order by points desc) cnt
          from test3 t)
  where rnk <= 3 and cnt <= 3

Use dense_rank() or rank() :

select *
from (select id, dense_rank() over (order by points desc) as rn
      from test3
     ) tab
where rn <= 3;

Use dense_rank() if you want the people with the three highest distinct scores. This is probably what you want.

Note that if the scores are

10, 10, 10, 10, 9, 8

Then all people will be chosen. If you want at least three but not to have extras, then the query is a little trickier:

select t3.*
from test3 t3
where t3.score >= (select max(tt3.point)
                   from (select t3.*, row_number() over (order by points desc) as seqnum
                         from test3 t3
                         order by points desc
                        ) tt3
                   where seqnum <= 3
                  );

In Oracle 12C, this can be simplified to:

select t3.*
from test3 t3
where t3.score >= (select tt3.point
                   from test3 tt3
                   order by points desc
                   offset 2 fetch first 1 row only
                  );
    select * from (
        select id, RANK() over (Partition By Points order by ID) as rn from 
    test3) tab where rn <= 3;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM