데이터 분석/SQL

[day11] 이런 속담이 있다. window 함수만 잘 사용하면 쿼리가 간단해진다는.

경 민 2025. 2. 27. 19:44

Q. 리뷰를 가장 많이 작성한 회원의 리뷰들을 조회


보자마자 작성한 쿼리. 나름 논리적이지만 내가 봐도 당황스럽다.

SELECT M3.MEMBER_NAME,
        R3.REVIEW_TEXT,
        DATE_FORMAT(R3.REVIEW_DATE,'%Y-%m-%d') AS REVIEW_DATE
FROM MEMBER_PROFILE M3 JOIN REST_REVIEW R3
ON M3.MEMBER_ID = R3.MEMBER_ID
WHERE M3.MEMBER_ID IN
(
SELECT M2.MEMBER_ID
FROM MEMBER_PROFILE M2 JOIN REST_REVIEW R2
ON M2.MEMBER_ID = R2.MEMBER_ID
GROUP BY 1
HAVING COUNT(R2.review_id) =
(
SELECT MAX(review_cnt)
FROM 
(
SELECT M.MEMBER_ID,
       COUNT(R.REVIEW_ID) AS review_cnt
FROM MEMBER_PROFILE M JOIN REST_REVIEW R
ON M.MEMBER_ID = R.MEMBER_ID
GROUP BY 1
) a
)
)
ORDER BY 3, 2

" 1. 회원별 리뷰 개수 구하고

2. 그 중 최댓값 구하고

3. 회원별 리뷰 개수가 최댓값과 일치하는 애들 데려와 "

 

맞긴 맞는데

일을 왜이렇게 어렵게 시키세요 ;;;;;;;;

 

max 값을 구하는 더 쉬운 방법을 찾아야할 것 같은 느낌.


 

방법1. ORDER BY ~ LIMIT n ??????

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

"회원별로 리뷰 개수가 가장 많은 애 1명만 데려와"

 

상위 몇 개 구할 때 LIMIT 쓰니까 여기서도 이렇게 해서 최댓값 구하면 되지 않을까? 싶었지만

최댓값만큼 리뷰를 쓴 사람이 1명이 아닐 수도 있기 때문에 안된다.

 

방법2. RANK 함수 !!!!!!!!!!!

SELECT MEMBER_ID
FROM
(
SELECT MEMBER_ID,
        RANK() OVER (ORDER BY COUNT(*) DESC) AS rnk
FROM REST_REVIEW
GROUP BY 1
) A
WHERE rnk = 1

"회원별 리뷰 개수가 많은 순서대로 순위를 매겼을 때 1등인 애들만 데려와"

 

RANK는 중복값도 같은 순위를 매기니까 rnk = 1 이라고 해도 여러 명의 데이터가 나올 수 있다는 뜻.

문제에서 조건으로 주어진 '리뷰를 가장 많이 작성한 회원' 을 만족하는 MEMBER_ID 리스트가 나오는 거다.

 

조건 만들었으니 이제 다시 필요한 애들만 데려오는 메인 쿼리를 작성해보자.

SELECT M.MEMBER_NAME,
        R.REVIEW_TEXT,
        DATE_FORMAT(R.REVIEW_DATE,'%Y-%m-%d') AS REVIEW_DATE
FROM MEMBER_PROFILE M JOIN REST_REVIEW R
ON M.MEMBER_ID = R.MEMBER_ID
WHERE M.MEMBER_ID IN
    (
    SELECT MEMBER_ID
    FROM
        (
        SELECT MEMBER_ID,
                RANK() OVER (ORDER BY COUNT(*) DESC) AS rnk
        FROM REST_REVIEW
        GROUP BY 1
        ) A
    WHERE rnk = 1
    )
ORDER BY 3,2

 

처음에는 서브쿼리가 3개였는데 RANK 사용하니 1개로 줄었다. 🫢

회원별 리뷰 개수를 구하고 ➡️ 그 중 최댓값을 구한 다음 ➡️ 회원별 리뷰 개수가 최댓값과 일치하는 조건의 회원을 구하는 3번의 단계가

순위 매기고 ➡️ 그 중 1등인 회원만 구하는 걸로 끝나기 때문 !

 

window 함수는 복잡한 서브쿼리 계산 없이 간단하게 원하는 조건을 만들어줄 수 있다.

 

근데 이거보다 더 간단한 방법이 있었다 ,, 

같은 팀원 분이 공유해주신 쿼리 !

 

방법3. RANK 함수 + INNER JOIN 👍🏼👍🏼👍🏼👍🏼👍🏼👍🏼

SELECT m.member_name, 
		r.review_text, 
        DATE_FORMAT(r.review_date, '%Y-%m-%d') review_date
FROM member_profile m 
INNER JOIN rest_review r
ON m.member_id = r.member_id 
INNER JOIN (
    SELECT member_id, 
            RANK() OVER (ORDER BY COUNT(*) DESC) rk
    FROM rest_review
    GROUP BY member_id
) a
ON m.member_id = a.member_id
WHERE a.rk = 1
ORDER BY review_date, review_text;

위에서는 WHERE 절로 [회원ID가 서브쿼리의 회원 ID에 포함될 때] 라는 조건을 넣어줬는데

그냥 아예 조인을 2번 해버리는 거다.

공유할 수 있는 공통칼럼으로 그 조건을 만들어 줄 수 있으니까 !

그럼 INNER JOIN 이 의미하는 바가 [회원ID가 서브쿼리의 회원 ID에 포함될 때] 라는 조건과 같아지니까 !!