본문 바로가기
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

경쟁 없는 삽입 또는 갱신

존재 여부를 먼저 읽고 INSERT나 UPDATE를 고르면 두 요청이 동시에 “없음”을 보고 충돌할 수 있어. ON CONFLICT는 이 선택을 데이터베이스 안의 원자적 작업으로 만들어.

세 가지 대표 형태

  • ON CONFLICT (col) DO NOTHING은 지정한 충돌 행을 조용히 건너뛰어.
  • ON CONFLICT (col) DO UPDATE SET ...은 기존 행을 갱신하는 진짜 upsert야.
  • ON CONFLICT ON CONSTRAINT name DO ...은 열 목록 대신 이름 붙은 제약을 충돌 대상으로 삼아.

새 값은 EXCLUDED에서

DO UPDATE 안의 EXCLUDED는 INSERT하려던 새 행을 가리켜. 접두사 없는 열은 기존 행을 뜻하므로 옛 값을 유지할지, 새 값을 쓸지, 둘을 계산할지 명시할 수 있어.

Code

DO NOTHING으로 중복 막기·sql
INSERT INTO tags (name) VALUES ('postgres'), ('sql'), ('postgres')
ON CONFLICT (name) DO NOTHING;
-- 두 행 insert; 중복 'postgres' 조용히 스킵.
DO UPDATE로 갱신하거나 삽입하기·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

프로젝트에서 먼저 확인한 뒤 INSERT나 UPDATE하는 코드를 찾아 INSERT ... ON CONFLICT로 바꿔. 클라이언트 2개에서 동시에 실행해도 중복이나 유실 없이 처리되는지 확인해.

Progress

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

댓글 0

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

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