ORACLE SQL 在没有子查询或派生表的情况下检索 n 行

ORACLE SQL retrieve n rows without subqueries or derived tables

我正在做我的 SQL 练习,但我卡在了一个练习中。我需要检索薪水最高的两个员工,但我不能使用任何类型的子查询或派生 table。我用这样的子查询来做:

SELECT *
FROM (SELECT * FROM emp ORDER BY sal DESC) new_emp
WHERE ROWNUM < 3;

我也知道这可以使用 WITH 子句来实现,但我想知道是否有其他替代方法。

PS: 我正在使用 Oracle 11。

如果您是 Oracle 12.1 或更高版本,您可以使用行限制子句。在您的情况下,您只需使用子查询加上行限制子句,如下所示:

SELECT * FROM emp 
ORDER BY sal DESC
FETCH FIRST 5 ROWS ONLY;

来源:https://oracle-base.com/articles/12c/row-limiting-clause-for-top-n-queries-12cr1#top-n

这是作弊,但是...您可以定义一个视图而不是使用子查询:

CREATE VIEW sal_ordered_emps AS
  SELECT *
  FROM   emp
  ORDER BY sal DESC;

那么你可以这样做:

SELECT * FROM sal_ordered_emps WHERE ROWNUM < 3;

或者,您可以使用流水线函数...

CREATE OR REPLACE PACKAGE emp_pkg
AS
  TYPE emp_table IS TABLE OF EMP%ROWTYPE;

  FUNCTION get_max_sals(
    n INT
  ) RETURN emp_table PIPELINED;
END;
/

CREATE OR REPLACE PACKAGE BODY emp_pkg
AS
  FUNCTION get_max_sals(
    n INT
  ) RETURN emp_table PIPELINED
  AS
    cur SYS_REFCURSOR;
    in_rec EMP%ROWTYPE;
    i INT := 0;
  BEGIN
    OPEN cur FOR SELECT * FROM EMP ORDER BY SAL DESC;
    LOOP
      i := i + 1;
      FETCH cur INTO in_rec; 
      EXIT WHEN cur%NOTFOUND OR i > n;
      PIPE ROW(in_rec);
    END LOOP;
    CLOSE cur;
    RETURN;
  END;
END;
/

SELECT *
FROM   TABLE( emp_pkg.get_max_sals( 2 ) );

在我看来,这实际上是一种可悲的方法,但您可以使用 join:

select e.col1, e.col2, . . .
from emp e left join
     emp e2
     on e2.salary >= e.salary
group by e.col1, e.col2, . . .
having count(distinct e2.salary) <= 2;

注意:这实际上等同于 dense_rank(),因此如果有联系,您将获得两行以上。解决这个问题很容易(假设每一行都有一个唯一的标识符),但是这个修复使逻辑复杂化并隐藏了基本思想。

一个很好的练习应该有助于准备解决实际问题。所以这个重要的事情不是子查询的使用而是要意识到这两个高薪可以有很多员工

在使用@MT0 时查看解决方法这是查询

CREATE VIEW sal_ordered_emps AS
  SELECT e.*,
  row_number() over (order by sal desc) as RN
  FROM   SCOTT.emp e
  ORDER BY sal DESC;

select e.* from scott.emp e join 
sal_ordered_emps soe on e.sal = soe.sal and rn <= 2
;

解释的结果可以超过 2 条记录

     EMPNO ENAME      JOB              MGR HIREDATE                   SAL       COMM     DEPTNO
---------- ---------- --------- ---------- ------------------- ---------- ---------- ----------
      7788 SCOTT      ANALYST         7566 19.04.1987 00:00:00       3000                    20 
      7839 KING       PRESIDENT            17.11.1981 00:00:00       5000                    10 
      7902 FORD       ANALYST         7566 03.12.1981 00:00:00       3000                    20 

此解决方案不使用子查询,但需要三个步骤:

-- Q1
select max(sal) from scott.emp;
-- Q2
select max(sal) from scott.emp where sal < {result of Q1}; 

select * from scott.emp where sal in ({result of Q1},{result of Q2});

一般情况下,您需要 N+1 个查询才能获得排名前 N 位的雇员。