
잘 돌던 INSERT가 갑자기 ERROR 1062 (23000): Duplicate entry '...' for key '...'를 뱉으면 당황스럽습니다. 급한 마음에 INSERT IGNORE를 붙였다가 데이터가 조용히 사라지기도 하죠. 이 글은 MySQL 8.4 LTS 공식 매뉴얼에서 확인한 내용만으로 어떤 인덱스가 막고 있는지 찾는 순서와 각 해결책의 부작용을 정리했습니다. 2026년 9월 14일 기준입니다.
3줄 빠른 결론
- 1062는 PRIMARY KEY 또는 UNIQUE 인덱스 위반이며, 메시지 뒤의 키 이름이 범인입니다.
SHOW CREATE TABLE과SHOW INDEX FROM 테이블로 그 인덱스의 컬럼 구성부터 확인하세요.INSERT IGNORE는 행을 버리고,REPLACE는 기존 행을 삭제 후 삽입합니다. 의도한 동작인지 꼭 구분하세요.
오류 메시지 정확히 읽기
| 번호 | 심볼 | SQLSTATE | 의미 |
|---|---|---|---|
| 1062 | ER_DUP_ENTRY | 23000 | 데이터 값이 중복 |
| 1586 | ER_DUP_ENTRY_WITH_KEY_NAME | 23000 | 같은 상황, 키 이름 표시형 |
| 1061 | ER_DUP_KEYNAME | 42000 | 인덱스 이름이 중복(데이터 아님) |
공식 레퍼런스는 "1062의 메시지는 ER_DUP_ENTRY_WITH_KEY_NAME의 형식 문자열을 사용한다"고 적고 있어 실제로는 키 이름이 문자열로 보입니다. 8.4 매뉴얼 예시입니다.
mysql> CREATE TABLE t (i INT NOT NULL PRIMARY KEY);
mysql> INSERT INTO t (i) VALUES(1),(1);
ERROR 1062 (23000): Duplicate entry '1' for key 't.PRIMARY'
테이블명.인덱스명 형태이며 PRIMARY는 기본키입니다. 이 표기의 도입 시점은 릴리스 노트에서 확인하지 못해 8.4 매뉴얼 예시 기준으로만 봐 주세요.
원인 찾는 순서
- 키 이름 확보 - 오류 메시지의
for key뒤 이름을 그대로 복사합니다. - 테이블 정의 확인 -
SHOW CREATE TABLE 테이블명;으로 PRIMARY KEY·UNIQUE 정의와 끝줄의COLLATE값을 봅니다. - 인덱스 컬럼 확인 -
SHOW INDEX FROM 테이블명;에서 해당Key_name행의Non_unique가 0인지,Seq_in_index로 복합 인덱스 컬럼 순서를 확인합니다. 공식 ALTER TABLE 문서도 인덱스 이름은 이 명령으로 알아내라고 안내합니다. - 실제 중복 행 찾기
매뉴얼이 레시피로 안내하진 않고 문서화된 기능을 조합한 표준 기법입니다.SELECT 컬럼, COUNT(*) FROM 테이블 GROUP BY 컬럼 HAVING COUNT(*) > 1; - 콜레이션 확인 - 기본값
utf8mb4_0900_ai_ci는 악센트·대소문자 무시라Apple과apple이 중복으로 잡힙니다. 구분하려면utf8mb4_0900_as_cs나utf8mb4_bin으로 선언해야 합니다.
해결책 3가지와 문서화된 부작용
| 방법 | 동작 | 알아야 할 점 |
|---|---|---|
INSERT IGNORE |
중복 행을 버리고 경고로 강등 | 1062뿐 아니라 NULL 위반·외래키 참조 오류도 함께 무시됨. 잘못된 값이 "가장 가까운 값"으로 보정될 수 있음 |
INSERT ... ON DUPLICATE KEY UPDATE |
충돌 시 기존 행을 UPDATE | 영향 행 수가 삽입 1 / 갱신 2 / 값 동일 0. 유니크 인덱스가 여러 개면 "사용을 피하라"고 매뉴얼이 권고 |
REPLACE |
기존 행 DELETE 후 INSERT | 명시하지 않은 컬럼은 기본값으로 초기화. INSERT와 DELETE 권한 모두 필요. 반환 카운트가 1보다 크면 삭제가 일어난 것 |
INSERT IGNORE로 넘어간 행은 직후 SHOW WARNINGS;로 확인하세요. REPLACE는 PRIMARY KEY나 UNIQUE 인덱스가 있을 때만 의미가 있습니다.
자주 놓치는 원인 3가지
- NULL은 중복으로 안 잡힙니다. 공식 문서는 "UNIQUE 인덱스는 NULL 허용 컬럼에 여러 개의 NULL을 허용한다"고 명시합니다.
- AUTO_INCREMENT에 값을 직접 섞어 넣을 때입니다. 매뉴얼은 최근 시퀀스가 100일 때
(1,'a'), (NULL,'b'), (101,'c')를 넣으면 NULL 행에 101이 배정돼(101,'c')가 실패한다고 예시를 듭니다. - "재시작하면 카운터가 초기화된다"는 옛말입니다. InnoDB는 최대값을 저장해 재시작 후에도 유지하며, 비정상 종료 시에만 이전 값이 재사용될 수 있습니다.
주의사항
- InnoDB 같은 트랜잭션 엔진은 제약 위반 시 해당 문장을 자동 롤백합니다. 비트랜잭션 엔진은 오류 행에서 멈추고 나머지를 처리하지 않습니다.
ON DUPLICATE KEY UPDATE에IGNORE를 함께 쓰면 유니크 키가 여러 개인 테이블에서 예상과 다르게 동작할 수 있다고 매뉴얼이 경고합니다.REPLACE와 외래키 CASCADE·트리거의 상호작용은 8.4 공식 페이지에서 확인하지 못해 미확인입니다.- 기본키가 없으면 InnoDB가 첫 UNIQUE NOT NULL 인덱스를 기본키로 승격합니다.
중복이 이미 있는 테이블에 UNIQUE를 걸어야 한다면
- 위 5번 쿼리로 중복 건수를 먼저 셉니다.
- MySQL은 UNIQUE 인덱스 생성 시 중복 값이 없는지 검사하고 PRIMARY KEY면 NULL 여부도 검사합니다. 중복이 남아 있으면 실패하며, 이때의 정확한 에러 번호는 매뉴얼에 없어 미확인입니다.
- 과거의
ALTER IGNORE TABLE ... ADD UNIQUE는 8.4 문법에 존재하지 않습니다. 중복을 먼저 정리해야 합니다. - 매뉴얼은 기본키를 테이블 생성 시 정의하는 것이 최선이라고 권고합니다.
자주 묻는 질문
Q1. 1062와 1061은 뭐가 다른가요?
1062는 데이터 값 중복, 1061(SQLSTATE 42000)은 인덱스 이름 중복입니다. 마이그레이션 스크립트를 두 번 돌렸을 때 자주 뜹니다.
Q2. 영향 행 수가 2로 나오는데 정상인가요?
정상입니다. ON DUPLICATE KEY UPDATE는 삽입 1, 갱신 2, 값이 같으면 0을 반환합니다.
Q3. 대소문자만 다른 값인데 왜 중복인가요?
MySQL 8.x 기본 콜레이션 utf8mb4_0900_ai_ci가 대소문자를 구분하지 않기 때문입니다. 참고로 utf8mb4_0900_bin은 NO PAD라 'a'와 'a '를 다른 값으로 봅니다.
함께 보면 좋은 글
- MySQL Access denied for user 해결: 계정@호스트·SHOW GRANTS 점검
- MySQL Too many connections 1040 오류 해결: PROCESSLIST로 원인 진단
- Spring Boot Failed to configure a DataSource 해결: JDBC URL·드라이버 점검
공식 출처
- MySQL 8.4 Error Message Reference: server-error-reference.html
- PRIMARY KEY and UNIQUE Index Constraints: constraint-primary-key.html
- INSERT ... ON DUPLICATE KEY UPDATE: insert-on-duplicate.html
- REPLACE 구문: replace.html
- InnoDB AUTO_INCREMENT Handling: innodb-auto-increment-handling.html
'IT > DB' 카테고리의 다른 글
| MySQL Access denied for user 해결: 계정@호스트·비밀번호·SHOW GRANTS 점검 순서 (MySQL 8.4) (0) | 2026.09.04 |
|---|---|
| PostgreSQL remaining connection slots 오류 해결: pg_stat_activity로 원인 진단 (0) | 2026.08.10 |
| MySQL Too many connections 1040 오류 해결: PROCESSLIST로 원인 진단 (0) | 2026.08.06 |
| 오라클 기본자료형 (0) | 2020.02.26 |
| 이클립스 오라클 연동 OJDBC (0) | 2020.02.20 |
댓글