서브쿼리 vs JOIN: “최근 30일 적립 합계”를 가장 깔끔하게 구하는 기준
SQL을 배우다 보면 같은 결과를 만드는 방법이 여러 개라는 사실을 자주 만나게 됩니다. 특히 “서브쿼리로도 되는데 JOIN으로도 된다”는 상황이 흔합니다. 초반에는 둘 다 동작하면 그걸로 끝내기 쉽지만, 데이터가 커지고 요구사항이 늘어나면 “읽기 쉬운 쿼리”, “수정하기 쉬운 쿼리”, “오답이 덜 나는 쿼리”의 차이가 크게 드러납니다. 이번 글에서는 포인트 시스템에서 정말 자주 나오는 질문 하나로 비교해봅니다. “최근 30일 동안의 적립 합계(earned_30d)를 사용자별로 구하라.” 이 한 문장에 조건, 집계, 그룹핑, 결측 처리까지 다 들어있어서 연습 주제로 적당합니다. 이 단원의 목적: “정답을 만드는 방법”보다 “선택 기준”을 갖기 서브쿼리와 JOIN의 선택은 취향 문제가 아니라, 유지보수성과 안정성의 문제입니다. 이번 글의 목표는 아래 3가지입니다. 서브쿼리/파생 테이블(derived table)이 언제 읽기 쉬운지 이해한다 JOIN으로 풀었을 때 발생하기 쉬운 중복 집계 위험을 피하는 방법을 익힌다 동일 결과를 여러 방식으로 작성해 보고, 변경에 강한 형태를 고른다 0) 기준 정의: “적립(earned)”은 무엇인가 “적립 합계”라고 했을 때, 어떤 action_type을 적립으로 볼지는 정책입니다. 이 글에서는 다음을 적립 계열로 가정합니다. CHARGE: 일반 적립 USE_CANCEL: 사용 취소로 되돌림(결과적으로 +) ADJUST: 운영 정정(양수/음수 모두 가능하지만, 여기서는 ‘적립’ 합계에 포함 여부를 명확히 해야 함) 여기서 ADJUST는 정책에 따라 “적립으로 분류하지 않고 별도 집계”로 두는 경우도 많습니다. 다만 학습을 위해 “적립 계열로 묶되, 조건으로 쉽게 분리할 수 있다”는 방향으로 진행합니다. 1) 가장 단순한 답: point_history만으로 uid별 earned_30d 구하기 users 테이블을 굳이 붙일 필요가 없다면, point_history만으로도 답은 나옵니다. ...