SQL/문제풀이

[HackerRank] Challenges 📌

응엉잉 2024. 5. 9. 16:04

 

https://www.hackerrank.com/challenges/challenges/problem?isFullScreen=true

 

Challenges | HackerRank

Print the total number of challenges created by hackers.

www.hackerrank.com

강의를 듣기 전 내 풀이

 

1. 아이디별로 만든 챌린지 수를 확인할 수 있는 테이블을 만들어줌

SELECT hacker_id,
	COUNT(challenge_id) AS challenges_created
FROM Challenges
GROUP BY hacker_id

 

이 테이블을 이용해서 추후 조건에 사용될 challenges_created의 MAX 값을 구해주는 서브쿼리를 또 작성해야 했기에, WITH문을 사용해서 쿼리 맨 위로 빼버림

WITH sub AS (
    SELECT hacker_id,
        COUNT(challenge_id) AS challenges_created
    FROM Challenges
    GROUP BY hacker_id
)

그리고 MAX 값을 구하는 쿼리도 작성해줌 (추후 사용할 예정)

SELECT MAX(challenges_created) FROM sub

 

2. 1번의 테이블을 이용해서, 만든 챌린지 수별 학생 수를 구해줌

SELECT challenges_created,
	COUNT(hacker_id) AS cnt
FROM sub
GROUP BY challenges_created

 

문제에서 cnt가 2 이상이고, challenges_created가 MAX 값보다 작은 challenges_created 값에 대해서는 제외해주길 원함

 

3. 2번 테이블을 이용해서, 결과에서 제외해야 할 챌린지 수를 구해줌

SELECT challenges_created,
	COUNT(hacker_id) AS cnt
FROM sub
GROUP BY challenges_created
HAVING cnt >= 2
	AND (SELECT MAX(challenges_created) FROM sub) > challenges_created

 

이때 challenges_created 값만 필요하기 때문에 cnt는 제거한 상태로 사용

SELECT challenges_created
FROM sub
GROUP BY challenges_created
HAVING COUNT(hacker_id) >= 2
    AND (SELECT MAX(challenges_created) FROM sub) > challenges_created

 

4. 1번에서 만든 테이블에서 3번에 해당되는 challenges_created 값을 가진 행을 제거해줌

SELECT *
FROM sub
WHERE challenges_created NOT IN (3번쿼리)

 

5. 문제에서 학생들의 이름을 출력해주길 원하기 때문에 INNER JOIN을 이용해줌

SELECT s.hacker_id,
	h.name,
    s.challenges_created
FROM sub s
	INNER JOIN hackers h ON s.hacker_id = h.hacker_id
WHERE challenges_created NOT IN (3번쿼리)

 

6. 문제에서 원하는 정렬 조건을 ORDER BY로 작성해줌

SELECT s.hacker_id,
	h.name,
    s.challenges_created
FROM sub s
	INNER JOIN hackers h ON s.hacker_id = h.hacker_id
WHERE challenges_created NOT IN (3번쿼리)
ORDER BY challenges_created DESC, hacker_id

 

최종적으로는 다음과 같은 쿼리를 작성했다.

WITH sub AS (
    SELECT c.hacker_id,
        COUNT(challenge_id) AS challenges_created
    FROM Challenges c
    GROUP BY c.hacker_id
)

SELECT s.hacker_id,
    h.name,
    s.challenges_created
FROM sub s
    INNER JOIN hackers h ON s.hacker_id = h.hacker_id
WHERE challenges_created NOT IN (
    SELECT challenges_created
    FROM sub
    GROUP BY challenges_created
    HAVING COUNT(hacker_id) >= 2
        AND (SELECT MAX(challenges_created) FROM sub) > challenges_created
)
ORDER BY challenges_created DESC, hacker_id

 

강의를 듣고 정리한 풀이

 

우선 기본 골자 (문제에서 출력하기를 원하는 형태) 를 잡아준다.

SELECT c.hacker_id,
	h.name,
    COUNT(challenge_id) AS challenges_created
FROM challenges c
	INNER JOIN hackers h ON c.hacker_id = h.hacker_id
GROUP BY c.hacker_id, h.name
HAVING -- 1) challenges_created가 MAX값인 경우
	OR -- 2) challenges_created가 중복이 아닌 경우
ORDER BY challenges_created DESC, c.hacker_id

GROUP BY를 통해 연산된 challenges_created에 따라 조건이 다르게 걸리는 문제이므로 HAVING을 사용해줘야 한다.

challenges_created가 MAX값인 경우 동일한 갯수를 만든 사람이 몇명인지와 무관하게 출력

MAX가 아닌 경우 동일한 갯수를 만든 사람이 있다면 그만큼 만든사람 모두 출력 X

 

1) challenges_created가 MAX인 경우

SELECT MAX(challenges_created)
FROM (
    SELECT c.hacker_id,
        h.name,
        COUNT(challenge_id) AS challenges_created
    FROM challenges c
        INNER JOIN hackers h ON c.hacker_id = h.hacker_id
)

 

이걸 HAVING절 조건에 쓰면 다음과 같다.

HAVING challenges_created = ( 1)번쿼리 내용 )

 

2) challenges_created가 중복이 아닌 경우

SELECT challenges_created,
	COUNT(c.hacker_id) AS cnt
FROM (
    SELECT c.hacker_id,
        h.name,
        COUNT(challenge_id) AS challenges_created
    FROM challenges c
        INNER JOIN hackers h ON c.hacker_id = h.hacker_id
)
GROUP BY challenges_created
HAVING cnt = 1

 

이걸 HAVING절 조건으로 쓰기 위해서는 SELECT 절에 challenges_created만 남아야 한다.

SELECT challenges_created
FROM (
    SELECT c.hacker_id,
        h.name,
        COUNT(challenge_id) AS challenges_created
    FROM challenges c
        INNER JOIN hackers h ON c.hacker_id = h.hacker_id
)
GROUP BY challenges_created
HAVING COUNT(c.hacker_id) = 1 -- cnt를 HAVING절 조건으로 빼줌

 

HAVING challenges_created IN ( 2)번쿼리 내용 )

 

이때 challenges_created를 세어주기 위한 쿼리가 계속 반복적으로 사용되고 있음을 알 수 있다.

같은 동작을 계속 실행하는걸 막기 위해 WITH문을 사용해준다.

WITH sub AS (
    SELECT c.hacker_id,
        h.name,
        COUNT(challenge_id) AS challenges_created
    FROM challenges c
        INNER JOIN hackers h ON c.hacker_id = h.hacker_id
    GROUP BY c.hacker_id, h.name
)

 

정리하면 다음과 같다.

WITH sub AS (
    SELECT c.hacker_id,
        h.name,
        COUNT(challenge_id) AS challenges_created
    FROM challenges c
        INNER JOIN hackers h ON c.hacker_id = h.hacker_id
    GROUP BY c.hacker_id, h.name
)

SELECT hacker_id,
    name,
    challenges_created
FROM sub
WHERE challenges_created = (
    SELECT MAX(challenges_created)
    FROM sub)
OR challenges_created IN (
    SELECT challenges_created
    FROM sub
    GROUP BY challenges_created
    HAVING COUNT(hacker_id) = 1)
ORDER BY challenges_created DESC, hacker_id

 

배운점

1. 큰그림을 먼저 파악하기 위해 전체적인 골자에 대한 쿼리를 먼저 작성

2. GROUP BY할 때 SELECT에 연산자를 적지 않고, HAVING절에 연산자를 적어도 됨

3. 조건을 말로 정리한 후 그걸 구현하기 위한 코드를 작성하기