본문 바로가기
IT

ORA-00001 unique constraint violated — PK·UK 중복 에러 원인·해결·예방

by 샤나엘 2026. 5. 12.
반응형

ORA-00001 unique constraint violated — PK·UK 중복 에러 원인·해결·예방

Oracle Database에서 INSERT나 UPDATE를 실행하다가 ORA-00001: unique constraint violated 에러를 만나는 경우가 있다. 단순한 중복 입력 외에도 MERGE 동시성 충돌이나 시퀀스 동기화 깨짐 같은 운영 이슈에서도 같은 에러로 나타나기 때문에 원인을 좁히기 위해서는 제약 종류와 위반 컬럼을 정확히 식별해야 한다.

 

본 글은 ORA-00001의 정확한 의미와 자주 발생하는 6가지 케이스, 단계별 디버깅, 회피 패턴(MERGE·DUP_VAL_ON_INDEX), UNIQUE 제약·인덱스·NULL 처리까지 정리한 트러블슈팅 자료다.

 

ORA-00001

이 글의 구성

 

01에러 메시지와 카테고리
02발생 원인 6가지
03재현 시나리오와 코드 예제
047단계 디버깅 순서
05회피 패턴 (NOT EXISTS·MERGE·DUP_VAL_ON_INDEX)
06UNIQUE 제약·인덱스 차이와 NULL 처리
Q&A자주 묻는 질문 5가지

01 에러 메시지와 카테고리

ORA-00001은 INSERT·UPDATE·MERGE가 PRIMARY KEY 또는 UNIQUE 제약(UNIQUE INDEX 포함)으로 보호된 컬럼에 중복 값을 생성하려고 시도할 때 발생한다.

항목 내용
에러 코드 ORA-00001
영문 메시지 unique constraint (schema.constraint_name) violated
23ai 이상 확장 에러 메시지에 테이블명과 위반 컬럼 목록까지 함께 표시 (23ai 신규 ERROR_MESSAGE_DETAILS 파라미터)
Cause (공식) An UPDATE or INSERT statement attempted to insert a duplicate key. For Trusted Oracle configured in DBMS MAC mode, you may see this message if a duplicate entry exists at a different level.
Action (공식) Either remove the unique restriction or do not insert the key.
발생 영역 SQL INSERT·UPDATE·MERGE 및 PL/SQL 블록

핵심 관찰

ORA-00001을 빠르게 진단하는 열쇠는 에러 메시지에 포함된 constraint_name이다. ALL_CONSTRAINTS · ALL_CONS_COLUMNS · ALL_INDEXES를 이 이름으로 조회하면 위반된 컬럼과 제약 종류를 즉시 확인할 수 있다.


02 발생 원인 6가지

실무에서 ORA-00001은 다음 6가지 상황에서 가장 자주 발생한다.

원인 1 — PRIMARY KEY 중복 INSERT

이미 존재하는 PK 값으로 INSERT를 시도할 때 발생한다. 시퀀스를 사용하지 않고 직접 PK를 지정하거나, ID 매핑 로직이 잘못된 경우 자주 나타난다.

원인 2 — UNIQUE 제약 중복 INSERT

이메일·사용자명·주민번호처럼 UNIQUE로 선언된 컬럼에 중복 값을 넣을 때 발생한다. 회원가입·중복 체크 누락 시 흔하다.

원인 3 — 복합 UNIQUE 제약 위반

두 개 이상의 컬럼이 함께 UNIQUE로 선언된 경우 같은 조합이 중복되면 발생한다. 주문 테이블의 (고객ID, 주문일자) 같은 조합이 대표적이다.

원인 4 — UPDATE로 UNIQUE 위반

기존 다른 행이 사용 중인 값으로 UPDATE를 시도할 때 발생한다. INSERT뿐 아니라 UPDATE도 제약 검사를 받기 때문에 자주 놓친다.

원인 5 — MERGE 시 동시성 충돌

두 세션이 동시에 같은 키로 MERGE를 실행할 때, 한 세션이 INSERT를 미커밋 상태로 유지하면 다른 세션은 그 행을 못 보고 INSERT 분기로 진입했다가 커밋 시점에 ORA-00001을 만난다.

원인 6 — 시퀀스 동기화 깨짐

데이터 마이그레이션이나 export/import 후 시퀀스 NEXTVAL이 테이블의 MAX(PK)보다 작을 때 발생한다. INSERT가 시퀀스 다음 값을 사용하지만 이미 그 값이 테이블에 존재한다.


03 재현 시나리오와 코드 예제

