Programing Language/SQL

해커랭크 Top Competitors (다중 JOIN, 필터링)

Data-SSung 2025. 6. 15. 23:34
반응형

https://www.hackerrank.com/challenges/full-score/problem

 

Top Competitors | HackerRank

Query a list of top-scoring hackers.

www.hackerrank.com


문제 정의 (영어 -> 한국어 번역 후 문제 파악하기)

줄리아가 방금 코딩 경진대회를 마쳤는데, 리더보드를 만드는 데 여러분의 도움이 필요합니다! 두 개 이상의 과제 에서 만점을 받은 해커의 hacker_id  이름을 출력하는 쿼리를 작성하세요. 해커가 만점을 받은 과제의 총 개수를 기준으로 내림차순으로 정렬하세요. 두 명 이상의 해커가 같은 개수의 과제에서 만점을 받은 경우, hacker_id를 기준으로 오름차순으로 정렬하세요 .

 

 

1차 풀기

  • 풀기 전략
    • 단계별 cte
      • 해커 id, 이름
      • 조건
        • 2개 이상 과제에서 만점 받은 해커
        • 정렬 기준
          • 만점 갯수 내림차순
          • 해커 id 오름차순
    • 피드백
      • 오타 조심
      • 만약 groupby를 하나 줄여서 연산 처리 속도를 빠르게 하고 싶다면, 윈도우 함수 사용할 수는 있지만...비추
        , max(score) over(partition by hacker_id, challeng_id order by score desc) max_score
        , row_number() over(partition by hacker_id, challeng_id order by score desc) rn
/*
-- 해커 id, 이름
-- 조건
	-- 2개 이상 과제에서 만점 받은 해커
    -- 정렬 기준
		-- 만점 갯수 내림차순
		-- 해커 id 오름차순

-- 테이블 정리
	-- Hackers: 해커, 이름
    -- Difficulty : 어려움, 만점 점수
    -- Challenges : 챌린지 id, 해커(만든) id, 어려움
    -- Submissions : 제출 id, 해커(제출) id, 챌린지 id, 점수   

-- join 관계 정리
select s.hacker_id
	, h.name
    , 
from submissions s
inner join challenges c -- 어려움 붙이기
	on s.challenge_id = c.challenge_id
inner join difficulty d -- 만점 기준
	on c.difficulty_level = d.difficlty_level
inner join hackers h -- 해커 이름 붙이기
	on s.hacker_id = h.hacker_id
*/

-- cte 작성
-- 1. 해커별 최고 점수 구하기
with tmp as (
    select hacker_id, challenge_id
        , max(score) max_score
    from submissions
    group by hacker_id, challenge_id
)
-- 2. 챌린지별 만점 여부 붙여서 해커별로 만점 과제 count
, tmp2 as (
    select s.hacker_id, h.name
        , count(distinct s.challenge_id) cnt_full_score 
    from tmp s
    inner join challenges c -- 어려움 붙이기
        on s.challenge_id = c.challenge_id
    inner join difficulty d -- 만점 기준
        on c.difficulty_level = d.difficulty_level
            and s.max_score = d.score -- 과제별 만점자 필터링
    inner join hackers h -- 해커 이름 붙이기
        on s.hacker_id = h.hacker_id
    group by s.hacker_id, h.name
)
select hacker_id, name
from tmp2
where cnt_full_score >= 2
order by cnt_full_score desc
	, hacker_id asc
;

 

최적화

  • 가독성을 위해서 해커별 만점 과제 정보 만든 후, hacker별 2개이상 만점 수 필터링 과정을 나누기
-- ⚡ 방법 1: CTE 최적화 (권장 - 가독성 + 성능)
WITH hacker_max_scores AS (
    -- 1단계: 해커별 챌린지별 최고점만 (중복 제거)
    SELECT hacker_id, challenge_id, MAX(score) AS max_score
    FROM submissions
    GROUP BY hacker_id, challenge_id
),
full_score_challenges AS (
    -- 2단계: 만점받은 챌린지만 필터링
    SELECT h.hacker_id, h.challenge_id
    FROM hacker_max_scores h
    INNER JOIN challenges c ON h.challenge_id = c.challenge_id
    INNER JOIN difficulty d ON c.difficulty_level = d.difficulty_level
                              AND h.max_score = d.score
)
-- 3단계: 최종 집계 및 필터링
SELECT f.hacker_id, 
       h.name,
       COUNT(*) AS cnt_full_score
FROM full_score_challenges f
INNER JOIN hackers h ON f.hacker_id = h.hacker_id
GROUP BY f.hacker_id, h.name
HAVING COUNT(*) >= 2
ORDER BY cnt_full_score DESC, f.hacker_id ASC;

 

 

 

 


📊 이 문제에서 윈도우 함수 분석

❌ 윈도우 함수가 부적합한 이유

1️⃣ GROUP BY 최적화 효과 없음

 
sql
-- 현재 문제의 GROUP BY 구조
GROUP BY hacker_id, challenge_id  -- 1차
GROUP BY hacker_id, name          -- 2차

윈도우 함수 핵심 목적인 "GROUP BY 줄이기"가 이 문제에선 적용 안 됨

2️⃣ 불필요한 복잡성 증가

 
sql
-- ❌ 윈도우 함수 방식 (복잡)
MAX(score) OVER(PARTITION BY ...), ROW_NUMBER() OVER(...), 중복 제거

-- ✅ 기본 GROUP BY (간단)
MAX(score) GROUP BY hacker_id, challenge_id

3️⃣ 성능상 이점 없음

  • 윈도우 함수: 전체 스캔 + 윈도우 계산 + 필터링
  • GROUP BY: 인덱스 활용 + 직접 집계

🎯 언제 윈도우 함수를 써야 하나?

✅ 윈도우 함수가 유리한 경우

예시 1: Contest Leaderboard 타입

 
sql
-- 중첩 GROUP BY 제거 효과
GROUP BY hacker_id, challenge_id → MAX(score)  -- 1차
GROUP BY hacker_id → SUM(score)               -- 2차
↓
윈도우 함수로 한 번에 처리

예시 2: 순위 + 원본 데이터

 
sql
-- 각 해커의 순위 + 원본 제출 정보
SELECT hacker_id, submission_id, score,
       RANK() OVER(ORDER BY score DESC)

예시 3: 이전/다음 값 참조

 
sql
-- 이전 제출과 비교
SELECT hacker_id, score,
       LAG(score) OVER(PARTITION BY hacker_id ORDER BY date)

📈 Top Competitors 특성 분석

이 문제의 구조:

 
1. submissions → 해커별 챌린지별 최고점 (GROUP BY 필수)
2. difficulty와 JOIN → 만점 여부 확인 (조건부 필터링)  
3. 해커별 만점 개수 집계 (GROUP BY 필수)

💡 각 단계가 독립적이고 GROUP BY가 최적 → 윈도우 함수 불필요


🏆 결론

Top Competitors에서는:

  • 윈도우 함수: 오버엔지니어링
  • CTE + GROUP BY: 최적 솔루션
  • 단순 명확함: 코테에서 중요

윈도우 함수 vs GROUP BY 판단 기준:

 
중첩된 GROUP BY 있음? → 윈도우 함수 고려
순위/누적값 필요? → 윈도우 함수 필수
단순 집계만? → GROUP BY가 최적

 

반응형