SQL(Structured Query Language)은 관계형 데이터베이스(RDBMS)를 관리하고 조작하기 위한 표준 언어다. 1970년대 IBM에서 Edgar Codd의 관계형 모델을 기반으로 개발됐으며, 현재 MySQL, PostgreSQL, Oracle, SQL Server 등 모든 주요 RDBMS의 표준 언어다.
SQL 명령어 분류
| 분류 | 이름 | 주요 명령어 | 설명 |
|---|
| DDL | 데이터 정의 | CREATE, ALTER, DROP, TRUNCATE | 스키마 구조 정의 |
| DML | 데이터 조작 | SELECT, INSERT, UPDATE, DELETE | 데이터 CRUD |
| DCL | 데이터 제어 | GRANT, REVOKE | 접근 권한 관리 |
| TCL | 트랜잭션 제어 | BEGIN, COMMIT, ROLLBACK, SAVEPOINT | 트랜잭션 관리 |
기본 구조 — 테이블
users 테이블:
┌────┬───────┬─────────────────┬─────┐
│ id │ name │ email │ age │
├────┼───────┼─────────────────┼─────┤
│ 1 │ Alice │ alice@email.com │ 30 │
│ 2 │ Bob │ bob@email.com │ 25 │
│ 3 │ Carol │ carol@email.com │ 35 │
└────┴───────┴─────────────────┴─────┘
DDL (Data Definition Language) — 구조 정의
sql
-- 테이블 생성
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
age INTEGER CHECK (age >= 0),
created_at TIMESTAMP DEFAULT NOW()
);
-- 컬럼 추가
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
-- 테이블 삭제
DROP TABLE users;
DML (Data Manipulation Language) — 데이터 조작
sql
-- 삽입
INSERT INTO users (name, email, age)
VALUES ('Alice', 'alice@email.com', 30);
-- 조회
SELECT id, name, age
FROM users
WHERE age > 25
ORDER BY age DESC
LIMIT 10;
-- 수정
UPDATE users
SET age = 31
WHERE name = 'Alice';
-- 삭제
DELETE FROM users
WHERE id = 3;
SELECT 심화
sql
-- 집계 함수
SELECT
COUNT(*) AS total,
AVG(age) AS avg_age,
MAX(age) AS max_age,
MIN(age) AS min_age
FROM users;
-- GROUP BY
SELECT age, COUNT(*) AS count
FROM users
GROUP BY age
HAVING COUNT(*) > 1;
-- 서브쿼리
SELECT name FROM users
WHERE age = (SELECT MAX(age) FROM users);
JOIN
sql
-- INNER JOIN: 두 테이블 모두에 있는 데이터
SELECT u.name, o.product, o.amount
FROM users u
INNER JOIN orders o ON u.id = o.user_id;
-- LEFT JOIN: users 기준, orders가 없어도 포함
SELECT u.name, o.product
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;
-- FULL OUTER JOIN: 양쪽 모두 포함, 매칭 없으면 NULL
SELECT u.name, o.product
FROM users u
FULL OUTER JOIN orders o ON u.id = o.user_id;
-- CROSS JOIN: 카티션 곱 (모든 조합)
SELECT u.name, p.name
FROM users u
CROSS JOIN products p;
| JOIN 유형 | 결과 |
|---|
| INNER JOIN | 양쪽 모두 존재하는 행만 |
| LEFT JOIN | 왼쪽 테이블 전체 + 매칭되는 오른쪽 |
| RIGHT JOIN | 오른쪽 테이블 전체 + 매칭되는 왼쪽 |
| FULL OUTER JOIN | 양쪽 전체 (매칭 없으면 NULL) |
윈도우 함수 (Window Functions)
GROUP BY와 달리 행을 집계해도 원본 행 수를 유지한다. OVER() 절이 핵심.
sql
-- 각 직원의 급여와 부서 평균 급여를 동시에 조회
SELECT
name,
department,
salary,
AVG(salary) OVER (PARTITION BY department) AS dept_avg,
salary - AVG(salary) OVER (PARTITION BY department) AS diff_from_avg
FROM employees;
-- 순위 함수
SELECT
name,
salary,
ROW_NUMBER() OVER (ORDER BY salary DESC) AS row_num, -- 고유 순번
RANK() OVER (ORDER BY salary DESC) AS rank, -- 동순위 시 간격 발생
DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rank -- 동순위 시 간격 없음
FROM employees;
-- 누적 합계
SELECT
date,
amount,
SUM(amount) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS cumulative
FROM sales;
-- LAG / LEAD: 이전/다음 행 참조
SELECT
date,
revenue,
LAG(revenue, 1) OVER (ORDER BY date) AS prev_revenue,
LEAD(revenue, 1) OVER (ORDER BY date) AS next_revenue,
revenue - LAG(revenue, 1) OVER (ORDER BY date) AS day_over_day_change
FROM daily_sales;
CTE (Common Table Expressions) — WITH 절
복잡한 쿼리를 가독성 있게 분리. 재귀 쿼리도 가능하다.
sql
-- 기본 CTE
WITH high_earners AS (
SELECT id, name, salary
FROM employees
WHERE salary > 80000
),
dept_counts AS (
SELECT department_id, COUNT(*) as emp_count
FROM employees
GROUP BY department_id
)
SELECT h.name, h.salary, d.emp_count
FROM high_earners h
JOIN dept_counts d ON h.department_id = d.department_id;
-- 재귀 CTE: 조직도/트리 탐색
WITH RECURSIVE org_chart AS (
-- 기저 케이스: 최상위 직원
SELECT id, name, manager_id, 0 AS level
FROM employees
WHERE manager_id IS NULL
UNION ALL
-- 재귀: 하위 직원 탐색
SELECT e.id, e.name, e.manager_id, oc.level + 1
FROM employees e
JOIN org_chart oc ON e.manager_id = oc.id
)
SELECT level, name FROM org_chart ORDER BY level, name;
뷰 (View)
자주 사용하는 복잡한 쿼리를 가상 테이블로 저장.
sql
-- 뷰 생성
CREATE VIEW active_user_orders AS
SELECT u.name, u.email, COUNT(o.id) AS order_count, SUM(o.amount) AS total_spent
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.active = true
GROUP BY u.id, u.name, u.email;
-- 뷰 사용 (일반 테이블처럼)
SELECT * FROM active_user_orders WHERE total_spent > 10000;
-- 뷰 삭제
DROP VIEW active_user_orders;
Materialized View (PostgreSQL, Oracle): 쿼리 결과를 실제로 저장. 읽기 성능 극대화, 수동 REFRESH 필요.
인덱스 (Index)
sql
-- 인덱스 없이: 전체 테이블 스캔 O(n)
SELECT * FROM users WHERE email = 'alice@email.com';
-- 단일 컬럼 인덱스 생성: B-Tree 검색 O(log n)
CREATE INDEX idx_users_email ON users(email);
-- 복합 인덱스: 자주 함께 조회되는 컬럼
CREATE INDEX idx_orders_user_date ON orders(user_id, created_at DESC);
-- 부분 인덱스: 조건에 맞는 행만 인덱싱
CREATE INDEX idx_active_users ON users(email) WHERE active = true;
-- 인덱스 삭제
DROP INDEX idx_users_email;
| 인덱스 유형 | 구조 | 적합 상황 |
|---|
| B-Tree | 균형 트리 | 범위 검색, =, >, < |
| Hash | 해시 테이블 | 등호(=) 검색만 |
| GIN | 역인덱스 | 배열, JSON, 전문 검색 |
| BRIN | 블록 범위 | 순차 데이터 (로그, 시계열) |
트랜잭션과 ACID
sql
BEGIN;
UPDATE accounts SET balance = balance - 1000 WHERE id = 1;
UPDATE accounts SET balance = balance + 1000 WHERE id = 2;
COMMIT; -- 둘 다 성공 시 확정
-- 오류 발생 시
ROLLBACK; -- 원래 상태로 되돌림
-- SAVEPOINT: 부분 롤백
BEGIN;
INSERT INTO logs VALUES ('start');
SAVEPOINT sp1;
UPDATE users SET status = 'active' WHERE id = 1; -- 실패
ROLLBACK TO sp1; -- sp1 이전 상태로만 복원
INSERT INTO logs VALUES ('partial rollback');
COMMIT;
ACID 속성:
| 속성 | 의미 |
|---|
| Atomicity (원자성) | 트랜잭션 전체가 성공하거나 전체 취소 |
| Consistency (일관성) | 트랜잭션 전후로 데이터 무결성 유지 |
| Isolation (격리성) | 동시 실행 트랜잭션 간 간섭 없음 |
| Durability (지속성) | 커밋된 데이터는 장애 후에도 영구 보존 |
쿼리 실행 계획 (EXPLAIN)
성능 문제 진단에 핵심 도구.
sql
-- 실행 계획 확인
EXPLAIN SELECT * FROM users WHERE email = 'alice@email.com';
-- 실제 실행 시간 포함
EXPLAIN ANALYZE SELECT u.name, COUNT(o.id)
FROM users u
JOIN orders o ON u.id = o.user_id
GROUP BY u.name;
-- 주요 키워드 해석
-- Seq Scan: 전체 테이블 순차 스캔 (인덱스 없음, 느림)
-- Index Scan: 인덱스 사용 (빠름)
-- Hash Join: 해시 기반 조인
-- cost=X..Y: 예상 비용 (첫 행..전체 행)
-- rows=N: 예상 결과 행 수
슬로우 쿼리 최적화 체크리스트:
- 1.EXPLAIN ANALYZE로 Seq Scan 위치 확인
- 2.WHERE 절 컬럼에 인덱스 추가
- 3.SELECT * 대신 필요한 컬럼만 지정
- 4.N+1 쿼리 문제는 JOIN으로 해결
- 5.큰 테이블 페이지네이션: OFFSET 대신 커서(Keyset) 방식
관련 개념
- •해시맵 — 데이터베이스 해시 인덱스
- •이진 트리 — B-Tree 인덱스 구조
- •이진 탐색 — 정렬된 인덱스 탐색
- •트랜잭션 — ACID 속성
- •PostgreSQL — 오픈소스 RDBMS 구현체
참고문헌
- •ISO/IEC 9075 — SQL 표준
- •Codd, E.F. (1970). A Relational Model of Data for Large Shared Data Banks
- •PostgreSQL 공식 문서
- •Use The Index, Luke — https://use-the-index-luke.com