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 位的雇员。
我正在做我的 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 位的雇员。