A melhor maneira de fazer isso é com funções analíticas, RANK() ou DENSE_RANK()...
SQL> select * from (
2 select empno
3 , sal
4 , rank() over (order by sal desc) as rnk
5 from emp)
6 where rnk <= 5
7 /
EMPNO SAL RNK
---------- ---------- ----------
7839 5000 1
7788 3000 2
7902 3000 2
7566 2975 4
8083 2850 5
7698 2850 5
6 rows selected.
SQL>
DENSE_RANK() comprime as lacunas quando há empate:
SQL> select * from (
2 select empno
3 , sal
4 , dense_rank() over (order by sal desc) as rnk
5 from emp)
6 where rnk <= 5
7 /
EMPNO SAL RNK
---------- ---------- ----------
7839 5000 1
7788 3000 2
7902 3000 2
7566 2975 3
8083 2850 4
7698 2850 4
8070 2500 5
7 rows selected.
SQL>
Qual comportamento você prefere depende de seus requisitos de negócios.
Há também a função analítica ROW_NUMBER() que podemos usar para retornar um número preciso de linhas. No entanto, devemos evitar o uso de soluções baseadas no número da linha, a menos que a lógica de negócios esteja disposta a truncar arbitrariamente o conjunto de resultados em caso de empate. Há uma diferença entre pedir os cinco valores mais altos e os cinco primeiros registros classificados por valores altos
Há também uma solução não analítica usando a pseudocoluna ROWNUM. Isso é desajeitado porque ROWNUM é aplicado antes da cláusula ORDER BY, o que pode levar a resultados inesperados. Raramente há qualquer razão para usar ROWNUM em vez de ROW_NUMBER() ou uma das funções de classificação.