SQL 기초 — RDBMS·SELECT·WHERE·GROUP BY·JOIN
SQL 기초 — RDBMS·SELECT·WHERE·GROUP BY·JOIN
🎯 이 lesson 을 읽고 나면
이 lesson 을 다 읽고 나면 아래 3가지를 자신 있게 할 수 있습니다.
- ▸✅ SELECT · WHERE · GROUP BY · HAVING · ORDER BY 절 정확한 순서
- ▸✅ INNER JOIN vs LEFT JOIN 차이 + 케이스 선택
- ▸✅ EXPLAIN 으로 실행계획 읽는 법 (type · rows 컬럼)
학습 목표를 체크리스트로 두고 다 답할 수 있게 되면 lesson 을 닫으세요.
RDBMS 핵심 6가지
한 줄: 관계형 DB = 테이블 + PK/FK + 정규화 + ACID + 인덱스 + SQL. 50년 표준.
6가지 핵심 개념
정규화 단계 빠른 이해
> 💡 실무: 보통 3NF 까지. 성능 위해 의도적 비정규화 (조회 잦은 곳).
SQL 4부분
SELECT 7절 실행 순서
작성 순서 vs 실행 순서
각 절 — 역할과 흔한 함정
빠른 암기 팁
- ▸FWG·HSO·L — From-Where-Group / Having-Select-Order / Limit
- ▸데이터 줄이기 우선 — WHERE 가 빠를수록 뒤 단계 가벼움
- ▸인덱스 안 타는 패턴 5가지: 함수·LIKE 좌측%·형변환·NOT·OR
JOIN + GROUP BY 패턴
JOIN 5종 비교
JOIN 안티 패턴
안티 조인 (한쪽에만 있는 것):
자기 조인 (셀프 참조):
GROUP BY 6 패턴
WHERE vs HAVING
💾 실전 1 — 사용자별 주문 통계 (LEFT JOIN + GROUP BY)
-- 입력: users 테이블 + orders 테이블
-- 처리: LEFT JOIN 으로 주문 안 한 사용자도 포함 + GROUP BY 로 사용자별 집계
-- 출력: 사용자별 주문 수, 총 결제액 (주문 0건은 0 표시)
SELECT
u.id,
u.name,
COUNT(o.id) AS order_count, -- 주문 개수
COALESCE(SUM(o.amount), 0) AS total -- 총 결제액 (NULL → 0)
FROM users u
LEFT JOIN orders o
ON o.user_id = u.id
AND o.status = 'PAID' -- 결제완료만
GROUP BY u.id, u.name
ORDER BY total DESC
LIMIT 5;
💾 실전 2 — 월별 매출 (DATE_TRUNC + HAVING)
-- 입력: orders (created_at, total, status)
-- 처리: 월 단위 그룹화 + 결제완료 필터 + 100만 이상만
-- 출력: 월별 주문 수, 매출
SELECT
DATE_TRUNC('month', created_at)::DATE AS month,
COUNT(*) AS orders,
SUM(total) AS revenue,
ROUND(AVG(total)) AS avg_order
FROM orders
WHERE status = 'PAID'
GROUP BY month
HAVING SUM(total) > 1000000
ORDER BY month;
💾 실전 3 — Window 함수 (사용자별 최근 주문 Top 3)
-- 입력: orders
-- 처리: PARTITION BY user_id 로 사용자별 정렬 → 상위 3개
-- 출력: 각 사용자의 최근 주문 3건
WITH ranked AS (
SELECT
o.*,
ROW_NUMBER() OVER (
PARTITION BY user_id
ORDER BY created_at DESC
) AS rn
FROM orders o
)
SELECT user_id, id AS order_id, total, created_at
FROM ranked
WHERE rn <= 3
ORDER BY user_id, rn;
EXPLAIN — 쿼리 실행 계획 보기
EXPLAIN 이 왜 필요한가
같은 결과를 내는 쿼리도 DB 가 내부적으로 어떻게 실행 하느냐에 따라 수십·수백 배 속도 차이. EXPLAIN 은 MySQL 옵티마이저의 결정 을 보여주는 도구.
사용법
결과:
가장 중요한 컬럼 — type 과 rows
type 등급 — 좋은 순서대로
1. const / system — 1건 확정 (PK 로 조회). 최고.
2. eq_ref — JOIN 시 PK·UNIQUE. 매우 좋음.
3. ref — 인덱스 사용. 좋음.
4. range — 범위 인덱스 (WHERE age > 20). 무난.
5. index — 인덱스 전체 스캔. 나쁨.
6. ALL — 풀 테이블 스캔. 극혐 — 인덱스 없는 상태.
type 컬럼에 ALL 이 나오면 인덱스를 만들거나 쿼리를 수정 하세요.
rows — 검사할 행 수의 예측
100만 행 테이블에서 rows: 1000000 → 풀 스캔. rows: 5 → 인덱스 적중. rows 가 작을수록 빠름.
EXPLAIN ANALYZE — 실제 실행 시간까지 (MySQL 8+)
실제로 실행하면서 단계별 소요 시간 출력. 느린 쿼리 디버깅의 핵심 도구.
실무 워크플로우
1. 느린 쿼리 로그 (slow_query_log) 에서 1초 이상 쿼리 수집
2. 각 쿼리에 EXPLAIN 실행
3. type=ALL 또는 rows 가 큰 쿼리 → 인덱스 추가 검토
4. EXPLAIN ANALYZE 로 개선 전후 비교
🤖 AI 에게 이렇게 요청해보세요
이 lesson 의 개념을 알면 AI 에게 구체적으로 지시할 수 있습니다. 막연한 "고쳐줘" 가 아니라 어휘를 가진 요청 — 그게 토큰 절약의 출발점입니다.
- ▸"users · orders · products 3 테이블 설계 + FK + INDEX 까지 만들어줘"
- ▸"최근 7일 가입한 사용자 수를 일별로 집계하는 쿼리 작성해줘"
- ▸"이 쿼리에 EXPLAIN 붙여서 실행계획 해석해줘"
왜 이게 토큰을 줄이나
개념을 모를 땐 AI 답변을 받고도 "그게 뭐예요?" 를 다시 물어야 합니다. 그 "다시 물음" 이 토큰을 잡아먹습니다. 개념 한 번 익혀두면 대화가 한 번에 끝납니다.