key value 값도 데이터베이스와 엑셀 비슷한거니?
Key-Value도 비슷하긴 한데, 살짝 달라요!
엑셀 DB (테이블) Key-Value
────────────────────────────────────────────────────
열 제목 = 이름 컬럼명 = name Key = "name"
셀 값 = 김철수 값 = "김철수" Value = "김철수"
엑셀 / DB → 표 구조 (행과 열이 정해져 있음)
id | name | age
1 | 김철수 | 25
2 | 이영희 | 30
Key-Value → 쌍으로만 존재 (표 구조 없음)
{
"name": "김철수",
"age": 25
}
| 엑셀/DB | Key-Valu | |
|---|---|---|
| 구조 | (행+열) | 쌍만 존재 |
| 컬럼고정 | 모든 행이 같은 컬럼 | 각자 다른 Key 가능 |
| 관계 표현| | JOIN으로 연결 | 어려움 |
| 대표 예시 | MySQL | |Redis, JSON |
DB/엑셀 → 학교 출석부 (모든 학생이 같은 양식)
Key-Value → 각자 다른 포스트잇
어떤 건 이름만, 어떤 건 전화번호만 적혀있어도 됨
개념은 비슷하지만, DB/엑셀은 표 형태로 엄격하게 구조가 고정되고 Key-Value는 자유롭게 쌍으로만 저장한다는 게 차이예요! 🎯
데이터 베이스와 엑셀 비슷하다고 했는데 그렇다면 필드명 테이블이라고 하고 컬럼은 필드명의 속성들값이라고 생각하면되나요?
거의 다 왔는데 살짝 반대로 이해하신 것 같아요! 정확히 잡아드릴게요.
엑셀 열 제목 (이름, 나이, 이메일) = 필드명 = 컬럼명
↕
엑셀 열 전체 = 컬럼 (필드)
↕
각 셀의 값 = 값 (Value)
| id | name | age |
|---|---|---|
| 1 | 김철수 | 25 |
| 2 | 이영희 | 30 |
테이블 = 표 전체
필드명 = "id", "name", "age" ← 열 제목 자체
컬럼 = "name" 열 전체 (김철수, 이영희 포함한 열)
값 = "김철수", 25 같은 각각의 데이터
필드명 = 컬럼명 (같은 말이에요!) 테이블 안에 있는 열의 이름이 필드명이고, 그 열 전체가 컬럼입니다.
쉽게 말하면 필드명은 컬럼의 이름표 라고 생각하면 딱 맞아요! 🎯
Structured(구조화된) Query(질문) Language
— 데이터베이스와 대화하는 언어예요. 데이터를 저장, 조회, 수정, 삭제하는 모든 작업을 SQL로 합니다.
맞아요! 개념 잡을 때 엑셀이랑 비교하면 딱 이해됩니다.
엑셀 데이터베이스
─────────────────────────────────────
파일 (.xlsx) ≈ 데이터베이스
시트 (Sheet) ≈ 테이블 (Table)
열 (Column) ≈ 컬럼 (Column)
행 (Row) ≈ 행 (Row)
셀 ≈ 값 (Value)
엑셀의 시트 한 장 = DB의 테이블 하나라고 보면 됩니다.
| 상황 | 엑셀 | 데이터베이스 |
|---|---|---|
| 데이터 100만 건 | 버벅거리거나 뻗음 💀 | 거뜬함 ✅ |
| 동시 접속 | 한 명만 수정 가능 | 1000명이 동시에 읽기/쓰기 ✅ |
| 앱이랑 연동 | 불가능 | API로 바로 연결 ✅ |
| 실수로 삭제 | ctrl+Z 조마조마 | 트랜잭션으로 롤백 ✅ |
| 데이터 검색 | 필터 직접 클릭 | SQL로 0.001초 ✅ |
| 보안 | 파일 공유하면 다 보임 | 권한별 접근 제어 ✅ |
엑셀은 사람이 눈으로 보는 도구, DB는 프로그램이 자동으로 처리하는 도구
개인 가계부 → 엑셀로 충분 쇼핑몰/앱 → 무조건 DB 필요
데이터베이스 (Database)
└── 테이블 (Table) ← 엑셀 시트라고 생각
├── 컬럼 (Column) ← 열 (이름, 나이, 이메일...)
└── 행 (Row) ← 실제 데이터 한 줄
예시 테이블 users:
| id | name | age | |
|---|---|---|---|
| 1 | 김철수 | 25 | chul@gmail.com |
| 2 | 이영희 | 30 | young@gmail.com |
| 3 | 박민수 | 22 | min@gmail.com |
Data Definition Language — 테이블을 생성/수정/삭제
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT, -- 고유 번호, 자동 증가
name VARCHAR(100) NOT NULL, -- 최대 100자, 필수
age INT,
email VARCHAR(200) UNIQUE, -- 중복 불가
created_at DATETIME DEFAULT NOW() -- 기본값 현재 시간
);
INT -- 정수 (1, 100, -5)
VARCHAR(n) -- 가변 문자열 최대 n자 ("홍길동")
TEXT -- 긴 문자열 (게시글 본문)
DATETIME -- 날짜+시간 (2024-01-15 09:30:00)
DATE -- 날짜만 (2024-01-15)
BOOLEAN -- true / false
DECIMAL(10, 2) -- 소수점 (돈 계산할 때: 99999999.99)
ALTER TABLE users ADD COLUMN phone VARCHAR(20); -- 컬럼 추가
ALTER TABLE users DROP COLUMN phone; -- 컬럼 삭제
ALTER TABLE users MODIFY COLUMN name VARCHAR(200); -- 컬럼 수정
DROP TABLE users; -- 테이블 완전 삭제 (복구 불가 ⚠️)
TRUNCATE TABLE users; -- 데이터만 전부 삭제, 구조는 유지
Data Manipulation Language — 실제 데이터를 다루는 것
-- 한 줄 삽입
INSERT INTO users (name, age, email)
VALUES ('김철수', 25, 'chul@gmail.com');
-- 여러 줄 한번에
INSERT INTO users (name, age, email) VALUES
('이영희', 30, 'young@gmail.com'),
('박민수', 22, 'min@gmail.com'),
('최지훈', 28, 'ji@gmail.com');
-- 전체 조회
SELECT * FROM users;
-- 특정 컬럼만
SELECT name, email FROM users;
-- 조건 (WHERE)
SELECT * FROM users WHERE age >= 25;
SELECT * FROM users WHERE name = '김철수';
SELECT * FROM users WHERE age BETWEEN 20 AND 30;
SELECT * FROM users WHERE name LIKE '김%'; -- '김'으로 시작하는 이름
SELECT * FROM users WHERE email IS NULL; -- 이메일 없는 유저
-- 정렬 (ORDER BY)
SELECT * FROM users ORDER BY age ASC; -- 나이 오름차순
SELECT * FROM users ORDER BY age DESC; -- 나이 내림차순
-- 개수 제한 (LIMIT)
SELECT * FROM users ORDER BY id DESC LIMIT 10; -- 최신 10명만
-- 중복 제거 (DISTINCT)
SELECT DISTINCT age FROM users;
-- WHERE 없으면 전체 수정 ⚠️ 항상 WHERE 확인!
UPDATE users
SET age = 26, email = 'new@gmail.com'
WHERE id = 1;
-- WHERE 없으면 전체 삭제 ⚠️
DELETE FROM users WHERE id = 1;
DELETE FROM users WHERE age < 20; -- 20살 미만 전부 삭제
SELECT COUNT(*) FROM users; -- 총 유저 수
SELECT AVG(age) FROM users; -- 평균 나이
SELECT MAX(age) FROM users; -- 최고 나이
SELECT MIN(age) FROM users; -- 최저 나이
SELECT SUM(age) FROM users; -- 나이 합계
-- 나이대별 유저 수
SELECT age, COUNT(*) as user_count
FROM users
GROUP BY age
ORDER BY age;
-- 결과:
-- age | user_count
-- 22 | 3
-- 25 | 5
-- 30 | 2
-- 유저가 2명 이상인 나이대만
SELECT age, COUNT(*) as cnt
FROM users
GROUP BY age
HAVING cnt >= 2;
-- WHERE vs HAVING
-- WHERE : 그룹화 전에 필터링 (개별 행)
-- HAVING : 그룹화 후에 필터링 (그룹 결과)
두 테이블이 있다고 가정:
users 테이블 orders 테이블
id | name id | user_id | product
1 | 김철수 1 | 1 | 노트북
2 | 이영희 2 | 1 | 마우스
3 | 박민수 3 | 2 | 키보드
4 | 99 | 모니터 ← user_id 99는 없음
SELECT users.name, orders.product
FROM users
INNER JOIN orders ON users.id = orders.user_id;
-- 결과:
-- name | product
-- 김철수 | 노트북
-- 김철수 | 마우스
-- 이영희 | 키보드
-- (박민수는 주문 없어서 제외, user_id=99도 제외)
SELECT users.name, orders.product
FROM users
LEFT JOIN orders ON users.id = orders.user_id;
-- 결과:
-- name | product
-- 김철수 | 노트북
-- 김철수 | 마우스
-- 이영희 | 키보드
-- 박민수 | NULL ← 주문 없어도 유저는 나옴
SELECT u.name, o.product, p.price
FROM users u
JOIN orders o ON u.id = o.user_id
JOIN products p ON o.product_id = p.id
WHERE u.age >= 25;
-- 평균 나이보다 많은 유저
SELECT name, age
FROM users
WHERE age > (SELECT AVG(age) FROM users);
-- 주문한 적 있는 유저만 조회
SELECT name FROM users
WHERE id IN (SELECT DISTINCT user_id FROM orders);
-- 주문한 적 없는 유저 조회
SELECT name FROM users
WHERE id NOT IN (SELECT DISTINCT user_id FROM orders);
-- 인덱스 없으면: 100만 건을 처음부터 끝까지 스캔 🐢
-- 인덱스 있으면: 바로 위치로 점프 ⚡
CREATE INDEX idx_email ON users(email); -- 단일 인덱스
CREATE INDEX idx_name_age ON users(name, age); -- 복합 인덱스
-- 인덱스 확인
SHOW INDEX FROM users;
-- 인덱스 삭제
DROP INDEX idx_email ON users;
언제 인덱스 쓸까? WHERE, JOIN, ORDER BY에 자주 쓰이는 컬럼에 걸기. 단, 인덱스가 너무 많으면 INSERT/UPDATE가 느려짐.
-- 은행 송금 예시
-- A가 B에게 10만원 송금할 때 중간에 오류나면?
-- → 둘 다 취소되어야 함
START TRANSACTION;
UPDATE accounts SET balance = balance - 100000 WHERE id = 'A';
UPDATE accounts SET balance = balance + 100000 WHERE id = 'B';
-- 둘 다 성공하면
COMMIT;
-- 하나라도 실패하면
ROLLBACK; -- 전부 원래대로 되돌림
SQL은 작성 순서와 실행 순서가 달라요:
-- 작성 순서
SELECT name, COUNT(*) -- 5. 조회할 컬럼
FROM users -- 1. 테이블 선택
JOIN orders ON ... -- 2. 조인
WHERE age >= 20 -- 3. 필터링
GROUP BY name -- 4. 그룹화
HAVING COUNT(*) >= 2 -- 6. 그룹 필터링
ORDER BY name -- 7. 정렬
LIMIT 10; -- 8. 개수 제한
-- 실행 순서: FROM → JOIN → WHERE → GROUP BY → SELECT → HAVING → ORDER BY → LIMIT
-- 페이지네이션 (2페이지, 한 페이지 10개)
SELECT * FROM users ORDER BY id DESC LIMIT 10 OFFSET 10;
-- 없으면 INSERT, 있으면 UPDATE
INSERT INTO users (id, name, email)
VALUES (1, '김철수', 'new@gmail.com')
ON DUPLICATE KEY UPDATE email = 'new@gmail.com';
-- NULL 처리
SELECT name, COALESCE(phone, '번호없음') as phone FROM users;
-- 날짜 필터
SELECT * FROM users
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY); -- 최근 7일
1단계: SELECT, WHERE, ORDER BY, LIMIT (조회 기본)
2단계: INSERT, UPDATE, DELETE (데이터 수정)
3단계: GROUP BY, HAVING, 집계함수 (통계)
4단계: JOIN (핵심 ⭐)
5단계: 서브쿼리, 인덱스, 트랜잭션 (심화)
궁금한 부분 있으면 더 파고들어 드릴게요! 🚀