[SQL] SUB QUERY 구문 복습 + 과제 5번 문제풀이

📌 오늘의 TIL 을 시작해 보겠다.
    1. SUB QUERY 구문
    2. 과제 5문제 중 어려웠던 부분
    3. 오늘의 회고
    4. 내일의 계획

 

💡 SUB QUERY 구문 💡

📌 특징
서브쿼리는 복잡한 분석을 차근차근 정리해 가며 수행할 수 있게 해주는 중요한 도구 이며,
SELECT 결과를 중간 결과처럼 활용해 추가 연산을 이어갈 수 있다.
JOIN, UNION을 이용하여 복잡한 로직을 단순한 구조로 대체 가능하며,
복잡한 조건 필터링, 집계 결과 비교 등에서 유용하게 사용된다.

적용 순서는 서브쿼리 실행(안쪽에 위치한 쿼리) → 메인쿼리(바깥쪽에 위치한 쿼리) 실행

 

✅ 중첩(일반) 서브쿼
위치 WHERE 절에서 사용됨
목적 서브쿼리 결과에 따라 WHERE 조건을 결정
(서브쿼리의 결과에 따라 달라지는 WHERE 조건절)
형태 WHERE 컬럼 = (SELECT …)

🥸 예시

-- 서브쿼리 활용
-- 가장 나이가 많은 사람의 이름 찾기

SELECT 이름
FROM basic.theglory
WHERE 나이 = (
  SELECT MAX(나이)
  FROM basic.theglory
);

 

✅ 스칼라 서브쿼리

사용 위치 SELECT 절에서 사용
특징 서브쿼리 결과가 단 하나의 값(1개의 행, 1개의 열)을 반환해야 함.
사용 방식 하나의 컬럼처럼 다른 컬럼들과 함께 사용
주의사항 일반적으로 다른 테이블과 함께 사용할 때 의미가 있음.
- 스칼라 서브쿼리 이용을 위해서는, 서로 다른 테이블이 필요함.

 

⁉️ 스칼라 서브쿼리는 언제 주로 사용해?

 

  1. 요약 통계값 비교

  • 평균, 중앙값, 최대값 등 전체 기준 값과 비교
  • 예시) 평균 구매금액 이상 구매한 고객 필터링

  2. 각 행에 파생 컬럼 추가

  • 파생 컬럼? 원본 데이터에 없던 값을 계산해서 새로 만들어 붙이는 컬럼
  • 다른 테이블에서 해당 행에 관련된 단일 값 가져오기
  • 예시) 고객 테이블에 “최근 구매일” 컬럼 붙이기
SELECT
    c.customer_id,
    c.customer_name,
    (SELECT MAX(order_date)
     FROM orders o
     WHERE o.customer_id = c.customer_id) AS last_order
FROM customers c;

 

 

✅ 인라인 뷰 서브쿼리(가장 많이 사용)

정의 FROM 절에 사용되는 서브쿼리로, 하나의 테이블처럼 사용
위치 FROM 절 내부
형태 (SELECT ...) AS 별칭
별칭(AS) 반드시 지정해야 함. (AS 별칭 없으면 외부에서 참조 불가)
주요 사용처 JOIN, UNION 시 가장 유용하게 사용됨.

 

⁉️ 인라인 뷰 서브쿼리는 언제 주로 사용해?

  1. 중간 결과를 바로 쓰고 싶을 때
    • 복잡한 계산을 한 번 해서 임시표처럼 만들고 그걸 바로 조인/조회하고 싶을 때
    • 예시) 고객별 월매출 합을 먼저 구해 둔 뒤 → 그 합계로 다시 조건/조인
  2. 조인하기 전에 덩치를 줄이고 싶을 때 (성능↑)
    • 원본이 너무 크면, 먼저 필요한 행/컬럼만 남긴 작은 표를 만든 다음 조인
    • 예시) 최근 3개월 데이터만 뽑아 둔 뒤 → 고객 마스터랑 조인
  3. 쿼리를 단계별로 나눠서 읽기 쉽게 만들 때
    • 안쪽에서 ‘집계’ 끝 → 바깥에서 ‘표시/정렬’을 한다.
  4. 집계한 뒤에 거르고 싶을 때
    • WHERE는 집계 전, HAVING은 집계 후 거르기
    • 인라인 뷰 안에서 GROUP BY + HAVING으로 걸러서, 바깥에서는 결과만 조인/표시
    • 예시) 고객별 합계가 10만 이상인 고객만 남겨서 → 이름 붙여 출력

