5

我希望你能帮我做作业:)

我们需要构建一个查询,输出前 N 个薪酬最高的员工。

我的版本运行良好。
比如前三名:

SELECT name, salary
FROM staff
WHERE salary IN ( SELECT * 
                  FROM ( SELECT salary
                         FROM staff 
                         ORDER BY salary DESC ) 
                  WHERE ROWNUM <= 3 )
ORDER BY salary DESC
;

请注意,这将输出前 3 名且薪水相同的员工。

1:迈克,4080
2:史蒂夫,2800
2:苏珊,2800
2:杰克,2800
3:克洛伊,1400


但是现在我们的老师不允许我们使用ROWNUM
我四处寻找,没有发现任何有用的东西。


感谢 Justin Caves 的提示,我的第二个解决方案。

首先我尝试了这个:

SELECT name, salary, ( rank() OVER ( ORDER BY salary DESC ) ) as myorder
FROM staff
WHERE myorder <= 3
;

错误消息是:“myorder:无效标识符”

多亏了 DCookie,它现在很清楚:

“[...] 在评估 where 子句之后应用分析,这就是为什么您会收到 myorder 是无效标识符的错误。”

包装一个 SELECT 解决了这个问题:

SELECT *
FROM ( SELECT name, salary, rank() OVER ( ORDER BY salary DESC ) as myorder FROM staff )
WHERE myorder <= 3
;

我的老师再次罢工,不允许这种奇异的分析功能。

@Justin Caves 的第三个解决方案。

“如果分析函数也被禁止,我可以想象的另一种选择——你永远不会在实践中真正写出的选择,会是这样的”

SELECT name, salary
  FROM staff s1
 WHERE (SELECT COUNT(*)
          FROM staff s2
         WHERE s1.salary < s2.salary) <= 3
4

5 回答 5

11

由于这是家庭作业,因此是提示而不是答案。您将需要使用分析函数。ROW_NUMBER、RANK 或 DENSE_RANK 可以根据您要如何处理关系来工作。

如果分析函数也被禁止,我可以想象的另一种选择——你永远不会在实践中真正写出的选择,就像

SELECT name, salary
  FROM staff s1
 WHERE (SELECT COUNT(*)
          FROM staff s2
         WHERE s1.salary < s2.salary) <= 3

关于性能,我不会依赖查询计划中的 COST 数字——这只是一个估计值,通常不可能比较不同 SQL 语句的计划之间的成本。您最好查看查询实际执行的一致获取的数量,并考虑查询性能将如何随着表中行数的增加而扩展。第三个选项的效率将大大低于其他两个选项,因为它需要扫描 STAFF 表两次。

我没有你的 STAFF 表,所以我将使用 SCOTT 模式中的 EMP 表

解析函数解和 ROWNUM 解一样实际上做了 7 次一致的 get

Wrote file afiedt.buf

  1  select ename, sal
  2    from( select ename,
  3                 sal,
  4                 rank() over (order by sal) rnk
  5            from emp )
  6*  where rnk <= 3
SQL> /

ENAME             SAL
---------- ----------
smith             800
SM0               950
ADAMS            1110


Execution Plan
----------------------------------------------------------
Plan hash value: 3291446077

--------------------------------------------------------------------------------
-
| Id  | Operation                | Name | Rows  | Bytes | Cost (%CPU)| Time
|
--------------------------------------------------------------------------------
-
|   0 | SELECT STATEMENT         |      |    14 |   672 |     4  (25)| 00:00:01
|*  1 |  VIEW                    |      |    14 |   672 |     4  (25)| 00:00:01
|*  2 |   WINDOW SORT PUSHED RANK|      |    14 |   140 |     4  (25)| 00:00:01
|   3 |    TABLE ACCESS FULL     | EMP  |    14 |   140 |     3   (0)| 00:00:01
--------------------------------------------------------------------------------
-

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("RNK"<=3)
   2 - filter(RANK() OVER ( ORDER BY "SAL")<=3)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          7  consistent gets
          0  physical reads
          0  redo size
        668  bytes sent via SQL*Net to client
        524  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
          3  rows processed

