SQL/LeetCode&HackerRank

[MySQL] Hackerrank - Occupations

oatmeal 2024. 1. 30. 18:44

문제: https://www.hackerrank.com/challenges/occupations/problem

 

한 번 풀었던 문젠데 못 풀었다!! ㅠㅠ 

물론 예전에도 못 풀어서 풀이를 봤지만 이번에도 못 풂 ㅎ ㅠ

SELECT MAX(IF(occupation = 'Doctor', name, NULL)) AS Doctor
     , MAX(IF(occupation = 'Professor', name, NULL))AS Professor
     , MAX(IF(occupation = 'Singer', name, NULL))AS Singer
     , MAX(IF(occupation = 'Actor', name, NULL))AS Actor
FROM (
    SELECT *
         , ROW_NUMBER() OVER (PARTITION BY occupation ORDER BY name) AS rnum
    FROM occupations
    ) sub
GROUP BY rnum

 

로직은 다음과 같다

문제는 알파벳 순으로 정렬된 직업별 컬럼을 만드는 것인데

방법은 로넘버에서 파티션 바이를 직업으로 넣고 오더바이에 네임을 넣으면

직업별로 알파벳 순으로 정렬된 로넘버가 생성된다!

 

단순히 그룹바이를 하지 않는 이유는 알파벳 순으로 하나씩 꺼내와야하기 때문이다

 

이게 무슨 말이냐면, 

로넘버를 생성한 다음 로넘버로 그룹바이를 하게 되면

로넘버별 모든 직업군이 하나씩 모이게 된다 !

 

로넘버 1 그룹은 각 직업군에서 알파벳 순 정렬 첫번째 사람들이 오게 되고

로넘버 2 그룹은 각 직업군에서 알파벳 순 정렬 두번째 사람들이 오게 됨

 

rnum1 (doctor1, prof1, singer1, actor1)

rnum2 (doctor2, prof2, singer2, actor2)

rnum3 (doctor3, prof3, singer3, actor3)

 

요런 식으로!

그리고 로넘버 그룹을 세로로 쌓은 모양이 결괏값의 형태가 된다 => 알파벳 순으로 정렬된 직업별 컬럼

그래서 로넘버 그룹이 결과 테이블의 행넘버라고 생각하면 됨

 

그래서 그룹바이 로넘버를 한 후

select에서 if절을 사용해서 로넘버 그룹에서 직업별로 한명씩 선택해주면 된다

 

if절에서 조건과 일치하는 직업을 가진 사람이면 이름을 가져오고 아니면 NULL을 가져오라는 옵션을 걸어주면 되는데

로넘버 그룹에서 직업별로 1명만 존재해서 가능한 부분이다

 

결과 예시로 

그룹바이 로넘버를 하고 직업 조건을 doctor로 줬을 때 결과는 다음과 같다

 

rnum1 (doctor1, Null, Null, Null)

rnum2 (doctor2, Null, Null, Null)

rnum3 (doctor3, Null, Null, Null)

 

하지만 우리가 원하는 건 로넘버 그룹이 아니라 닥터의 이름임!

if절에 max를 씌워주는 이유가 여기에 있다

 

일단 max(str)의 경우 

소문자 > 대문자 이며

소문자 또는 대문자 내에서는 알파벳 순서로 값이 커진다!

 

예시)

  • 'a'의 유니코드 코드 포인트: 97
  • 'b'의 유니코드 코드 포인트: 98
  • 'A'의 유니코드 코드 포인트: 65
  • 'B'의 유니코드 코드 포인트: 66

문자열 조합의 경우 첫번째 문자를 기준으로 대소가 결정되고 첫번째 문자가 같을 경우 다음 문자를 기준으로 결정됨!

 

문제의 경우,

이름과 Null을 비교하게 될 텐데,

Null은 값이 없음을 나타내는 특별한 상태이며, 크기를 비교할 수 없다

 

따라서 Null이 어떤 알파벳보다 작아서 이름이 선택되는 것이 아니라, 애초에 max 함수에서 고려되지 않음! 

숫자의 경우에도 집계함수를 적용했을 때 Null값은 집계에서 제외되는 상황과 동일하다

 

그래서 맥스를 적용하면 로넘그룹에서 이름을 가진 하나의 값만 선택된다.

 

그리고 로넘버가 뒤로갈수록 특정 직업이 없을 수도 있다

왜냐하면 직업별 사람 수가 다를 수 있기 때문이다 <= 문제에서 모든 직업의 인원 수가 같다는 말이 없음 

조건의 직업과 일치하지 않을 경우 Null을 넣으라는 설정을 해줬기 때문에 

없는 직업에 대해서는 if절의 결과가 모두 Null이게 됨

(Null, Null, Null, Null)

 

이때 Null은 집계되지 않기 때문에, max함수에 아무것도 들어가지 않게 되고

결과는 Null을 반환하게 된다! 

Null 밖에 없어서 Null이 선택된 게 아니라 Max()안에 아무 것도 들어가지 않아서 Null이 반환된 것임!

 

문제를 이해하는 과정은 재밌지만

이해하고 나면 항상 이런 로직을 처음 생각해내는 사람들이 정말 똑똑하고,, 신기하다는 생각이 든다 ㅋㅅㅋ