📌 오늘의 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 시 가장 유용하게 사용됨. |
⁉️ 인라인 뷰 서브쿼리는 언제 주로 사용해?
- 중간 결과를 바로 쓰고 싶을 때
- 복잡한 계산을 한 번 해서 임시표처럼 만들고 그걸 바로 조인/조회하고 싶을 때
- 예시) 고객별 월매출 합을 먼저 구해 둔 뒤 → 그 합계로 다시 조건/조인
- 조인하기 전에 덩치를 줄이고 싶을 때 (성능↑)
- 원본이 너무 크면, 먼저 필요한 행/컬럼만 남긴 작은 표를 만든 다음 조인
- 예시) 최근 3개월 데이터만 뽑아 둔 뒤 → 고객 마스터랑 조인
- 쿼리를 단계별로 나눠서 읽기 쉽게 만들 때
- 안쪽에서 ‘집계’ 끝 → 바깥에서 ‘표시/정렬’을 한다.
- 집계한 뒤에 거르고 싶을 때
- 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강 수강
끝.
'내일배움캠프' 카테고리의 다른 글
| [SQL] SQL코드카타 : 프로그래머스_NULL 처리하기(IFNULL, CASE) (4) | 2025.08.14 |
|---|---|
| [SQL 라이브 세션 - 3회차] SQL 집계함수, 그룹화(GROUP BY/HAVING) (1) | 2025.08.12 |
| [내일배움캠프] '25.08.11(월) / 2주차(1) - SOLVE SQL 난이도1 풀이 (2) | 2025.08.11 |