Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Oracle Analytic function for min value in grouping

I'm new to working with analytic functions.

DEPT EMP   SALARY
---- ----- ------
  10 MARY  100000
  10 JOHN  200000
  10 SCOTT 300000
  20 BOB   100000
  20 BETTY 200000
  30 ALAN  100000
  30 TOM   200000
  30 JEFF  300000

I want the department and employee with minimum salary.

Results should look like:

DEPT EMP   SALARY
---- ----- ------
  10 MARY  100000
  20 BOB   100000
  30 ALAN  100000

EDIT: Here's the SQL I have (but of course, it doesn't work as it wants staff in the group by clause as well):

SELECT dept, 
  emp,
  MIN(salary) KEEP (DENSE_RANK FIRST ORDER BY salary)
FROM mytable
GROUP BY dept
like image 441
Travis Heseman Avatar asked Oct 07 '09 18:10

Travis Heseman


People also ask

How do you find min and max in Oracle?

The MIN and MAX aggregate functions are used to calculate the minimum and maximum values of a set of data respectively. As aggregate functions they reduce the number of rows, hence the term "aggregate". If the data isn't grouped we turn the 14 rows in the EMP table to a single row with the aggregated values.

What is the purpose of MIN function in Oracle?

MIN returns minimum value of expr . You can use it as an aggregate or analytic function. The following example determines, for each employee, the employees who were hired on or before the same date as the employee.

What is over () in Oracle SQL?

It operates over a moving window (3 rows wide) over the rows, ordered by date. Example #2: calculate a running balance SUM(amt) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) date amt sum_amt ===== ==== ======= 1-Jan 10.0 10.0 2-Jan 11.0 21.0 3-Jan 30.0 51.0 4-Jan 10.0 61.0 5-Jan 14.0 75.0.


2 Answers

I think that the Rank() function is not the way to go with this, for two reasons.

Firstly, it is probably less efficient than a Min()-based method.

The reason for this is that the query has to maintain an ordered list of all salaries per department as it scans the data, and the rank will then be assigned later by re-reading this list. Obviously in the absence of indexes that can be leveraged for this, you cannot assign a rank until the last data item has been read, and maintenance of the list is expensive.

So the performance of the Rank() function is dependent on the total number of elements to be scanned, and if the number is sufficient that the sort spills to disk then performance will collapse.

This is probably more efficient:

select dept,
       emp,
       salary
from
       (
       SELECT dept, 
              emp,
              salary,
              Min(salary) Over (Partition By dept) min_salary
       FROM   mytable
       )
where salary = min_salary
/

This method only requires that the query maintain a single value per department of the minimum value encountered so far. If a new minimum is encountered then the existing value is modified, otherwise the new value is discarded. The total number of elements that have to be held in memory is related to the number of departments, not the number of rows scanned.

It could be that Oracle has a code path to recognise that the Rank does not really need to be computed in this case, but I wouldn't bet on it.

The second reason for disliking Rank() is that it just answers the wrong question. The question is not "Which records have the salary that is the first ranking when the salaries per department are ascending ordered", it is "Which records have the salary that is the minimum per department". That makes a big difference to me, at least.

like image 160
David Aldridge Avatar answered Sep 19 '22 14:09

David Aldridge


I think you were pretty close with your original query. The following would run and do match your test case:

SELECT dept, 
  MIN(emp) KEEP(DENSE_RANK FIRST ORDER BY salary, ROWID) AS emp,
  MIN(salary) KEEP (DENSE_RANK FIRST ORDER BY salary, ROWID) AS salary
FROM mytable
GROUP BY dept

In contrast to the RANK() solutions, this one guarantees at most one row per department. But that hints at a problem: what happens in a department where there are two employees on the lowest salary? The RANK() solutions will return both employees -- more than one row for the department. This answer will pick one arbitrarily and make sure there's only one for the department.

like image 41
William Rose Avatar answered Sep 19 '22 14:09

William Rose