Oracle 选择不带ROWNUM的前N行?
我希望你能帮我做作业: 我们需要构建一个查询,以输出前N名薪酬最高的员工 我的版本非常好用。 例如,前三名:Oracle 选择不带ROWNUM的前N行?,oracle,rownum,Oracle,Rownum,我希望你能帮我做作业: 我们需要构建一个查询,以输出前N名薪酬最高的员工 我的版本非常好用。 例如,前三名: SELECT name, salary FROM staff WHERE salary IN ( SELECT * FROM ( SELECT salary FROM staff ORDER BY salary DESC )
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
;
错误消息为:myorder:无效标识符
多亏了DCookie,它现在变得清晰了:
[…]分析在
计算where子句,其中
这就是为什么你会得到我的订单上的错误
是无效的标识符
环绕选择可以解决以下问题:
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
因为这是家庭作业,是一个提示而不是答案。您需要使用分析函数。行数、列组或密集列组可以根据您希望如何处理领带而工作 如果分析函数也被禁止,我可以想象的另一种选择——你永远不会,永远不会,永远不会在实践中真正写出来,会是这样的
SELECT name, salary
FROM staff s1
WHERE (SELECT COUNT(*)
FROM staff s2
WHERE s1.salary < s2.salary) <= 3
关于性能,我不会依赖于查询计划中的成本数字——这只是一个估计,通常不可能比较不同SQL语句的计划之间的成本。您最好看看查询实际执行的一致get的数量,并考虑查询性能将如何随着表中行数的增加而扩展。第三个选项的效率将大大低于其他两个选项,因为它需要扫描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个一致get,并且必须对表进行两次完整扫描,因此效率要低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
甲骨文?窗口功能呢
select * from
(SELECT s.*, row_number over (order by salary desc ) as rn FROM staff s )
where rn <=3
必须用另一个select语句包装该语句的原因是,外部select语句将结果集限制为所需的行号。这是一个例子。如果您自己运行内部选择,您将看到为什么必须这样做。分析是在where子句求值后应用的,这就是为什么会出现错误,即myorder是无效标识符。当您使用countdistinct时,排名相同的高薪将被视为平局
select NAME, SALARY
from STAFF STAFF1
where 3 >= ( select count(distinct STAFF2.SALARY) RANK
from STAFF STAFF2
where STAFF2.SALARY >= STAFF1.SALARY)
您可以在Oracle 12c中解决此问题
select NAME, SALARY
from STAFF
order by SALARY DESC
FETCH FIRST 3 ROWS ONLY
FETCH FIRST语法是Oracle 12c新增的您没有应用任何窗口函数。好的,好的。解析函数。没关系。我没有尝试这个,因为我是因为@JustinCave的提示而寻找排名的。但现在我有了同样的解决方案。但是为什么我必须在它周围加一个SELECT来使用rn值呢?是的,我查了RANK,现在我找到了第二个解决方案。@Justin我给老师看了第二个解决方案。现在他也不允许这种奇异的分析函数。你有第二个提示给我吗?谢谢@Justin,这个解决方案很出乎意料,我还是不明白。今天我要在家复习。现在我明白了。这不比我的第一个解决方案好吗?成本:解决方案1:8;解决方案2:4;解决方案3:6@Pew-更新了对性能影响的更详细讨论。解释为什么这是答案,对OP也很有用。是的,好的,并为代码道歉。只要想一个解释,任何时候在SELECT语句中使用Top N,它的工作原理取决于ORDER BY子句。在WHERE子句中,括号中的部分按工资降序排列,因此前三名为最高工资。因为工资是分组在一起的,所以语句并不关心它们可能出现多少次。然后我会询问任何有该工资金额的姓名。这对Oracle来说是无效的语法。@russ编辑到您的“发布”而不是评论/
select NAME, SALARY
from STAFF
order by SALARY DESC
FETCH FIRST 3 ROWS ONLY