세조목

SQL 예제 정리('그룹별 조건에 맞는 식당 목록 출력하기')(24.05.05) 본문

데이터 분석 공부/SQL

SQL 예제 정리('그룹별 조건에 맞는 식당 목록 출력하기')(24.05.05)

세조목 2024. 5. 5. 21:43

https://school.programmers.co.kr/learn/courses/30/lessons/131124

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

리뷰를 가장 많이 작성한 회원의 이름, 리뷰 텍스트, 리뷰 작성일을 출력하는 것이 이번 문제의 요구사항입니다.

우선 제가 작성한 쿼리 먼저 보여드리겠습니다.

SELECT MP.MEMBER_NAME,
       RR.REVIEW_TEXT,
       DATE_FORMAT(RR.REVIEW_DATE, '%Y-%m-%d') REVIEW_DATE
FROM MEMBER_PROFILE MP RIGHT JOIN REST_REVIEW RR ON MP.MEMBER_ID=RR.MEMBER_ID
WHERE MP.MEMBER_ID IN (
    SELECT B.MEMBER_ID
    FROM
    (
    SELECT A.MEMBER_ID, RANK() OVER(ORDER BY A.CNT_REVIEW DESC) RANKING
    FROM
    (
    SELECT MP.MEMBER_ID,
           COUNT(*) CNT_REVIEW
    FROM MEMBER_PROFILE MP RIGHT JOIN REST_REVIEW RR ON MP.MEMBER_ID=RR.MEMBER_ID
    GROUP BY MP.MEMBER_ID
    ) A
    ) B
    WHERE B.RANKING = 1
)
ORDER BY REVIEW_DATE, RR.REVIEW_TEXT;

뭐가 좀 많이 복잡하긴한데

  1. 총 두 개의 서브 쿼리를 활용하여 리슈 수가 가장 많은 멤버를 필터링해서
  2. 해당 멤버들의 정보들을 본 쿼리에서 출력하게끔 작성된 쿼리입니다.

하나씩 뜯어보겠습니다.

    SELECT A.MEMBER_ID, RANK() OVER(ORDER BY A.CNT_REVIEW DESC) RANKING
    FROM
    (
    SELECT MP.MEMBER_ID,
           COUNT(*) CNT_REVIEW
    FROM MEMBER_PROFILE MP RIGHT JOIN REST_REVIEW RR ON MP.MEMBER_ID=RR.MEMBER_ID
    GROUP BY MP.MEMBER_ID
    ) A

가장 안쪽에 있는 서브쿼리입니다.

여기서는 MEMBER_ID별 리뷰 수를 COUNTING한 후

바깥 쿼리에서 리뷰 수 기준 RANK를 매깁니다.

 

    SELECT B.MEMBER_ID
    FROM
    (
    SELECT A.MEMBER_ID, RANK() OVER(ORDER BY A.CNT_REVIEW DESC) RANKING
    FROM
    (
    SELECT MP.MEMBER_ID,
           COUNT(*) CNT_REVIEW
    FROM MEMBER_PROFILE MP RIGHT JOIN REST_REVIEW RR ON MP.MEMBER_ID=RR.MEMBER_ID
    GROUP BY MP.MEMBER_ID
    ) A
    ) B
    WHERE B.RANKING = 1

 

 

RANK를 매기는 서브쿼리를 다시 한 번 더 서브쿼리로 하는

쿼리문을 작성합니다.

여기서는 단순하게 RANK가 1인 MEMEBER_ID만 뽑아냅니다.

 

SELECT MP.MEMBER_NAME,
       RR.REVIEW_TEXT,
       DATE_FORMAT(RR.REVIEW_DATE, '%Y-%m-%d') REVIEW_DATE
FROM MEMBER_PROFILE MP RIGHT JOIN REST_REVIEW RR ON MP.MEMBER_ID=RR.MEMBER_ID
WHERE MP.MEMBER_ID IN (
    SELECT B.MEMBER_ID
    FROM
    (
    SELECT A.MEMBER_ID, RANK() OVER(ORDER BY A.CNT_REVIEW DESC) RANKING
    FROM
    (
    SELECT MP.MEMBER_ID,
           COUNT(*) CNT_REVIEW
    FROM MEMBER_PROFILE MP RIGHT JOIN REST_REVIEW RR ON MP.MEMBER_ID=RR.MEMBER_ID
    GROUP BY MP.MEMBER_ID
    ) A
    ) B
    WHERE B.RANKING = 1
)
ORDER BY REVIEW_DATE, RR.REVIEW_TEXT;

RANK가 1인 MEMBER_ID만 뽑아내는 쿼리문을

본 쿼리의 WHERE 절에 넣어준 후

SELECT 절에 문제에서 요구한 MEMBER_NAME, REVIEW_TEXT, REVIEW_DATE를 적어준 후,

마지막으로 REVIEW_DATE와 REVIEW_TEXT를 기준으로 정렬해줍니다.

 


이 쿼리의 경우 이전에 작성한 쿼리문을 보지 않고 새롭게 작성한 쿼리문인데요,

예전에 작성한 쿼리문을 확인해보니 훨씬 간단했습니다.

SELECT MP.MEMBER_NAME,
       RR.REVIEW_TEXT,
       DATE_FORMAT(RR.REVIEW_DATE, '%Y-%m-%d') REVIEW_DATE
FROM MEMBER_PROFILE MP JOIN REST_REVIEW RR ON MP.MEMBER_ID = RR.MEMBER_ID
WHERE MP.MEMBER_ID =
(
    SELECT MEMBER_ID
    FROM REST_REVIEW
    GROUP BY MEMBER_ID
    ORDER BY COUNT(*) DESC
    LIMIT 1
)
ORDER BY REVIEW_DATE, RR.REVIEW_TEXT

첫 번째 쿼리와 이 쿼리의 차이점은 뭘까요?

첫 번째 쿼리 결과
두 번째 쿼리 결과

두 번째 쿼리는 조건에 해당하는 MEMBER들 중 한 명의 리뷰들만을 출력하는 쿼리입니다.

 

SELECT MEMBER_ID
FROM REST_REVIEW
GROUP BY MEMBER_ID
ORDER BY COUNT(*) DESC
LIMIT 1

두 번째 쿼리의 서브쿼리를 보시면 '개수'를 기준으로 정렬 후 LIMIT 1을 걸어버립니다.

이렇게 되면 리뷰 수가 동일하더라도 가장 위에 출력되는 MEMBER_ID 하나만 WHERE절에 들어가게 되죠.

 

PROGRAMMERS에서 두 결과값 모두 정답 처리를 하니 어떤 쿼리를 사용해도 상관은 없을 것 같습니다.

어짜피 쿼리 작성 로직 자체는 동일하니까요.