sql rank, dense_rank, row_number 에 대하여

문제, Median Employee Salary

  • https://leetcode.com/problems/median-employee-salary/
  • 한 테이블에 직원, 회사, 임금이 있고, 각 회사마다 가운데 순위의 임금을 받는 레코드를 출력하는 것이 목표이다. 만약 한 회사에 6명이 있다면, 3-4위 2명을 출력한다.
Input: 
Employee table:
+----+---------+--------+
| id | company | salary |
+----+---------+--------+
| 1  | A       | 2341   |
| 2  | A       | 341    |
| 3  | A       | 15     |
| 4  | A       | 15314  |
| 5  | A       | 451    |
| 6  | A       | 513    |
| 7  | B       | 15     |
| 8  | B       | 13     |
| 9  | B       | 1154   |
| 10 | B       | 1345   |
| 11 | B       | 1221   |
| 12 | B       | 234    |
| 13 | C       | 2345   |
| 14 | C       | 2645   |
| 15 | C       | 2645   |
| 16 | C       | 2652   |
| 17 | C       | 65     |
+----+---------+--------+
Output: 
+----+---------+--------+
| id | company | salary |
+----+---------+--------+
| 5  | A       | 451    |
| 6  | A       | 513    |
| 12 | B       | 234    |
| 9  | B       | 1154   |
| 14 | C       | 2645   |
+----+---------+--------+

문제의 해소의 방향

  • 각 회사마다 직원의 숫자에서 2를 나눈 숫자를 구한다. 그리고 회사의 직원이 짝수인지를 판별한다. 만약 직원의 숫자가 6명이면 3이 되며, 7명이면 3과 4를 출력 대상으로 한다.
  • 각 회사마다 임금을 기준으로 등수를 매길 때, row_number() 함수를 사용했다.

쿼리

select 
    tb1.id
    , tb1.company
    , tb1.salary
from (
    select 
        id
        , company
        , salary
        , row_number() over(partition by company order by company asc, salary asc) rk -- 정의한 바에 따라 1부터 순서를 새롭게 매긴다.
    from employee
) tb1
join (
    select 
        company
        , round(count(salary)/2) 'mid'
        , case when count(salary)%2=0 then 'Y'
                else 'N'
                end 'even'
    from employee 
    group by company
) tb2
on tb1.company = tb2.company 
    and (tb1.rk = tb2.mid or tb1.rk = if(tb2.even='Y', tb2.mid+1, 0)) -- (6명 회사에서 3등) or (홀수 회사에서 3+1등)

순서 함수의 사용과 기능

  • row_number() over(partition by company order by company asc, salary asc)

함수의 종류

  • row_number() : 순서에 따라 번호를 매긴다. 1부터 하나씩 더해지는 것을 보장한다.
  • dense_rank() : 등수를 매긴다. 등수는 중복이 가능하다. 등수는 1씩 가산된다 : 1, 2, 2, 2, 3
  • rank() : 등수를 매긴다. 등수는 중복이 가능하다. 하지만 1씩 가산되지 않고 앞의 레코드의 갯수에 따른 등수를 보여준다 : 1, 2, 2, 2, 5

over(order by)

  • order by 는 등수를 매기는 기준이며 이 기준으로 정렬한다. order by 와 같다.

over(partition by)

  • 각 그룹마다 등수를 매기고 싶을 경우 사용한다. group by로 각 그룹마다 등급을 매긴 후 union all 을 한 것과 유사하다.