문제 설명
다음은 고객의 정보를 담은
MEMBER_PROFILE테이블과 식당의 리뷰 정보를 담은 REST_REVIEW 테이블입니다. MEMBER_PROFILE 테이블은 다음과 같으며 MEMBER_ID, MEMBER_NAME, TLNO, GENDER, DATE_OF_BIRTH는 회원 ID, 회원 이름, 회원 연락처, 성별, 생년월일을 의미합니다.Column name | Type | Nullable |
MEMBER_ID | VARCHAR(100) | FALSE |
MEMBER_NAME | VARCHAR(50) | FALSE |
TLNO | VARCHAR(50) | TRUE |
GENDER | VARCHAR(1) | TRUE |
DATE_OF_BIRTH | DATE | TRUE |
REST_REVIEW 테이블은 다음과 같으며 REVIEW_ID, REST_ID, MEMBER_ID, REVIEW_SCORE, REVIEW_TEXTREVIEW_DATE는 각각 리뷰 ID, 식당 ID, 회원 ID, 점수, 리뷰 텍스트, 리뷰 작성일을 의미합니다.Column name | Type | Nullable |
REVIEW_ID | VARCHAR(10) | FALSE |
REST_ID | VARCHAR(10) | TRUE |
MEMBER_ID | VARCHAR(100) | TRUE |
REVIEW_SCORE | NUMBER | TRUE |
REVIEW_TEXT | VARCHAR(1000) | TRUE |
REVIEW_DATE | DATE | TRUE |
예시
MEMBER_PROFILE 테이블이 다음과 같고MEMBER_ID | MEMBER_NAME | TLNO | GENDER | DATE_OF_BIRTH |
jiho92@naver.com | 이지호 | 01076432111 | W | 1992-02-12 |
jiyoon22@hotmail.com | 김지윤 | 01032324117 | W | 1992-02-22 |
jihoon93@hanmail.net | 김지훈 | 01023258688 | M | 1993-02-23 |
seoyeons@naver.com | 박서연 | 01076482209 | W | 1993-03-16 |
yelin1130@gmail.com | 조예린 | 01017626711 | W | 1990-11-30 |
REST_REVIEW 테이블이 다음과 같을 때REVIEW_ID | REST_ID | MEMBER_ID | REVIEW_SCORE | REVIEW_TEXT | REVIEW_DATE |
R000000065 | 00028 | soobin97@naver.com | 5 | 부찌 국물에서 샤브샤브 맛이나고 깔끔 | 2022-04-12 |
R000000066 | 00039 | yelin1130@gmail.com | 5 | 김치찌개 최곱니다. | 2022-02-12 |
R000000067 | 00028 | yelin1130@gmail.com | 5 | 햄이 많아서 좋아요 | 2022-02-22 |
R000000068 | 00035 | ksyi0316@gmail.com | 5 | 숙성회가 끝내줍니다. | 2022-02-15 |
R000000069 | 00035 | yoonsy95@naver.com | 4 | 비린내가 전혀없어요. | 2022-04-16 |
SQL을 실행하면 다음과 같이 출력되어야 합니다.
MEMBER_NAME | REVIEW_TEXT | REVIEW_DATE |
조예린 | 김치찌개 최곱니다. | 2022-02-12 |
조예린 | 햄이 많아서 좋아요 | 2022-02-22 |
주의사항
REVIEW_DATE의 데이트 포맷이 예시와 동일해야 정답처리 됩니다.코드 - in 을 사용한 방법
SELECT
MP.MEMBER_NAME,
RR.REVIEW_TEXT,
DATE_FORMAT(RR.REVIEW_DATE, '%Y-%m-%d') AS REVIEW_DATE
FROM
REST_REVIEW RR
INNER JOIN
MEMBER_PROFILE MP
ON
RR.MEMBER_ID = MP.MEMBER_ID
WHERE
RR.MEMBER_ID IN (
SELECT
MEMBER_ID
FROM
(
SELECT
MEMBER_ID,
RANK() OVER (ORDER BY COUNT(*) DESC) AS RANK_NUM
FROM
REST_REVIEW
GROUP BY
MEMBER_ID
) REVIEW
WHERE
RANK_NUM = 1
)
ORDER BY
RR.REVIEW_DATE ASC,
RR.REVIEW_TEXT ASC;
코드 - 서브쿼리 & join 을 사법
SELECT
MP.MEMBER_NAME,
RR.REVIEW_TEXT,
DATE_FORMAT(RR.REVIEW_DATE, '%Y-%m-%d') AS REVIEW_DATE
FROM
REST_REVIEW RR
JOIN
MEMBER_PROFILE MP
ON
MP.MEMBER_ID = RR.MEMBER_ID
JOIN
(
SELECT
MEMBER_ID
FROM
REST_REVIEW
GROUP BY
MEMBER_ID
HAVING
COUNT(*) = (
SELECT
MAX(cnt)
FROM
(
SELECT
COUNT(*) AS cnt
FROM
REST_REVIEW
GROUP BY
MEMBER_ID
) t
)
) TOPPER
ON
TOPPER.MEMBER_ID = RR.MEMBER_ID
ORDER BY
RR.REVIEW_DATE ASC,
RR.REVIEW_TEXT ASC;
두 방식의 차이점
구분 | 첫 번째 쿼리 (RANK) | 두 번째 쿼리 (HAVING = MAX) |
핵심 방식 | RANK() OVER (ORDER BY COUNT(*) DESC) 사용 | HAVING COUNT(*) = (SELECT MAX(cnt) …) 사용 |
의존 기능 | 윈도 함수 필요 (MySQL 8.0 이상) | 집계와 서브쿼리만 사용 (MySQL 5.7도 가능) |
계산 구조 | 한 번의 GROUP BY + 윈도 함수 | GROUP BY 두 번 수행 (내부·외부) |
성능 | 상대적으로 효율적 (집계 1회) | 중첩 집계로 약간 비효율적 |
조인 형태 | WHERE … IN (subquery) 세미조인 | 명시적 JOIN (파생 테이블 결합) |
가독성 | 직관적, 짧음 | 더 길고 중첩 많음 |
이식성 | 낮음 (윈도 함수 미지원 DB 불가) | 높음 (모든 주요 DB 지원) |
결과 | 동점자 모두 반환 | 동일 |
추천 상황 | MySQL 8.0 이상 환경 | MySQL 5.7 이하, 호환성 중시 |
요약:
- 최신 MySQL이면 RANK() 쿼리가 빠르고 간결하다.
- 구버전이나 타 DB 호환을 원하면 HAVING = MAX 쿼리가 안전하다.
힌트
WITH CustomerCounts AS (
-- 1. 고객별 주문 건수를 계산하고 순위를 매기는 CTE (Common Table Expression)
SELECT
customer_id,
COUNT(*) AS order_count,
-- 주문 건수에 따라 순위(rank_num)를 매김. 동률은 같은 순위 부여.
RANK() OVER (ORDER BY COUNT(*) DESC) AS rank_num
FROM
Orders
GROUP BY
customer_id
)
SELECT
T1.* -- Orders 테이블의 모든 주문 정보 (T1)
FROM
Orders T1
INNER JOIN
CustomerCounts T2
ON
T1.customer_id = T2.customer_id -- 주문 정보와 순위 정보를 고객 ID로 연결
WHERE
T2.rank_num = 1; -- 순위가 1인 (가장 많은 주문을 한) 고객의 주문만 선택- 주문테이블에서 가장많은 주문을 한 고객의 모든 주문정보
- 동률도 찾아줌
Share article