각 원인을 실제 코드로 살펴본다.

원인 1 — PRIMARY KEY 중복

CREATE TABLE emp (id NUMBER PRIMARY KEY, name VARCHAR2(50));

-- ✓ 첫 INSERT 성공
INSERT INTO emp VALUES (1, 'Alice');

-- ✗ 같은 PK로 두 번째 INSERT
INSERT INTO emp VALUES (1, 'Bob');
-- ORA-00001: unique constraint (SCOTT.SYS_C00xxx) violated

원인 2 — UNIQUE 제약 중복

ALTER TABLE emp ADD CONSTRAINT emp_email_uk UNIQUE(email);

INSERT INTO emp(id, email) VALUES (2, 'a@x.com');

-- ✗ 같은 이메일로 INSERT
INSERT INTO emp(id, email) VALUES (3, 'a@x.com');
-- ORA-00001: unique constraint (SCOTT.EMP_EMAIL_UK) violated

원인 3 — 복합 UNIQUE 위반

ALTER TABLE orders ADD CONSTRAINT ord_uk
  UNIQUE(customer_id, order_date);

INSERT INTO orders VALUES (10, DATE '2026-05-11', 100);

-- ✗ 같은 조합 INSERT
INSERT INTO orders VALUES (10, DATE '2026-05-11', 200);
-- ORA-00001: unique constraint (SCOTT.ORD_UK) violated

원인 4 — UPDATE로 UNIQUE 위반

-- 현재 상태: id=2 → 'a@x.com', id=3 → 'b@x.com'

-- ✗ 이미 id=2가 사용 중인 이메일로 UPDATE
UPDATE emp SET email = 'a@x.com' WHERE id = 3;
-- ORA-00001: unique constraint (SCOTT.EMP_EMAIL_UK) violated

원인 5 — MERGE 동시성 충돌

-- 세션 A: id=1로 INSERT 후 미커밋
INSERT INTO target VALUES (1, 'X');

-- 세션 B: 같은 시점 MERGE 시도 → A의 미커밋 행 못 봄
MERGE INTO target t
USING (SELECT 1 id, 'Y' val FROM dual) s
ON (t.id = s.id)
WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val);
-- 세션 A 커밋 후 세션 B 커밋 시점에 ORA-00001 발생

원인 6 — 시퀀스 동기화 깨짐

-- 데이터 import 후 시퀀스가 뒤처진 상황 확인
SELECT MAX(id) FROM emp;          -- 결과: 9999
SELECT seq_emp.NEXTVAL FROM dual; -- 결과: 100

-- ✗ INSERT 시 100, 101 ... 사용 → 이미 존재 → ORA-00001

-- ✓ 해결: 시퀀스를 MAX(PK)보다 큰 값으로 점프
ALTER SEQUENCE seq_emp INCREMENT BY 9900;
SELECT seq_emp.NEXTVAL FROM dual; -- 10000
ALTER SEQUENCE seq_emp INCREMENT BY 1;

04 7단계 디버깅 순서

단계별 디버깅 순서

 

01에러 메시지의 constraint_name을 정확히 추출한다.
02ALL_CONSTRAINTS로 제약 종류(P·U)·테이블·상태를 확인한다.
03ALL_CONS_COLUMNS로 제약에 포함된 컬럼 목록을 확인한다.
0423ai 이상이면 에러 메시지에 위반 값이 표시되는지 확인 (ERROR_MESSAGE_DETAILS).
05기존 데이터 SELECT로 중복 행을 확인하고 비즈니스 의도를 검토한다.
06PK 사용 시 시퀀스 NEXTVAL과 MAX(PK)를 비교해 동기화를 점검한다.
07MERGE 환경이면 동시 세션·미커밋 트랜잭션을 V$LOCK·V$TRANSACTION으로 확인한다.
-- 제약 종류와 상태 확인
SELECT owner, constraint_name, constraint_type, table_name,
       status, deferrable, deferred, validated, index_name
  FROM all_constraints
 WHERE constraint_name = 'EMP_EMAIL_UK';
-- constraint_type: P=Primary Key, U=Unique, C=Check, R=Foreign Key

-- 제약 컬럼 확인
SELECT constraint_name, column_name, position
  FROM all_cons_columns
 WHERE constraint_name = 'EMP_EMAIL_UK';

-- 백킹 인덱스 확인
SELECT index_name, uniqueness, table_name, status
  FROM all_indexes
 WHERE table_name = 'EMP';

05 회피 패턴

ORA-00001을 미리 방지하거나 우아하게 처리하는 표준 패턴 3가지가 있다.

