topic난이도 · 약 25

SQL 기초 · SELECT/INSERT/UPDATE/DELETE · JOIN

4가지 CRUD 키워드 + INNER/LEFT JOIN — DB 대화의 알파벳.

#SQL#JOIN#INNER#LEFT#CTE#N+1
왜 배우는가

Supabase·Prisma·Drizzle이 SQL을 가려도, 에러·성능 문제 생기면 생 SQL을 읽어야 한다. JOIN 종류를 모르면 "왜 사용자가 빠졌지?" 사고를 못 푼다.

SQL은 '데이터에 던지는 4가지 질문'. SELECT(조회) · INSERT(추가) · UPDATE(수정) · DELETE(삭제). 표준 SQL은 대부분 DB에서 공통(미세 차이 존재).

sql
-- SELECT 기본
SELECT id, name FROM users WHERE age >= 20 ORDER BY name ASC LIMIT 10;

-- 집계 (GROUP BY + HAVING)
SELECT country, COUNT(*) AS n
FROM users
WHERE signup_at > '2024-01-01'
GROUP BY country
HAVING COUNT(*) >= 100
ORDER BY n DESC;

-- INSERT
INSERT INTO users (email, name) VALUES ('kim@example.com', '성훈');

-- UPDATE (WHERE 꼭!)
UPDATE users SET role = 'admin' WHERE id = 42;
-- ⚠️ WHERE 빼먹으면 모든 행이 바뀐다

-- DELETE
DELETE FROM posts WHERE created_at < NOW() - INTERVAL '1 year';

-- UPSERT (Postgres)
INSERT INTO users (email, name) VALUES ('k@e.com', '김')
ON CONFLICT (email) DO UPDATE SET name = EXCLUDED.name;

"WHERE 없는 UPDATE/DELETE"는 전설의 실수 1번. 반드시 트랜잭션으로 감싸고 먼저 SELECT로 영향 행 수를 확인하자.

JOIN은 여러 테이블을 한 결과로 합치기. 관계형 DB의 진짜 힘. 핵심 네 가지만 알면 된다.

JOIN 종류교집합·합집합
INNER JOIN양쪽에 매칭되는 행만교집합
LEFT JOIN왼쪽 전부 + 오른쪽 매칭 (없으면 NULL)왼쪽 포함
RIGHT JOIN오른쪽 전부 + 왼쪽 매칭오른쪽 포함 (드물게 사용)
FULL OUTER JOIN양쪽 전부 (안 맞으면 NULL)합집합
sql
-- 예시 테이블
-- users:  (id, name)                 posts: (id, user_id, title)
-- 1 김,  2 이,  3 박                 10 (user 1), 11 (user 2), 12 (user 99)

-- INNER JOIN — 양쪽 매칭만 (user 3 박, post 12가 사라짐)
SELECT u.name, p.title
FROM users u
INNER JOIN posts p ON u.id = p.user_id;
-- 김 | 제목A
-- 이 | 제목B

-- LEFT JOIN — 모든 사용자 + 글 (글 없으면 NULL)
SELECT u.name, p.title
FROM users u
LEFT JOIN posts p ON u.id = p.user_id;
-- 김 | 제목A
-- 이 | 제목B
-- 박 | NULL         ← user 3 유지, post 없음

-- "글 없는 사용자만 찾기" 이디엄
SELECT u.name
FROM users u
LEFT JOIN posts p ON u.id = p.user_id
WHERE p.id IS NULL;
-- 박

"LEFT JOIN + WHERE IS NULL"은 "관계가 없는 것"을 찾는 고전 패턴. 초보가 모르면 결과가 이상하다.

N+1 쿼리 문제 — ORM이 만드는 단골 함정. `users.map(u => db.posts.where(userId: u.id))`처럼 유저마다 쿼리 1번 = 1+N. JOIN 하나로 끝낼 걸 수백 번 왕복. Claude가 만든 코드에 루프 안 DB 호출이 있으면 JOIN으로 묶자.

sql
-- 실전: 서브쿼리와 CTE (Common Table Expression)
-- CTE가 가독성·디버깅 측면에서 우위

WITH active_users AS (
  SELECT id FROM users WHERE last_login > NOW() - INTERVAL '30 days'
)
SELECT u.name, COUNT(p.id) AS post_count
FROM users u
JOIN active_users a ON u.id = a.id
LEFT JOIN posts p ON p.user_id = u.id
GROUP BY u.id, u.name
ORDER BY post_count DESC
LIMIT 10;

-- 윈도우 함수 — "사용자별 순위" 같은 고급 분석
SELECT
  name,
  score,
  RANK() OVER (PARTITION BY country ORDER BY score DESC) AS country_rank
FROM users;

CTE·윈도우 함수는 Postgres·MySQL 8+·Supabase 모두 지원. Claude가 복잡 쿼리 짤 때 자주 등장 — 읽을 줄 알아야 한다.

실기 드릴 3문항
edit실기 드릴 · 단답형

양쪽 테이블에 모두 매칭되는 행만 결합하는 JOIN은?

database실기 드릴 · SQL 작성

글이 한 개도 없는 사용자의 이름을 모두 조회하는 SQL을 작성하시오.

sql
-- users(id, name), posts(id, user_id, title)
-- posts.user_id 는 users.id 참조
check_circle실기 드릴 · OX

`UPDATE users SET role='admin'` 처럼 WHERE를 빼먹으면 에러가 난다.