SQL 기본 개념과 실무 활용
SQL 기본 개념과 실무 활용
🗄️ 데이터베이스 기본 개념
관계형 데이터베이스(RDBMS)란?
관계형 데이터베이스는 데이터를 테이블 형태로 저장하고 관리하는 시스템입니다. 각 테이블은 행(Row)과 열(Column)로 구성되며, 테이블 간의 관계를 통해 복잡한 데이터 구조를 표현할 수 있습니다.
주요 RDBMS
- PostgreSQL: 오픈소스, 강력한 기능, 표준 SQL 준수
- MySQL: 빠른 성능, 웹 애플리케이션에서 많이 사용
- SQLite: 경량, 임베디드 데이터베이스
- Oracle: 엔터프라이즈급, 고성능
- SQL Server: Microsoft 제품군과 호환성
📝 SQL 기본 문법
DDL (Data Definition Language) - 데이터 정의어
테이블 생성
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
age INTEGER CHECK (age >= 0),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
테이블 수정
-- 컬럼 추가
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
-- 컬럼 수정
ALTER TABLE users ALTER COLUMN name TYPE VARCHAR(150);
-- 컬럼 삭제
ALTER TABLE users DROP COLUMN phone;
테이블 삭제
DROP TABLE users;
DML (Data Manipulation Language) - 데이터 조작어
데이터 삽입
-- 단일 데이터 삽입
INSERT INTO users (name, email, age)
VALUES ('김개발', 'kim@example.com', 25);
-- 다중 데이터 삽입
INSERT INTO users (name, email, age) VALUES
('이백엔드', 'lee@example.com', 30),
('박프론트', 'park@example.com', 28),
('최풀스택', 'choi@example.com', 32);
데이터 조회
-- 기본 조회
SELECT * FROM users;
-- 특정 컬럼 조회
SELECT name, email FROM users;
-- 조건부 조회
SELECT * FROM users WHERE age >= 30;
-- 정렬
SELECT * FROM users ORDER BY age DESC;
-- 제한
SELECT * FROM users LIMIT 10 OFFSET 20;
데이터 수정
-- 조건부 수정
UPDATE users
SET age = 26
WHERE email = 'kim@example.com';
-- 다중 컬럼 수정
UPDATE users
SET name = '김시니어', age = 35
WHERE id = 1;
데이터 삭제
-- 조건부 삭제
DELETE FROM users WHERE age < 18;
-- 전체 삭제 (주의!)
DELETE FROM users;
🔍 고급 쿼리 기법
JOIN 연산
테이블 설계 예시
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
title VARCHAR(200) NOT NULL,
content TEXT,
user_id INTEGER REFERENCES users(id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE comments (
id SERIAL PRIMARY KEY,
content TEXT NOT NULL,
post_id INTEGER REFERENCES posts(id),
user_id INTEGER REFERENCES users(id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INNER JOIN
-- 사용자와 그들의 게시글 조회
SELECT u.name, p.title, p.created_at
FROM users u
INNER JOIN posts p ON u.id = p.user_id
ORDER BY p.created_at DESC;
LEFT JOIN
-- 모든 사용자와 그들의 게시글 수 조회 (게시글이 없는 사용자도 포함)
SELECT u.name, COUNT(p.id) as post_count
FROM users u
LEFT JOIN posts p ON u.id = p.user_id
GROUP BY u.id, u.name;
복합 JOIN
-- 게시글, 작성자, 댓글 정보 함께 조회
SELECT
p.title,
u.name as author,
c.content as comment,
cu.name as commenter
FROM posts p
JOIN users u ON p.user_id = u.id
LEFT JOIN comments c ON p.id = c.post_id
LEFT JOIN users cu ON c.user_id = cu.id
ORDER BY p.created_at DESC, c.created_at ASC;
집계 함수와 GROUP BY
-- 사용자별 게시글 통계
SELECT
u.name,
COUNT(p.id) as post_count,
AVG(LENGTH(p.content)) as avg_content_length,
MAX(p.created_at) as last_post_date
FROM users u
LEFT JOIN posts p ON u.id = p.user_id
GROUP BY u.id, u.name
HAVING COUNT(p.id) > 0
ORDER BY post_count DESC;
서브쿼리
-- 가장 많은 댓글을 받은 게시글
SELECT title,
(SELECT COUNT(*) FROM comments WHERE post_id = p.id) as comment_count
FROM posts p
WHERE (SELECT COUNT(*) FROM comments WHERE post_id = p.id) = (
SELECT MAX(comment_count)
FROM (
SELECT COUNT(*) as comment_count
FROM comments
GROUP BY post_id
) subquery
);
윈도우 함수 (PostgreSQL, MySQL 8.0+)
-- 사용자별 게시글에 순위 매기기
SELECT
name,
title,
created_at,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) as post_rank,
COUNT(*) OVER (PARTITION BY user_id) as total_posts
FROM posts p
JOIN users u ON p.user_id = u.id;
🚀 성능 최적화
인덱스 생성
-- 단일 컬럼 인덱스
CREATE INDEX idx_users_email ON users(email);
-- 복합 인덱스
CREATE INDEX idx_posts_user_created ON posts(user_id, created_at);
-- 부분 인덱스
CREATE INDEX idx_active_users ON users(email) WHERE age >= 18;
쿼리 실행 계획 확인
-- PostgreSQL
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'kim@example.com';
-- MySQL
EXPLAIN SELECT * FROM users WHERE email = 'kim@example.com';
성능 최적화 팁
-
적절한 인덱스 사용
- WHERE, JOIN, ORDER BY 절에서 자주 사용되는 컬럼에 인덱스 생성
- 너무 많은 인덱스는 INSERT/UPDATE 성능 저하
-
쿼리 최적화
- SELECT * 대신 필요한 컬럼만 조회
- 서브쿼리보다 JOIN 사용 고려
- LIMIT 사용으로 불필요한 데이터 조회 방지
-
정규화와 비정규화
- 적절한 정규화로 데이터 중복 제거
- 성능이 중요한 경우 선택적 비정규화 고려
🛡️ 데이터 무결성과 제약조건
기본 제약조건
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
price DECIMAL(10,2) CHECK (price > 0),
category_id INTEGER NOT NULL,
stock INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (category_id) REFERENCES categories(id),
UNIQUE (name, category_id)
);
트랜잭션
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- 모든 작업이 성공하면 커밋
COMMIT;
-- 문제가 있으면 롤백
-- ROLLBACK;
📊 실무에서 자주 사용하는 패턴
페이지네이션
-- OFFSET 방식 (간단하지만 큰 OFFSET에서 성능 저하)
SELECT * FROM posts
ORDER BY created_at DESC
LIMIT 20 OFFSET 100;
-- 커서 기반 페이지네이션 (성능 우수)
SELECT * FROM posts
WHERE created_at < '2024-01-01 12:00:00'
ORDER BY created_at DESC
LIMIT 20;
검색 기능
-- 기본 텍스트 검색
SELECT * FROM posts
WHERE title ILIKE '%검색어%'
OR content ILIKE '%검색어%';
-- PostgreSQL 전문 검색
SELECT * FROM posts
WHERE to_tsvector('korean', title || ' ' || content)
@@ plainto_tsquery('korean', '검색어');
통계 쿼리
-- 월별 게시글 통계
SELECT
DATE_TRUNC('month', created_at) as month,
COUNT(*) as post_count,
COUNT(DISTINCT user_id) as unique_authors
FROM posts
WHERE created_at >= '2024-01-01'
GROUP BY DATE_TRUNC('month', created_at)
ORDER BY month;
🔧 유용한 함수들
문자열 함수
SELECT
UPPER(name) as upper_name,
LENGTH(email) as email_length,
SUBSTRING(email FROM 1 FOR POSITION('@' IN email) - 1) as username,
CONCAT(name, ' (', age, '세)') as display_name
FROM users;
날짜 함수
SELECT
NOW() as current_time,
DATE_PART('year', created_at) as year,
AGE(NOW(), created_at) as post_age,
created_at + INTERVAL '7 days' as expires_at
FROM posts;
조건부 함수
SELECT
name,
age,
CASE
WHEN age < 20 THEN '10대'
WHEN age < 30 THEN '20대'
WHEN age < 40 THEN '30대'
ELSE '40대 이상'
END as age_group,
COALESCE(phone, '미등록') as phone_display
FROM users;
📚 추가 학습 리소스
권장 학습 순서
- 기본 문법 익히기: SELECT, INSERT, UPDATE, DELETE
- 관계 이해하기: JOIN의 다양한 유형과 활용
- 고급 기능: 서브쿼리, 윈도우 함수, CTE
- 성능 최적화: 인덱스, 실행계획, 쿼리 튜닝
- 실무 적용: 트랜잭션, 저장 프로시저, 함수
연습 플랫폼
- LeetCode Database: SQL 문제 해결
- HackerRank SQL: 단계별 SQL 학습
- SQLBolt: 인터랙티브 SQL 튜토리얼
- PostgreSQL Tutorial: 공식 문서와 예제
SQL은 데이터를 다루는 모든 개발자에게 필수적인 기술입니다. 기본기를 탄탄히 다지고 실무에서 다양한 상황에 적용해보면서 실력을 향상시켜 나가세요! 🚀