C.W.K.
Stream
Lesson 07 of 10 · published

Upsert 와 INSERT ON CONFLICT

~12 min · transactions, upsert

Level 0스키마 새싹
0 XP0/86 lessons0/10 achievements
0/120 XP to next level120 XP to go0% complete

Race 없는 insert-or-update

고전 anti-pattern: "행 존재 확인; 있으면 UPDATE, 없으면 INSERT." 그게 둘 동시 실행되면 둘 다 "없음" 봄, 둘 다 INSERT, 한쪽이 unique 제약에 실패. PostgreSQL 의 ON CONFLICT 절이 전체 연산을 atomic 하게.

쓸 세 가지 모양

  • ON CONFLICT (col) DO NOTHING — 조용한 dedup; "없으면 insert" 에 유용.
  • ON CONFLICT (col) DO UPDATE SET ... — 진정한 upsert; insert 또는 update.
  • ON CONFLICT ON CONSTRAINT name DO ... — 컬럼 추측 대신 named 제약 타겟할 때.

EXCLUDED — INSERT 시도한 행

DO UPDATE 안에서 키워드 EXCLUDED 가 INSERT 에서 온 행. 접두사 없으면 컬럼 이름이 기존 행 참조. 이게 선택권 줘: 옛 값 유지, 새 거 가져가기, 또는 둘에서 계산.

Code

DO NOTHING (dedup)·sql
INSERT INTO tags (name) VALUES ('postgres'), ('sql'), ('postgres')
ON CONFLICT (name) DO NOTHING;
-- 두 행 insert; 중복 'postgres' 조용히 스킵.
DO UPDATE (upsert)·sql
INSERT INTO users (email, name, login_count, last_login)
VALUES ('alice@example.com', 'Alice', 1, now())
ON CONFLICT (email) DO UPDATE
SET name = EXCLUDED.name,
    login_count = users.login_count + 1,
    last_login = EXCLUDED.last_login
RETURNING *;
DO UPDATE 의 WHERE·sql
-- 새 값이 실제로 더 새로울 때만 update
INSERT INTO sensor_readings (id, value, recorded_at)
VALUES (?, ?, ?)
ON CONFLICT (id) DO UPDATE
SET value = EXCLUDED.value,
    recorded_at = EXCLUDED.recorded_at
WHERE EXCLUDED.recorded_at > sensor_readings.recorded_at;

External links

Exercise

프로젝트의 'check then insert/update' 코드를 INSERT ... ON CONFLICT 로 대체. 두 클라이언트에서 동시에 upsert 실행해서 동시 write 처리하는 거 확인.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.