패턴 1 — INSERT … WHERE NOT EXISTS

-- 동일 이메일이 없을 때만 INSERT
INSERT INTO emp(id, email)
SELECT 3, 'a@x.com' FROM dual
 WHERE NOT EXISTS (
   SELECT 1 FROM emp WHERE email = 'a@x.com'
 );

패턴 2 — MERGE INTO (UPSERT)

Oracle은 MySQL의 INSERT ... ON DUPLICATE KEY UPDATE 같은 문법을 지원하지 않는다. UPSERT는 MERGE로 구현한다.

MERGE INTO emp t
USING (SELECT 3 id, 'a@x.com' email FROM dual) s
ON (t.id = s.id)
WHEN MATCHED THEN UPDATE SET t.email = s.email
WHEN NOT MATCHED THEN INSERT (id, email) VALUES (s.id, s.email);

패턴 3 — DUP_VAL_ON_INDEX 예외 처리 (PL/SQL)

BEGIN
  INSERT INTO emp(id, email) VALUES (3, 'a@x.com');
EXCEPTION
  WHEN DUP_VAL_ON_INDEX THEN
    -- 이미 존재 → UPDATE로 전환 또는 로깅 후 무시
    UPDATE emp SET email = 'a@x.com' WHERE id = 3;
END;
/

DUP_VAL_ON_INDEX는 ORA-00001에 매핑된 미리 정의된 예외(predefined exception)다. MERGE 동시성 이슈가 잦은 환경에서는 INSERT를 먼저 시도하고 실패 시 UPDATE로 전환하는 이 패턴이 MERGE보다 안전하게 작동한다.


06 UNIQUE 제약·인덱스 차이와 NULL 처리

ORA-00001을 다룰 때 자주 혼동되는 두 가지 개념이 UNIQUE CONSTRAINT와 UNIQUE INDEX, 그리고 NULL 처리다.

항목 UNIQUE CONSTRAINT UNIQUE INDEX
목적 데이터 무결성 선언 빠른 조회 + 부수적 유일성
메타데이터 위치 ALL_CONSTRAINTS ALL_INDEXES
DEFERRABLE 지원 가능 (INITIALLY DEFERRED) 불가
활성/비활성 ALTER ... DISABLE/ENABLE DROP/CREATE만 가능
FK 참조 대상 가능 불가

NULL 처리 규칙

  • UNIQUE 제약과 UNIQUE INDEX 모두 NULL을 "유일하지 않음"으로 취급한다. 따라서 NULL은 같은 컬럼에 여러 행이 존재할 수 있다.
  • 복합 UNIQUE(col1, col2)에서는 모든 키 컬럼이 NULL인 행만 다수 허용된다. 한 컬럼이라도 값이 있으면 유일성 검사가 적용된다.
  • "NULL을 한 행만 허용"하고 싶다면 함수 기반 인덱스로 우회한다.
-- NULL도 유일하게 만들고 싶다면 함수 기반 인덱스
CREATE UNIQUE INDEX emp_email_uix
  ON emp (NVL(email, '__NO_EMAIL__'));

제약 비활성화/활성화 (대량 적재 시)

-- 대량 적재 직전 비활성화
ALTER TABLE emp DISABLE CONSTRAINT emp_email_uk;

-- 데이터 적재 후 위반 행을 EXCEPTIONS 테이블로 받으며 재활성화
ALTER TABLE emp ENABLE VALIDATE CONSTRAINT emp_email_uk
  EXCEPTIONS INTO exceptions;

07 자주 묻는 질문 5가지

Q1Oracle에 INSERT ON DUPLICATE KEY UPDATE 같은 문법이 있나

없다. Oracle은 MySQL의 INSERT ... ON DUPLICATE KEY UPDATE를 직접 지원하지 않는다. 같은 기능이 필요하면 MERGE INTO 문으로 UPSERT를 구현하거나, PL/SQL에서 INSERT 후 DUP_VAL_ON_INDEX 예외로 UPDATE로 전환하는 패턴을 사용한다.

Q2UNIQUE 컬럼에 NULL을 여러 개 넣을 수 있나

단일 컬럼 UNIQUE는 NULL을 유일성 검사에서 제외하므로 여러 행에 NULL을 가질 수 있다. 복합 UNIQUE에서는 모든 키 컬럼이 NULL인 경우만 다수 허용되고, 한 컬럼이라도 값이 있으면 유일성 검사가 적용된다. NULL도 한 행만 허용하고 싶다면 NVL을 사용한 함수 기반 인덱스로 우회한다.

