I hope you can help me with my homework 🙂
We need to build a query that outputs the top N best paid employees.
My version works perfectly fine.
For example the top 3:
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
;
Note that this will output employees that are in the top 3 and have the same salary, too.
1: Mike, 4080
2: Steve, 2800
2: Susan, 2800
2: Jack, 2800
3: Chloe, 1400
But now our teacher does not allow us to use ROWNUM.
I searched far and wide and didn’t find anything useable.
My second solution thanks to Justin Caves’ hint.
First i tried this:
SELECT name, salary, ( rank() OVER ( ORDER BY salary DESC ) ) as myorder
FROM staff
WHERE myorder <= 3
;
The errormessage is: “myorder: invalid identifier”
Thanks to DCookie its now clear:
“[…] Analytics are applied AFTER
the where clause is evaluated, which
is why you get the error that myorder
is an invalid identifier.”
Wrapping a SELECT around solves this:
SELECT *
FROM ( SELECT name, salary, rank() OVER ( ORDER BY salary DESC ) as myorder FROM staff )
WHERE myorder <= 3
;
My teacher strikes again and don’t allow such exotic analytic functions.
3rd solution from @Justin Caves.
“If analytic functions are also
disallowed, the other option I could
imagine– one that you would never,
ever, ever actually write in practice,
would be something like”
SELECT name, salary
FROM staff s1
WHERE (SELECT COUNT(*)
FROM staff s2
WHERE s1.salary < s2.salary) <= 3
Since this is homework, a hint rather than an answer. You’ll want to use analytic functions. ROW_NUMBER, RANK, or DENSE_RANK can work depending on how you want to handle ties.
If analytic functions are also disallowed, the other option I could imagine– one that you would never, ever, ever actually write in practice, would be something like
With regard to performance, I wouldn’t rely on the COST number from the query plan– that’s only an estimate and it is not generally possible to compare the cost between plans for different SQL statements. You’re much better off looking at something like the number of consistent gets the query actually does and considering how the query performance will scale as the number of rows in the table increases. The third option is going to be radically less efficient than the other two simply because it needs to scan the STAFF table twice.
I don’t have your STAFF table, so I’ll use the EMP table from the SCOTT schema
The analytic function solution actually does 7 consistent gets as does the ROWNUM solution
The COUNT(*) solution, however, actually does 99 consistent gets and has to do a full scan of the table twice so it is more than 10 times less efficient. And it will scale much worse as the number of rows in the table increases