VillageSQL is a drop-in replacement for MySQL with extensions.
All examples on this page work on VillageSQL. Install Now →
UPSERT 키워드가 없지만, 행을 삽입하거나 충돌을 처리하는 3가지 방법이 있습니다: INSERT ... ON DUPLICATE KEY UPDATE, REPLACE INTO, INSERT IGNORE입니다. 이들은 매우 다르게 동작하므로, 잘못 고르면 데이터가 조용히 손상될 수 있습니다.
INSERT … ON DUPLICATE KEY UPDATE
삽입이 고유 제약 조건(기본 키 또는 고유 인덱스)을 위반할 때, MySQL은 대신UPDATE를 실행합니다:
VALUES(col) 구문도 동작하지만 사용 중단되었습니다. 대신 행 별칭을 사용하세요:
REPLACE INTO
REPLACE는 충돌하는 행을 삭제하고 새 행을 삽입합니다. 업서트처럼 보이지만 삭제 + 삽입처럼 동작합니다:
BEFORE DELETE와 AFTER DELETE 트리거를 실행하고, 새 행에 대해 AUTO_INCREMENT 카운터를 다시 설정합니다. 삭제가 실제 행 삭제이므로, 참조하는 테이블의 ON DELETE CASCADE와 다른 외래 키 동작이 실제로 실행됩니다. REPLACE에 나열되지 않은 열은 이전 행의 값이 아니라 기본값을 받습니다.
REPLACE는 정말로 이전 행을 완전히 버리고 싶을 때만 사용하세요.
INSERT IGNORE
INSERT IGNORE는 오류를 경고로 낮춥니다. 키 충돌에서는 행을 건너뛰고, 유형 불일치나 잘림에서는 대신 강제 변환된 값을 씁니다:
INSERT IGNORE의 문제는 중복뿐 아니라 모든 오류를 억제한다는 것이며, 중복이 아닌 경우는 건너뛴 행보다 더 나쁩니다. INSERT IGNORE INTO page_views (page, views) VALUES ('/new', 'not_a_number')는 경고만 내고 ('/new', 0)을 삽입합니다: 아무 일도 하지 않는 것이 아니라 조용하고 손상된 쓰기입니다. 결과에 정말로 관심이 없을 때만 사용하세요.
비교
AUTO_INCREMENT 부작용
ON DUPLICATE KEY UPDATE는 삽입 경로가 아니라 업데이트 경로를 탈 때도 AUTO_INCREMENT 카운터를 증가시킵니다. 시간이 지나면 시퀀스에 간격이 생깁니다. page_views는 page를 기본 키로 사용하고 AUTO_INCREMENT 열이 없으므로, 효과를 보려면 대리 키가 있는 테이블을 사용하세요:
자주 묻는 질문
ON DUPLICATE KEY UPDATE는 원자적인가요?
예. 삽입 시도와 업데이트는 하나의 원자적 작업으로 일어납니다. 존재 확인과 삽입 사이의 경쟁 조건을 피하기 위해 트랜잭션으로 감쌀 필요가 없습니다.여러 고유 인덱스가 충돌하면 어떻게 되나요?
삽입이 동시에 2개 이상의 고유 제약 조건을 위반하면, MySQL은 행 1개만 업데이트하며 어느 행인지는 정의되어 있지 않습니다. INSERT 1개가 여러 고유 인덱스에서 충돌할 수 있는 설계는 피하세요. 스키마에 겹치는 고유 제약 조건이 있다면, 동작을 신중하게 테스트하세요.ON DUPLICATE KEY UPDATE를 다중 행 삽입과 함께 쓸 수 있나요?
예:VALUES를 사용하고 별칭에는 AS new를 사용하세요.
문제 해결
함께 보기
- MySQL의 트랜잭션 — UPSERT를 트랜잭션으로 감싸야 할 때
- MySQL의 교착 상태 — INSERT … ON DUPLICATE KEY UPDATE는 동시성 아래에서 교착 상태를 일으킬 수 있음
- MySQL의 대량 삽입 — 충돌 처리를 하든 안 하든 많은 행을 빠르게 적재하기
- 흔한 MySQL 오류와 해결 방법 — 중복 키 오류와 그 의미
- MySQL INSERT ON DUPLICATE KEY UPDATE — 예제와 함께 보는 이 문 자체