SQL

[프로그래머스 SQL KIT] JOIN #1 - 상품을 구매한 회원 비율 구하기

enayoiii 2025. 1. 25. 00:08

프로그래머스 문제 보러가기

 

 

위 문제는 상품을 구매한 회원 비율 구하기 문제는 회원 테이블과 온라인 구매 테이블 두 개를 JOIN해서 풀어야 하는 문제입니다. 2021년에 가입한 회원이면서 상품을 구매한 이력이 있는 회원을 찾아야 했어요.

 

그렇게 해서 결과 테이블이 < 구매 년도 | 구매 월 | 2021년에 가입했으면서 구매한 회원의 전체 수 | ((2021 가입 & 구매) / 2021가입) > 과 같은 형식이 되도록 출력하는 문제였습니다.

 

처음 제가 작성한 코드는 다음과 같습니다.

SELECT YEAR(O.SALES_DATE) AS YEAR, MONTH(O.SALES_DATE) AS MONTH,
    COUNT(O.USER_ID) AS PURCHASED_USERS,
    ROUND((COUNT(O.USER_ID)/COUNT(U.USER_ID)),1) AS PURCHASED_RATIO
FROM USER_INFO U
    JOIN ONLINE_SALE O
    ON U.USER_ID = O.USER_ID
WHERE YEAR(U.JOINED) = '2021'
ORDER BY YEAR;

WHERE 절에 조건을 걸어줬으니 COUNT 할 때 2021년에 구매한 사람만을 바탕으로 계산이 될 것이라 생각했는데, 제 착오였어요. 제가 실수한 부분은 다음과 같습니다.

 

 

WHERE, JOIN 문제

JOIN 조건 때문에 ONLINE_SALE 테이블에 구매 기록이 있는 회원만 결과에 포함돼서, 2021년에 가입한 전체 회원 수를 계산 할 때 문제가 있었습니다.

구매를 한 적 없는 회원은 JOIN 결과에 포함되지 않기 때문에, 2021년에 가입했으면서 구매를 한 이력이 있는 회원만 카운트되고 있었어요.

그래서 자꾸 PURCHASED_RATIO 계산시에 COUNT(DISTINCT O.USER_ID) / COUNT(DISTINCT U.USER_ID)를 계산하면, 구매한 회원 / 구매한 회원이 돼서 항상 1.0이 나오는 문제가 있었습니다.

 

 

COUNT DISTINCT 문제

ONLINE_SALE 테이블에 한 고객이 여러번 구매를 한 경우가 있기 때문에 DISTINCT를 써야한다는 것은 인지를 했었으나, COUNT(O.USER_ID) DISTINCT와 같이 적어서 syntax error가 계속 발생하길래 DISTINCT를 지웠었는데요, COUNT(DISTINCT O.USER_ID)와 같이 COUNT의 괄호 안에, 컬럼 앞에 DISTINCT 키워드를 적어줘야 함을 뒤늦게 기억해냈습니다.

 

 

정답

SELECT YEAR(O.SALES_DATE) AS YEAR, MONTH(O.SALES_DATE) AS MONTH,
    COUNT( DISTINCT O.USER_ID) AS PURCHASED_USERS,
    ROUND((COUNT(DISTINCT O.USER_ID)/(SELECT COUNT(*) FROM USER_INFO WHERE YEAR(JOINED) = 2021)),1) AS PURCHASED_RATIO
FROM ONLINE_SALE O
WHERE O.USER_ID IN (SELECT USER_ID FROM USER_INFO WHERE YEAR(JOINED) = 2021)
GROUP BY YEAR, MONTH
ORDER BY YEAR, MONTH;

 

수정된 부분

  1. DISTINCT 키워드 추가 : 구매 테이블에서 겹치는 USER_ID 카운트에서 제외
  2. JOIN 제거, 서브쿼리로 해결 : 위에서 언급했던 구매 이력이 있는 고객만 조회되는 문제 해결 위함
  3. YEAR 함수 사용시 문자열로 비교했던 것을 숫자로 변경 : 예) YEAR(SALES_DATE) = '2021'에서 YEAR(SALES_DATE) = 2021로.
let textNodes = document.querySelectorAll("div.tt_article_useless_p_margin.contents_style > *:not(figure):not(pre)"); textNodes.forEach(function(a) { a.innerHTML = a.innerHTML.replace(/`(.*?)`/g, '$1'); });