SQL, HackerRank, Challenges 풀이

1. 문제

💡 문제 링크
https://www.hackerrank.com/challenges/challenges/problem?isFullScreen=true&h_r=next-challenge&h_v=zen

1.1 테이블, 필드 정보

Hackers

  • hacker_id : (interger)
  • name : (string)

Challenges

  • challenge_id : (integer)
  • hacker_id : (integer) challenges create한 학생의 id

1.2 문제 요약

  • hacker_id, name, total = (총 challenges 개수) 출력
  • challenges 테이블은 hacker 테이블의 부분집합
  • total 내림차순, hacker_id 오름차순 정렬
  • total 값이 같은 학생들의 record는 제외. (단, total값이 최대인 경우는 포함)
    - 다시 말해서, total 값이 최대 or total 값이 유일한 학생인 경우만 출력

2. 풀이 (1)

-- (1) total 값이 최대인 학생
SELECT H.HACKER_ID, H.NAME, B.TOTAL
FROM (
    SELECT A.TOTAL, COUNT(A.TOTAL) TOTAL_COUNT
    FROM (
        SELECT HACKER_ID, COUNT(CHALLENGE_ID) TOTAL 
        FROM CHALLENGES
        GROUP BY 1
        ) A
    GROUP BY A.TOTAL
    ORDER BY 1 DESC
    LIMIT 1) B
JOIN (
    SELECT HACKER_ID, COUNT(CHALLENGE_ID) TOTAL
    FROM CHALLENGES
    GROUP BY 1
) C ON B.TOTAL = C.TOTAL
JOIN HACKERS H ON H.HACKER_ID=C.HACKER_ID
ORDER BY 3 DESC, 1;

--- (2) total 값이 유일한 학생
SELECT H.HACKER_ID, H.NAME, B.TOTAL
FROM (
    SELECT A.TOTAL, COUNT(A.TOTAL) TOTAL_COUNT
    FROM (
        SELECT HACKER_ID, COUNT(CHALLENGE_ID) TOTAL 
        FROM CHALLENGES
        GROUP BY 1
        ) A
    GROUP BY A.TOTAL
    HAVING TOTAL_COUNT = 1) B
JOIN (
    SELECT HACKER_ID, COUNT(CHALLENGE_ID) TOTAL
    FROM CHALLENGES
    GROUP BY 1
) C ON B.TOTAL = C.TOTAL
JOIN HACKERS H ON H.HACKER_ID=C.HACKER_ID
ORDER BY 3 DESC, 1;

2.1 무엇이 잘못되었나

  • (1) total값이 최대인 학생을 불러오는 쿼리와 (2) total값이 유일한 경우의 학생을 불러오는 쿼리를 따로 짜고 이어붙였다.
  • (2)에선, total값이 유일한 경우의 total값을 가져와서 이를 key로 inner join 했다.
    이러다 보니 (1)과 (2)를 or 구문으로 이어붙이지 못해 쿼리가 길어졌다. inner join 대신 in 연산을 이용한 풀이가 있어 참고했다.

2. 풀이 (2)

💡 출처 :
https://techblog-history-younghunjo1.tistory.com/157?category=962943

SELECT Hackers.hacker_id, Hackers.name, COUNT(*) AS challenges_created
FROM Hackers
INNER JOIN Challenges ON Hackers.hacker_id = Challenges.hacker_id
GROUP BY Hackers.hacker_id -- hacker id가 primary key역할을 한다면, name은 빼도 될 것 같다.
HAVING challenges_created IN (SELECT sub2.challenges_created
                              FROM (SELECT hacker_id, COUNT(*) AS challenges_created
                                    FROM Challenges
                                    GROUP BY Challenges.hacker_id) sub2
                              GROUP BY sub2.challenges_created
                              HAVING COUNT(*) = 1)
    OR challenges_created = (SELECT MAX(sub1.challenges_created)
                             FROM (SELECT COUNT(*) AS challenges_created
                                   FROM Challenges
                                   GROUP BY Challenges.hacker_id) sub1)
ORDER BY challenges_created DESC, Hackers.hacker_id
  • challenges_created(=학생 별 total 챌린지수)를 만들고 IN 함수로 조건을 만족하는 challenges_created를 가져왔다.
  • 별도로 challenges_created의 빈도를 나타내는 변수를 만들지 않았다. 대신, group by 를 쓰고 having count(*)=1 으로 빈도에 대한 조건문을 걸었다.

좋은 웹페이지 즐겨찾기