데이터베이스 따라잡기

SQL 기초

meellon 2026. 6. 20. 06:30

학습 목표

DDLDML을 구분하고 대표 문법을 읽을 수 있다.

SELECT 절의 논리적 처리 순서를 설명할 수 있다.

INNER JOINLEFT JOIN의 결과 차이를 예로 설명할 수 있다.

GROUP BY·HAVING으로 집계·필터를 할 수 있다.

서브쿼리가 쓰이는 대표 패턴을 인식할 수 있다.

문제 상황

  • ORM이 만든 SQL이 SELECT *JOIN이 여러 개라 느리다
    • 어떤 절이 먼저 적용되는지 모르면 튜닝이 막막하다
  • LEFT JOIN인데 WHERE orders.id IS NOT NULL을 쓰면 INNER처럼 동작한다
  • GROUP BY 없이 COUNT(*)와 컬럼을 섞으면 DB가 에러를 낸다
  • "서브쿼리 vs JOIN" 논쟁만 듣고 언제 무엇을 쓸지 기준이 없다

관계형 모델에서 릴레이션·키·제약을 봤다. SQL은 그 구조를 조작·조회하는 언어다.

1. DDL과 DML

분류 역할
DDL (Data Definition) 스키마 정의·변경 CREATE TABLE, ALTER, DROP
DML (Data Manipulation) 데이터 삽입·변경·삭제·조회 INSERT, UPDATE, DELETE, SELECT
DCL / TCL 권한, 트랜잭션 GRANT, COMMIT, ROLLBACK
CREATE TABLE users (
  id         BIGINT PRIMARY KEY,
  email      VARCHAR(255) UNIQUE NOT NULL,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

INSERT INTO users (id, email) VALUES (1, 'alice@example.com');
  • DDL은 보통 메타데이터 락·마이그레이션 이슈와 연결
  • DML은 앱·분석 쿼리의 대부분

2. SELECT 논리 순서

SQL을 쓰는 순서와 DB가 해석하는 순서가 다르다.

SELECT u.email, COUNT(o.id) AS order_count
FROM   users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE  u.created_at >= '2025-01-01'
GROUP BY u.email
HAVING COUNT(o.id) > 0
ORDER BY order_count DESC
LIMIT  10;
순서 역할
1 FROM / JOIN 테이블 결합, 카테시안 곱 후 조인 조건
2 WHERE 필터 (집계 전)
3 GROUP BY 그룹 형성
4 HAVING 그룹 필터 (집계 후)
5 SELECT 컬럼·표현식·집계 함수
6 ORDER BY 정렬
7 LIMIT 개수 제한
  • 옵티마이저는 실행 순서를 바꿀 수 있지만 의미(semantics) 는 위 순서를 따른다
  • WHERE vs HAVING: 행 조건 vs 그룹 조건

3. JOIN

관계형 모델의 관계를 쿼리로 표현한다.

-- 주문이 있는 사용자만
SELECT u.email, o.id
FROM users u
INNER JOIN orders o ON o.user_id = u.id;

-- 주문 없는 사용자도 포함
SELECT u.email, o.id
FROM users u
LEFT JOIN orders o ON o.user_id = u.id;
JOIN 결과
INNER JOIN 양쪽에 매칭 있는 행만
LEFT JOIN 왼쪽 전부 + 오른쪽 매칭 (없으면 NULL)
RIGHT JOIN 오른쪽 기준 (실무에선 LEFT로 대체 가능)
FULL OUTER 양쪽 미매칭까지 (DB별 지원 차이)
CROSS JOIN 카테시안 곱 — 의도적이지 않으면 위험
  • ON — 조인 조건, WHERE — 조인 필터
  • LEFT JOIN + WHERE o.id IS NOT NULL → 사실상 INNER

4. GROUP BY와 집계

SELECT status, COUNT(*) AS cnt, AVG(total) AS avg_total
FROM orders
WHERE created_at >= current_date - interval '30 days'
GROUP BY status;
  • SELECT에 나오는 비집계 컬럼은 GROUP BY에 포함되어야 한다 (표준 SQL)
  • HAVING status = 'paid' AND COUNT(*) > 100 — 그룹 단위 조건
함수 용도
COUNT(*) 행 수
SUM, AVG 합·평균
MIN, MAX 최소·최대
COUNT(DISTINCT col) 중복 제외 개수

5. 서브쿼리 개요

쿼리 안에 또 쿼리를 넣는 패턴이다.

-- 스칼라 서브쿼리
SELECT email,
       (SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) AS n
FROM users u;

-- IN
SELECT * FROM users
WHERE id IN (SELECT user_id FROM orders WHERE total > 1000);

-- EXISTS (반복 존재 검사에 유리한 경우 많음)
SELECT * FROM users u
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);
  • 상관 서브쿼리 — 안쪽이 바깥 행 참조
  • 성능은 옵티마이저·인덱스에 따라 JOIN과 동등 변환되기도, 안 되기도 함
  • 복잡하면 CTE(WITH)로 가독성 확보
WITH active_users AS (
  SELECT user_id, COUNT(*) AS cnt FROM orders GROUP BY user_id HAVING COUNT(*) >= 5
)
SELECT u.* FROM users u JOIN active_users a ON a.user_id = u.id;

6. 정리

  • DDL은 스키마, DML은 데이터 — 앱은 주로 DML
  • SELECT는 FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY 순으로 이해
  • JOIN 종류에 따라 누락·NULL 행이 달라진다
  • 서브쿼리·JOIN·CTE는 표현 방식; 실행 계획은 별도 확인

다음에 다룰 것

  • 정규화
  • 함수 종속, 1NF~3NF, 역정규화 트레이드오프

해당 내용은 Database System Concepts, 7/E (Avraham Silberschatz, Henry F. Korth, S. Sudarshan) 의 내용을 기반으로 합니다.

'데이터베이스 따라잡기' 카테고리의 다른 글

디스크와 페이지  (0) 2026.06.23
쿼리 처리 개요  (0) 2026.06.22
정규화  (0) 2026.06.21
관계형 모델  (0) 2026.06.19
데이터베이스 오리엔테이션  (0) 2026.06.18