INSERT·SELECT·UPDATE·DELETE — 첫 CRUD
데이터를 넣고·읽고·바꾸고·지우는 4종 + UPDATE/DELETE 의 한 가지 안전 규칙.
SQL 의 90% 는 4가지 동사로 끝납니다 — INSERT(넣기)·SELECT(읽기)·UPDATE(바꾸기)·DELETE(지우기). 합쳐서 CRUD(Create·Read·Update·Delete). 5편은 입문 파트의 마지막으로, 이 4가지를 한 화면에 익힙니다.
실습 테이블은 3편의 users 를 그대로 씁니다.
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE,
age INTEGER CHECK (age >= 0),
active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
INSERT — 데이터 넣기
-- 한 행
INSERT INTO users (name, email, age)
VALUES ('준성', '[email protected]', 39);
-- 여러 행 (한 번에)
INSERT INTO users (name, email, age) VALUES
('홍길동', '[email protected]', 30),
('김자바', '[email protected]', 25),
('박파이', '[email protected]', 45);
-- 결과 받아오기 (INSERT ... RETURNING)
INSERT INTO users (name, email, age)
VALUES ('이타입', '[email protected]', 28)
RETURNING id, created_at;
-- id | created_at
-- ----+-------------------------------
-- 5 | 2026-05-17 00:25:00.123+09
RETURNING 의 가치. 방금 만든 행의 자동 생성된 PK(id), 시각(created_at) 을 즉시 받습니다. 애플리케이션 코드에서 "INSERT 한 다음 다시 SELECT" 같은 두 번 왕복을 한 번으로 줄입니다. UPDATE·DELETE 에도 동일하게 붙일 수 있어요.
SELECT — 데이터 읽기
-- 전부 (작을 때만, 보통은 컬럼 지정 권장)
SELECT * FROM users;
-- 컬럼 지정 + 별칭
SELECT id AS user_id, name, age
FROM users;
-- WHERE 조건
SELECT name, email
FROM users
WHERE active = true AND age >= 30;
-- 정렬·갯수 제한
SELECT name, age FROM users
WHERE active
ORDER BY age DESC
LIMIT 5;
-- 함수·표현식
SELECT name,
age + 1 AS next_age,
upper(email) AS email_upper,
extract(year from created_at) AS year_joined
FROM users;
SELECT * 는 학습·디버그용으로만, 운영 코드에서는 거의 항상 컬럼 명시. 이유: 스키마가 바뀌어도 코드가 영향 안 받고, 네트워크/메모리도 아낍니다. WHERE·ORDER BY·LIMIT 의 본격적인 사용법은 6·7편에서.
UPDATE — 데이터 바꾸기
-- 한 행
UPDATE users
SET age = 40
WHERE id = 1;
-- 여러 컬럼
UPDATE users
SET age = age + 1,
active = true
WHERE email = '[email protected]';
-- 표현식
UPDATE users
SET name = upper(name)
WHERE active;
-- RETURNING 으로 변경 결과 확인
UPDATE users
SET age = age + 1
WHERE id = 1
RETURNING id, name, age;
DELETE — 데이터 지우기
-- 한 행
DELETE FROM users WHERE id = 5;
-- 조건에 맞는 모두
DELETE FROM users WHERE active = false AND created_at < NOW() - INTERVAL '1 year';
-- 전부 (조심!)
DELETE FROM users;
TRUNCATE users; -- 더 빠름, 시퀀스도 리셋(RESTART IDENTITY 옵션)
UPDATE/DELETE 의 단 하나의 안전 규칙
"UPDATE / DELETE 를 칠 때 먼저 같은 WHERE 로 SELECT 를 친다."
운영 DB 에서 가장 흔한 사고가 WHERE 를 빼먹은 UPDATE/DELETE입니다. 한 행을 바꾸려다 100만 행을 날립니다. 다음 두 줄 패턴을 몸에 새기세요.
-- 1단계: 영향 받을 행을 먼저 확인
SELECT id, name, email FROM users WHERE id = 5;
-- 결과 1건 → 의도와 맞음 확인
-- 2단계: 같은 WHERE 로 실제 변경
DELETE FROM users WHERE id = 5;
또 하나의 안전망: 작업 전에 트랜잭션을 열어두기.
BEGIN;
DELETE FROM users WHERE active = false;
-- 영향 행 수 출력됨: 예) DELETE 7
-- 잘못 됐으면:
ROLLBACK;
-- 맞으면:
COMMIT;
트랜잭션은 14편에서 깊게 다룹니다. 입문 단계에서도 "운영 DB 에선 무조건 BEGIN/COMMIT 사이에" 라는 습관만 들이세요.
실전 패턴 — UPSERT 한 줄
-- "있으면 업데이트, 없으면 삽입" — PostgreSQL 정통 방식
INSERT INTO users (id, name, email, age)
VALUES (1, '준성', '[email protected]', 39)
ON CONFLICT (id) DO UPDATE
SET name = EXCLUDED.name,
email = EXCLUDED.email,
age = EXCLUDED.age;
ON CONFLICT 는 충돌(PK/UNIQUE) 시 동작을 정합니다. DO NOTHING 으로 무시도 가능. 실무에서 동기화·재시도에 매우 자주 쓰는 패턴이에요.
6편 — WHERE 절 전문가 (기초 시작)
비교·BETWEEN·IN·LIKE·NULL — 조건 표현의 정석과 NULL 3값 논리의 함정.
이전: 4편 데이터 타입 · 현재: 5편 (입문 마지막) · 다음 → 6편 WHERE (SQL 기초 시작) · 진행: 5/24