Difference between highest and fifth highest salaries in SQL using Partition by

238 Views Asked by At

I have the data like this:

CREATE TABLE salaries AS
SELECT * FROM ( VALUES
  ('US'    , 'A', 3935),
  ('US'    , 'B', 7805),
  ('US'    , 'C', 2302),
  ('US'    , 'D', 6772),
  ('US'    , 'E', 3173),
  ('US'    , 'F', 7739),
  ('Japan' , 'G', 3881),
  ('Japan' , 'H', 1158),
  ('Japan' , 'I', 2591),
  ('Japan' , 'J', 3758),
  ('Japan' , 'K', 8710),
  ('Japan' , 'L', 3376),
  ('France', 'M', 5768),
  ('France', 'N', 9466),
  ('France', 'O', 1750),
  ('France', 'P', 1049),
  ('France', 'Q', 3479),
  ('France', 'R', 5305)
) AS t(country,employee,salary);

In order to find difference between maximum salary and fifth highest salary for every country I am trying the following approach:

select max(salary) over (partition by country) - rank(5) over (partition by country) 
from salaries

But it is throwing the following error:

"WITHIN GROUP is required for ordered-set aggregate function"

can anyone suggest any method without using any join?

4

There are 4 best solutions below

7
On BEST ANSWER
select      country
           ,max(salary) - max(case dr when 5 then salary end) as salary_diff

from       (select      country     
                       ,salary
                       ,dense_rank() over (partition by country order by salary desc) as dr

            from        salaries
            ) s

group by    country            
0
On
select country, max_sal - salary
from (
     select country, salary, 
            max(salary) over (partition by country) max_sal,
           case when (row_number() over (partition by country
                                         order by salary desc)) = 5 
                then 1 
           end fifth
from table
) t where fifth is not null;
7
On
  1. Create a WINDOW that defines what you want (partition by country, order by salary descending)
  2. Calculate the max(salary) over that window
  3. Calculate the nth_value from the top (because they're sorted by salary descending)

A special note is that WINDOWS have ranges and rows. They detail how the calculation is performed WITHIN that window. Here we have to UNBOUND the WINDOW to get the nth_value() to work. Normally, it calculates on everything thus seen so nth_value only kicks in if it is has seen that row -- but we can make it see ahead.

Code,

SELECT *
  , max(salary) OVER w1 - nth_value(salary,5) OVER w1 AS max_minus_fifth_highest
FROM foo
WINDOW w1 AS (
  PARTITION BY (country)
  ORDER BY SALARY desc
  ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
)
ORDER BY country;

Sql Demo

1
On

SQL DEMO

with fifth as (
     SELECT country, "salary",
            rank() over (partition by "country" order by "salary" desc)  rnk
     FROM salaries
)
SELECT *
FROM salaries s
JOIN fifth f
  ON s.country = f.country
 AND f.rnk = 5

OUTPUT

enter image description here