Q3MERGE가 더 안전한데 왜 DUP_VAL_ON_INDEX 패턴을 쓰나

MERGE도 동시 세션이 같은 키로 INSERT를 시도하면 ORA-00001이 발생할 수 있다. 한 세션의 미커밋 INSERT는 다른 세션이 볼 수 없기 때문이다. 이런 환경에서는 INSERT를 먼저 시도하고 실패 시 UPDATE로 전환하는 DUP_VAL_ON_INDEX 패턴이 더 안정적이다. 다만 단일 세션 작업이거나 동시성이 낮은 환경에서는 MERGE가 더 간결하다.

Q4시퀀스를 사용하는데 ORA-00001이 발생하면 어떻게 진단하나

데이터 마이그레이션이나 export/import 직후 시퀀스 NEXTVAL이 테이블의 MAX(PK)보다 작아진 경우가 흔하다. SELECT MAX(id) FROM 테이블SELECT 시퀀스.NEXTVAL FROM dual을 비교해 동기화 상태를 확인하고, ALTER SEQUENCE ... INCREMENT BY로 점프시킨 뒤 다시 1로 되돌려 동기화를 맞춘다.

Q5대량 데이터 적재 시 ORA-00001을 줄이는 방법은

적재 전에 UNIQUE 제약을 일시 비활성화(ALTER TABLE ... DISABLE CONSTRAINT)하고, 적재 후 ENABLE VALIDATE CONSTRAINT ... EXCEPTIONS INTO exceptions로 활성화하면 중복 행을 별도 테이블로 분리해 받을 수 있다. 또는 적재 데이터를 사전 정제하거나 MERGE를 사용해 중복을 흡수하는 방식도 있다.


08 결론

ORA-00001은 트러블슈팅 측면에서 비교적 명확한 에러다. 핵심은 에러 메시지의 제약 이름을 출발점으로 ALL_CONSTRAINTS·ALL_CONS_COLUMNS·ALL_INDEXES를 차례로 조회해 위반 컬럼과 제약 종류를 확인하는 것이다.

실무에서 이 에러를 줄이려면 다음 세 가지 습관이 도움이 된다.

 

첫째, 중복 가능성이 있는 INSERT는 처음부터 MERGE 또는 PL/SQL의 DUP_VAL_ON_INDEX 패턴으로 안전하게 처리한다.

둘째, 데이터 마이그레이션 후에는 시퀀스 NEXTVAL과 MAX(PK)를 비교하는 검증 단계를 표준화한다.

셋째, 대량 적재 시에는 제약 비활성화·재활성화 패턴과 EXCEPTIONS 테이블을 적극 활용해 중복 행을 분리 처리한다.

ORA-00001의 진단 출발점은 에러 메시지의 제약 이름이다. ALL_CONSTRAINTS·ALL_CONS_COLUMNS·ALL_INDEXES 3개 뷰로 위반 컬럼을 즉시 추적할 수 있다.

 

— 회피는 MERGE 또는 DUP_VAL_ON_INDEX 예외 처리가 표준

트러블슈팅 체크리스트

 

01에러 메시지의 제약 이름을 정확히 추출한다.
02ALL_CONSTRAINTS·ALL_CONS_COLUMNS로 제약 종류와 컬럼을 확인한다.
03기존 데이터를 SELECT로 확인해 중복 행을 식별한다.
04PK 사용 시 시퀀스 NEXTVAL과 MAX(PK)를 비교해 동기화를 확인한다.
05MERGE 동시성 충돌이 의심되면 DUP_VAL_ON_INDEX 패턴으로 전환한다.
06NULL 처리 의도가 명확한지 확인하고, 필요하면 함수 기반 인덱스를 사용한다.
07대량 적재 시 제약 비활성화·EXCEPTIONS INTO 패턴을 활용한다.

본 글은 Oracle Database에서 발생하는 ORA-00001 에러의 일반적 원인과 해결 방법을 정리한 자료다. 시퀀스·MERGE 동시성·제약 비활성화는 운영 환경마다 영향이 다르므로 적용 전 테스트 환경에서 충분히 검증한다. Oracle 공식 문서와 최신 패치 노트를 함께 참고하면 더 안정적인 트러블슈팅이 가능하다.

 

#ORA00001 #uniqueconstraint #오라클에러 #Oracle #OracleDatabase #PRIMARYKEY #UNIQUE #MERGE #UPSERT #DUPVALONINDEX #PLSQL #시퀀스 #ALLCONSTRAINTS #DB트러블슈팅 #SQL제약

반응형

댓글