@ddukbbok_kang

하고 싶은 건 일단 해봅니다.

SQL/HackerRank

[해커랭크 medium] Occupations(변수 선언, partition by)(MySQL)

강떡볶 2022. 3. 21. 23:15

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

 

Occupations | HackerRank

Pivot the Occupation column so the Name of each person in OCCUPATIONS is displayed underneath their respective Occupation.

www.hackerrank.com

 

 

set @d=0, @p=0, @s=0, @a=0;

select min(doctor), min(professor), min(singer), min(actor)
from(select 
       case when occupation="Doctor" then name end as Doctor,
       case when occupation="Professor" then name end as Professor,
       case when occupation="Singer" then name end as Singer,
       case when occupation="Actor" then name end Actor,
       
       case 
       when occupation="Doctor" then (@d:=@d+1)
       when occupation="professor" then (@p:=@p+1)
       when occupation="singer" then (@s:=@s+1)
       when occupation="actor" then (@a:=@a+1)
       end as rn
from occupations
order by name) sub
group by rn

 

이 문제는 공부하고 나면 아하!! 하고 손뼉을 치는 문제다.

일단 내가 헤맸던 이유는... case를 쓰는 건 알겠고 뭔가 partition by를 써야할 거 같은데... 감이 안잡혔다.

일단 위의 코드는 partition by를 안쓰고 변수 선언을 이용한 코드이다.

 

select 
       case when occupation="Doctor" then name end as Doctor,
       case when occupation="Professor" then name end as Professor,
       case when occupation="Singer" then name end as Singer,
       case when occupation="Actor" then name end Actor,
       
       case 
       when occupation="Doctor" then (@d:=@d+1)
       when occupation="professor" then (@p:=@p+1)
       when occupation="singer" then (@s:=@s+1)
       when occupation="actor" then (@a:=@a+1)
       end as rn
from occupations
order by name

안에 있는 서브쿼리를 먼저 보면,

직업이 'Doctor'인 사람들의 이름은 'Doctor' 열 안에,

직업이 'Professor'인 사람들의 이름은 'Professor 열 안에,

직업이 'Singer'인 사람들의 이름은 'Singer' 열 안에,

직업이 'Actor'인 사람들의 이름은 'Actor' 열 안에 넣어준다.

 

그리고 각 직업별로 이름이 나올 때마다 rn을 업데이트해준다.

이렇게 말이다! (행 이름은 Doctor, Professor, Singer, Actor, rn 순이다.)

예를 들면, 네 번째 줄의 Britney는 Professor 열에서 Ashely 다음으로 2번째로 등장했기 때문에 rn은 2가 된다.

이렇게 rn을 출력한 후, 이름순으로 정렬을 해준다.

그리고 rn으로 group by를 해주고 select에 각 열의 최솟값(min)을 넣어주면 null이 밑으로 가고 이름이 올라오게 된다(min 대신 max를 써도 된다.)

이미 서브쿼리에서 이름순으로 정렬을 해줬기 때문에 알파벳 순으로 이름이 정렬이 되고, 그 밑에 null로 채워지게 된다.

이렇게 말이다!

 

 

이 방법 외에 partition by를 쓴 다른 사람의 풀이도 찾아봤다.

SELECT 
    MIN(CASE WHEN OCCUPATION = 'DOCTOR' THEN NAME ELSE NULL END) ,
    MIN(CASE WHEN OCCUPATION = 'PROFESSOR' THEN NAME ELSE NULL END) ,
    MIN(CASE WHEN OCCUPATION = 'SINGER' THEN NAME ELSE NULL END) ,
    MIN(CASE WHEN OCCUPATION = 'ACTOR' THEN NAME ELSE NULL END)
FROM (SELECT OCCUPATION, NAME, ROW_NUMBER() OVER (PARTITION BY OCCUPATION ORDER BY NAME) rn FROM OCCUPATIONS) as tmp
GROUP BY tmp.rn

출처 : https://jogrammer.tistory.com/248

 

역시 서브쿼리를 먼저 살펴보면,

SELECT OCCUPATION, NAME, ROW_NUMBER() OVER (PARTITION BY OCCUPATION ORDER BY NAME)
FROM OCCUPATIONS

이런식으로 나온다. partition by occupation이라고 했으니 직업별로 row number가 매겨지는 것이다.

그리고 첫 번째 방법과 마찬가지로 row number(rn)를 기준으로 group by를 해준 후 각 rn별 이름의 최솟값을 구해주면 된다.

 

이 문제는 내가 어려워하는 것들을 다시 짚을 수 있었던 좋은 문제인 것 같다!