1. 내 답변
질문의 "부서별로 급여 상위 3위 안에 드는 금액을 받는 직원을 모두" + "동점자는 전원 포함"은 "부서별 상위 3개 급여 값에 해당하는 직원 전원"을 뜻하므로, 정답은 DENSE_RANK() <= 3이고 RANK() <= 3은 상위 급여에 3명 이상이 동점일 때 그 아래 금액을 받는 직원을 잘못 탈락시킨다. 따라서 검토 대상 답변의 DENSE_RANK 쿼리 자체는 맞지만, "두 함수 모두 이 요구사항에 효과적"이라는 결론과 함수별 설명 매핑은 틀렸다.
2. 주장별 검토
DENSE_RANK 쿼리 (정답 부분) — holds.
DENSE_RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) + WHERE salary_rank <= 3은 요구사항을 정확히 만족한다. 구조 설명도 정확하다.
"두 함수 모두 동일하게 작동" — wrong.
"대부분의 경우, 급여가 같은 사람을 모두 포함해야 한다는 요구사항에는 RANK()와 DENSE_RANK() 모두 동일하게 작동합니다."
반례(한 부서 급여 100, 100, 100, 90, 80):
| salary |
RANK |
DENSE_RANK |
| 100 |
1 |
1 |
| 100 |
1 |
1 |
| 100 |
1 |
1 |
| 90 |
4 |
2 |
| 80 |
5 |
3 |
상위 3개 급여 값은 {100, 90, 80}이므로 5명 전원이 포함되어야 하는데, RANK() <= 3은 100을 받는 3명만 남기고 90·80을 탈락시킨다. 즉 두 함수는 동점이 있을 때 바로 그 지점에서 결과가 갈린다.
"두 함수 모두 효과적"이라는 결론 — wrong.
"귀하의 요구사항인 "급여가 같은 사람이 여러 명이면 모두 포함해야 해요"라는 조건을 만족시키기 위해서는 두 함수 모두 효과적입니다."
요구사항을 만족하는 것은 DENSE_RANK()뿐이다.
"어떤 함수를 선택해야 할까요?" 항목의 함수 매핑 — wrong(뒤바뀜).
"RANK(): 만약 "급여 상위 3개 값"을 기준으로 하고 싶다면 RANK()를 사용하세요."
"상위 3개 값(금액)"은 DENSE_RANK()의 의미다. RANK()는 "상위 3개 순위 자리"에 가깝고, 동점이 상위에 몰리면 3개 값보다 적게 반환한다. 같은 이유로 두 번째 항목의 "상위 3개 순위 → DENSE_RANK" 대응도 서술이 뒤집혀 있다(두 항목에 든 순위 예시 1,2,3,3,5 / 1,2,3,3,4 자체는 각각 RANK·DENSE_RANK의 정의로 맞다).
"DENSE_RANK가 좀 더 직관적일 수 있습니다" — wrong(문제를 흐림).
"일반적으로 이 경우 DENSE_RANK()가 좀 더 직관적일 수 있습니다."
취향/직관의 문제가 아니라, RANK는 요구사항을 위반한다. 선택 기준을 "직관"이라고 표현하면 독자가 RANK를 써도 된다고 오해한다.
서두의 "RANK() 또는 DENSE_RANK()" 표현 — wrong. "또는"이 둘 다 가능하다는 인상을 준다.
참인 부분: RANK()가 동점 시 다음 순위를 건너뛴다는 정의, "동점이 3위 자리에 있으면 전원 포함된다"는 문장("같은 급여를 받는 여러 명의 직원이 3위 안에 포함될 수 있습니다" — 사실이나 일반적 보장으로 읽히면 오해 소지), DENSE_RANK()가 순위를 건너뛰지 않는다는 설명, 두 쿼리가 함수명만 다르다는 설명은 모두 정확하다.
3. 빠뜨린 내용
- 요구사항 ↔ 함수 대응의 명시: "급여 상위 3위 안에 드는 금액" = "상위 3개 서로 다른 급여 값" →
DENSE_RANK. 이 한 줄이 답변의 핵심인데 빠져 있다.
- 왜 RANK가 부족한지의 조건: 상위 급여에 3명 이상 동점자가 있을 때(즉 3번째 값보다 위쪽에서 순위가 벌어질 때)만 결과가 달라진다. 두 함수가 같은 결과를 내는 경우(예: 100, 90, 80, 80, 70)와 다른 경우를 나눠 주면 오해가 없다.
ROW_NUMBER() 금지 언급: 동점자 처리를 위해 흔히 ROW_NUMBER()를 쓰면 임의로 한 명만 남아 요구사항 위반이라는 경고가 있으면 좋다.
- SQL Server
TOP 3 WITH TIES 주의: 이 옵션도 마지막 행과의 동점만 포함하는 RANK식 의미라, 위 반례에서 90·80을 놓친다. 다른 방식을 제안할 때 필요한 주의점이다.
- 윈도우 함수 미지원 환경 대안: MySQL 5.7 등에서는 상관 서브쿼리(
WHERE salary >= (SELECT ... ORDER BY salary DESC LIMIT 1 OFFSET 2) 형태의 3번째 값)나 조인으로 대체해야 한다.
- 사소한 것:
salary가 NULL인 경우(정렬에서 전부 최하위)와 (department_id, salary) 인덱스 성능 언급. 부서 인원이 3명 미만이면 자동으로 전원 포함된다는 점도 굳이 필요하진 않다.
4. 수정된 답변
나머지 부분(쿼리 구조, DENSE_RANK 설명, 두 쿼리 비교)은 그대로 두고, 잘못된 결론과 매핑만 다음과 같이 고친다.
1. DENSE_RANK() 함수를 사용한 방법 (권장)
요구사항은 "부서별로 급여가 상위 3개 값에 속하는 직원을 전원 포함"이므로, 동점자에게 같은 순위를 주고 순위를 건너뛰지 않는 DENSE_RANK()를 사용해야 한다.
WITH RankedEmployees AS (
SELECT id, name, department_id, salary,
DENSE_RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS salary_rank
FROM employees
)
SELECT id, name, department_id, salary
FROM RankedEmployees
WHERE salary_rank <= 3;
2. RANK()는 이 요구사항에 부적합
RANK()는 동점 뒤의 순위를 건너뛰므로, 상위 급여에 동점자가 3명 이상이면 상위 3개 금액에 속하는데도 탈락하는 직원이 생긴다. 예: 한 부서 급여가 100, 100, 100, 90, 80이면 RANK()는 1, 1, 1, 4, 5를 부여해 90·80을 제외하지만, 상위 3개 금액은 {100, 90, 80}이므로 5명 모두 포함되어야 한다(DENSE_RANK()는 1, 1, 1, 2, 3을 부여).
정리
- "동점자는 전원 포함"만이 아니라 "상위 3개 금액" 기준이므로
DENSE_RANK() <= 3이 정답이다.
- 동점자가 3위 자리(또는 상위 아무 자리)에 있어도
DENSE_RANK()는 모두 포함한다.
ROW_NUMBER()나 SQL Server의 TOP 3 WITH TIES는 이 요구사항을 만족하지 못하므로 사용하지 않는다.