🥸 예시

# 고객별 최신 결제 내역만 붙이기
SELECT
    a.name,
    a.age,
    b.goods_nm,
    b.goods_pay_date
FROM s1 AS a
INNER JOIN (
    SELECT name, goods_nm, goods_pay_date
    FROM s2
    WHERE (name, goods_pay_date) IN (
        SELECT name, MAX(goods_pay_date)
        FROM s2
        GROUP BY name
    )
) AS b
    ON a.name = b.name;
# 월 매출 10만원 이상인 고객만 조회
SELECT
    c.customer_id,
    c.customer_name,
    m.total_amount
FROM customers AS c
INNER JOIN (
    SELECT customer_id, SUM(amount) AS total_amount
    FROM payments
    WHERE payment_date >= '2025-07-01'
      AND payment_date < '2025-08-01'
    GROUP BY customer_id
    HAVING SUM(amount) >= 100000
) AS m
    ON c.customer_id = m.customer_id;

 

 

💡 과제 5번 풀이 중 어려웠던 부분 💡

 

조건1)레벨이 30 이상인 캐릭터를 기준으로, 게임 계정 별 캐릭터 수를 중복값 없이 추출해주세요.
조건2)HAVING 구문을 사용하여 캐릭터 수가 2 이상인 게임 계정만 추출해주세요.
조건3)인라인 뷰 서브쿼리를 활용하여 캐릭터 수 별 게임 계정 개수를 중복값 없이 추출해주세요.

 

조건1 과 조건2 까지는 쿼리문을 작성했다.

select game_account_id
      , count(distinct game_actor_id) as cnt_actor
from basic.users
where level >= 30
group by 1
having cnt_actor >= 2

중간 결과값

 

하지만, 조건3 에서 조건1과는 순서가 반대인 값을 추출하라고 하니 헷깔리기 시작했다.
거의 한 시간 가까이 고민을 했고 안되겠다 싶어서 도움을 조금 받았다.
(답은 알려주지 말고 힌트만 줘 힌트!!!)

얻어낸 힌트

 

힌트를 확인하고, 아래와 같이 작성을 마무리 할 수 있었다.

결국엔 cnt_actor(캐릭터 수)를 기준으로 game_account_id(게임 계정 수)를 불러오는 것.

cnt_actor 을 group by 하면 2, 3, 4, ~~ 로 구분지어 지고, 

캐릭터 수 2 : 게임 계정 수 1355 / 캐릭터 수 3 : 게임 계정 수 17 ~ 쭉 이렇게 결과값이 나온다.

select a.cnt_actor
      , count(distinct a.game_account_id) as cnt_account
from
(
select game_account_id
		, count(distinct game_actor_id) as cnt_actor
from basic.users
where level >= 30
group by 1
having cnt_actor >= 2
) a
group by 1;

최종 결과값


✍️  오늘의 회고

문제에 대해서 모르는 부분은 최대한 고민해보았다.
그럼에도 불구하고 모를 경우에는 조금의 힌트가
시간을 절약해주겠구나 싶은 순간이었다.
(합리화, 정당화 뭐 그런 건 아님😉)

 

 

 

🧭 내일의 계획

SQL 코드카타(프로그래머스) 문제 풀이
파이썬 종합반 강의 1~5강 수강

 

 

끝.