我希望你能帮我完成我的作业:)
我们需要构建一个输出前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:Chloe,1400
但现在我们的老师不允许我们使用ROWNUM
我搜索得很远,没有找到任何可用的东西。
我的第二个解决方案感谢Justin Caves的提示。
首先我试过这个:
SELECT name, salary, ( rank() OVER ( ORDER BY salary DESC ) ) as myorder
FROM staff
WHERE myorder <= 3
;
错误消息是:“myorder:invalid identifier”
感谢DCookie,现在很清楚:
“[...]分析在之后应用 where子句被评估,哪个 这就是你得到错误的原因 是无效的标识符。“
环绕一个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
答案 0 :(得分:10)
因为这是家庭作业,所以提示而不是答案。您将需要使用分析函数。 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次一致性获取
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
答案 1 :(得分:3)
你必须用另一个select包装语句的原因是因为外部select语句是将结果集限制为所需行数的语句。这是一个helpful link on analytics。如果你自己运行内部选择,你会明白为什么你必须这样做。在评估where子句之后应用分析,这就是为什么你得到myorder是无效标识符的错误。
答案 2 :(得分:1)
甲骨文?窗函数怎么样?
select * from
(SELECT s.*, row_number over (order by salary desc ) as rn FROM staff s )
where rn <=3
答案 3 :(得分: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)
答案 4 :(得分:0)
您可以在Oracle 12c中解决此问题
select NAME, SALARY
from STAFF
order by SALARY DESC
FETCH FIRST 3 ROWS ONLY
(FETCH FIRST语法是Oracle 12c的新增功能)