[SQL/코드카타] 프로그래머스 - 그룹별 조건에 맞는 식당 목록 출력하기

문제

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

 

프로그래머스

SW개발자를 위한 평가, 교육의 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr


✍️ 문제 요약

  • MEMBER_PROFILEREST_REVIEW 테이블에서 리뷰를 가장 많이 작성한 회원의 리뷰들을 조회
  • 회원 이름, 리뷰 텍스트, 리뷰 작성일이 출력되도록 작성
  • 리뷰 작성일 오름차순, 같다면 리뷰 텍스트 기준 오름차순 정렬
  • 예시 테이블 참고

✍️ 내가 작성한 코드

SELECT p.member_name,
       r.review_text,
       r.review_date
FROM member_profile as p
JOIN rest_review as r
  ON p.member_id=r.member_id
  
>>> 두 테이블 JOIN
SELECT member_id,
       COUNT(member_id) AS cnt_member
FROM rest_review
GROUP BY member_id
ORDER BY cnt_member DESC

>>> 리뷰를 가장 많이 작성한 회원들(?) 
>>> 위 쿼리문 실행하니 3개가 최대! 그런데 3명이나? LIMIT 3을 붙여도 되는건가
>>> LIMIT를 붙이는 건 가장 많이 작성한 회원이 3명이라는 것을 알고 있을 때 인건데...
>>> 만약 모른다면 어떻게 하지?
WITH max_review AS (
    SELECT member_id,
           COUNT(member_id) AS cnt_member
    FROM rest_review
    GROUP BY member_id
    ORDER BY cnt_member DESC
    LIMIT 1
)
SELECT p.member_name,
       r.review_text,
       DATE_FORMAT(r.review_date, '%Y-%m-%d') AS review_date
FROM member_profile as p
JOIN rest_review as r ON p.member_id=r.member_id
JOIN max_review as m ON m.member_id=r.member_id
ORDER BY review_date ASC, r.review_text ASC;

>>> 최종쿼리문! 정답이다.
>>> 근데 정말 리뷰 최대3개 인데, 3명이었는데 LIMIT 1을 했는데 왜 이게 맞는거지?
>>> 3명다 출력되어야 하는게 맞지 않을까?
>>> 🚨 LIMIT 1이 가능한 경우는 정말 리뷰를 가장 많이 작성한 사람이 1명일 경우에만 가능
>>> 🚨 따라서, 이 방식은 정답으로 처리됐더라도, 맞지 않는 쿼리문임!!! 🚨

✅ 개선된 방향 코드 설명(feat. JS 튜터님)

▶ CTE 2개 사용 방법 (JOIN도 여러번..ㅎㅎ;)

1. 회원별 리뷰 작성 횟수 집계 구하기

WITH cnt AS (
	SELECT member_id, COUNT(*) AS c
    FROM rest_review
    GROUP BY member_id
),

2. 가장 많은 리뷰 수(최댓값)만 뽑기 : 위에서 뽑은 cnt에서 최댓값만 불러온다.

, mx AS (
  SELECT MAX(c) AS max_c
  FROM cnt
)

3. 최종 쿼리문:

 ㄱ. rest_review r와 cnt c를 member_id로 조인 ➡️ 각 리뷰에 그 회원의 리뷰 개수 c 를 붙이고
 ㄴ.  다시 mx m 와 조인 ➡️ 리뷰 개수가 최댓값인 회원의 리뷰만 남는다!!
 ㄷ. member_profile 와 조인 ➡️ member_name 을 얻음
WITH cnt AS (
  SELECT member_id, COUNT(*) AS c
  FROM rest_review
  GROUP BY member_id
),
mx AS (
  SELECT MAX(c) AS max_c
  FROM cnt
)
SELECT p.member_name,
       r.review_text,
       DATE_FORMAT(r.review_date, '%Y-%m-%d') AS review_date
FROM rest_review r
JOIN cnt        c  ON r.member_id = c.member_id
JOIN mx         m  ON c.c = m.max_c
JOIN member_profile p ON p.member_id = r.member_id
ORDER BY review_date ASC, r.review_text ASC;

💡 질문을 통해 배운 점

1) WITH 절에 만들어 놓은 테이블을 두 번째 WITH 절에서 참조하여 사용 가능

처음에 만든 cnt를 두번째 WITH절에서 테이블로 가져옴

2) JOIN은 정말 많이도 붙일 수가 있구나!!

붙이고 붙이고 붙이고 

FROM rest_review r
JOIN cnt        c  ON r.member_id = c.member_id
JOIN mx         m  ON c.c = m.max_c
JOIN member_profile p ON p.member_id = r.member_id

🔍 또 다른 풀이

▶ CTE 1개 + 윈도우 함수 사용

WITH ranked AS (
  SELECT member_id,
         COUNT(*) AS c,
         DENSE_RANK() OVER (ORDER BY COUNT(*) DESC) AS rk
  FROM rest_review
  GROUP BY member_id
)
SELECT p.member_name, r.review_text, DATE_FORMAT(r.review_date, '%Y-%m-%d') AS review_date
FROM rest_review r
JOIN ranked k ON r.member_id = k.member_id
JOIN member_profile p ON p.member_id = r.member_id
WHERE k.rk = 1
ORDER BY review_date ASC, r.review_text ASC;
[ 설명 ]
ㄱ. DENSE_RANK() OVER ( ) 윈도우 함수로 member_id 별 리뷰작성 횟수 순위를 매김 [*RANK () OVER()도 사용가능]
ㄴ. JOIN으로 연결 후, 순위가 1위 인 것들만 불러온다.

문법 설명 참조

➡️ https://www.notion.so/teamsparta/SQL-_5-2492dc3ef5148026bfd5e5d3bc58ae1b

 

[SQL세션_5회차]강민정 튜터님 | Notion

[강의 녹화본]

www.notion.so

끝.