← 홈페이지 5강 목록으로
🔗
EPISODE 03
JOIN · GROUP BY · 서브쿼리 · 인덱스

SQL 심화

여러 테이블을 연결하는 JOIN(INNER/LEFT), GROUP BY와 집계 함수, HAVING, 서브쿼리, 그리고 성능을 위한 인덱스(EXPLAIN QUERY PLAN)를 익힙니다.

JOINGROUP BYINDEX
소요 시간
60분
난이도
📊 중급
선수 조건
🎯 db-02
결과물
주문-상품-유저 3테이블을 자유롭게 조인·집계

이 강의에서 배우는 것

  • 1INNER JOIN과 LEFT JOIN의 차이를 안다
  • 2GROUP BY + 집계 함수로 통계를 낸다
  • 3WHERE와 HAVING의 적용 시점을 구분한다
  • 4서브쿼리를 작성한다
  • 5인덱스를 만들고 EXPLAIN QUERY PLAN으로 검증한다

1. JOIN

INNER JOIN — 매칭된 행만

sql
SELECT users.name, orders.total_price, orders.status
FROM orders
INNER JOIN users ON orders.user_id = users.id;

LEFT JOIN — 왼쪽 모든 행

sql
-- 주문 없는 회원도 포함
SELECT users.name, orders.total_price
FROM users
LEFT JOIN orders ON users.id = orders.user_id;

SQLite는 RIGHT/FULL OUTER JOIN을 직접 지원하지 않음. LEFT 방향을 바꾸거나 UNION 사용.

3개 테이블

sql
SELECT
    u.name AS 회원명, p.name AS 상품명,
    oi.quantity AS 수량, oi.unit_price AS 단가
FROM order_items oi
INNER JOIN orders   o ON oi.order_id   = o.id
INNER JOIN users    u ON o.user_id     = u.id
INNER JOIN products p ON oi.product_id = p.id;

2. GROUP BY

sql
-- 카테고리별 상품 수
SELECT category, COUNT(*) AS 상품수
FROM products
GROUP BY category;

-- 카테고리별 평균 가격
SELECT category, AVG(price) AS 평균가격
FROM products
GROUP BY category;
함수설명
COUNT(*)행 수
COUNT(컬럼)NULL 제외 행 수
SUM(컬럼)합계
AVG(컬럼)평균
MAX/MIN최대/최소

3. HAVING

WHERE는 그룹화 전 행 필터, HAVING은 그룹화 후 필터.

sql
-- 주문 2건 이상 회원만
SELECT user_id, COUNT(*) AS 주문수
FROM orders
GROUP BY user_id
HAVING COUNT(*) >= 2;

-- 두 절 함께
SELECT category, AVG(price) AS 평균가격
FROM products
WHERE stock > 0
GROUP BY category
HAVING AVG(price) > 50000;

4. 서브쿼리

sql
-- 평균보다 비싼 상품
SELECT name, price
FROM products
WHERE price > (SELECT AVG(price) FROM products);

-- 특정 회원이 주문한 상품 이름
SELECT name FROM products WHERE id IN (
    SELECT product_id FROM order_items WHERE order_id IN (
        SELECT id FROM orders WHERE user_id = 1
    )
);

5. 인덱스

책의 색인과 같음. 자주 검색하는 컬럼에 인덱스를 추가하면 성능이 향상됩니다.

sql
CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_products_category ON products(category);
CREATE INDEX idx_order_items_op ON order_items(order_id, product_id);

DROP INDEX idx_orders_user_id;

EXPLAIN QUERY PLAN

sql
EXPLAIN QUERY PLAN
SELECT * FROM orders WHERE user_id = 1;
-- SEARCH TABLE orders USING INDEX idx_orders_user_id ← 인덱스 사용
-- SCAN TABLE orders                                 ← 인덱스 없음 (느림)
  • WHERE 조건에 자주 사용되는 컬럼
  • JOIN 연결 컬럼 (외래키)
  • ORDER BY에 자주 사용되는 컬럼
  • 단, 쓰기 성능이 약간 저하 — 모든 컬럼에 걸지 않음
예제 코드 / 강의 자료

전체 강의 자료와 예제 코드는 GitHub에서 자유롭게 받아볼 수 있습니다.

GitHub에서 보기 ↗