SQL> select ename, sal
  2    from( select ename, sal
  3            from emp
  4           order by sal )
  5   where rownum <= 3;

ENAME             SAL
---------- ----------
smith             800
SM0               950
ADAMS            1110


Execution Plan
----------------------------------------------------------
Plan hash value: 1744961472

--------------------------------------------------------------------------------
| Id  | Operation               | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |      |     3 |   105 |     4  (25)| 00:00:01 |
|*  1 |  COUNT STOPKEY          |      |       |       |            |          |
|   2 |   VIEW                  |      |    14 |   490 |     4  (25)| 00:00:01 |
|*  3 |    SORT ORDER BY STOPKEY|      |    14 |   140 |     4  (25)| 00:00:01 |
|   4 |     TABLE ACCESS FULL   | EMP  |    14 |   140 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter(ROWNUM<=3)
   3 - filter(ROWNUM<=3)


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          7  consistent gets
          0  physical reads
          0  redo size
        668  bytes sent via SQL*Net to client
        524  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
          3  rows processed

然而,COUNT(*) 解决方案实际上执行了 99 次一致的获取,并且必须对表进行两次完整扫描,因此效率降低了 10 倍以上。随着表中行数的增加,它的扩展性会更差

SQL> select ename, sal
  2    from emp e1
  3   where (select count(*) from emp e2 where e1.sal < e2.sal) <= 3;

ENAME             SAL
---------- ----------
JONES            2975
SCOTT            3000
KING             5000
FORD             3000
FOO


Execution Plan
----------------------------------------------------------
Plan hash value: 2649664444

----------------------------------------------------------------------------
| Id  | Operation           | Name | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |      |    14 |   140 |    24   (0)| 00:00:01 |
|*  1 |  FILTER             |      |       |       |            |          |
|   2 |   TABLE ACCESS FULL | EMP  |    14 |   140 |     3   (0)| 00:00:01 |
|   3 |   SORT AGGREGATE    |      |     1 |     4 |            |          |
|*  4 |    TABLE ACCESS FULL| EMP  |     1 |     4 |     3   (0)| 00:00:01 |
----------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter( (SELECT COUNT(*) FROM "EMP" "E2" WHERE
              "E2"."SAL">:B1)<=3)
   4 - filter("E2"."SAL">:B1)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         99  consistent gets
          0  physical reads
          0  redo size
        691  bytes sent via SQL*Net to client
        524  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          5  rows processed
于 2011-04-12T14:17:50.153 回答
3

必须用另一个 select 包装语句的原因是外部 select 语句将结果集限制为所需的行号。这是有关分析的有用链接。如果您自己运行内部选择,您会明白为什么必须这样做。在评估 where 子句之后应用分析,这就是为什么您会收到 myorder 是无效标识符的错误。

于 2011-04-12T16:06:47.247 回答
1

甲骨文?窗口函数呢?

select * from 
(SELECT s.*, row_number over (order by salary desc ) as rn FROM staff s )
where rn <=3
于 2011-04-12T14:18:39.513 回答
0

当您使用 时count(distinct <exp>),同等排名的最高工资将被视为并列排名。

select NAME, SALARY
from  STAFF STAFF1
where 3 >= ( select count(distinct STAFF2.SALARY) RANK
               from STAFF STAFF2
              where STAFF2.SALARY >= STAFF1.SALARY)
于 2014-02-16T13:11:22.270 回答
0

你可以在 Oracle 12c 中解决这个问题

select NAME, SALARY
from  STAFF
order by SALARY DESC
FETCH FIRST 3 ROWS ONLY

(FETCH FIRST 语法是 Oracle 12c 的新语法)

于 2014-04-25T17:54:21.117 回答