2026년 07월 11일 | DBMS Error 가이드
이 글에서 다루는 내용
ORA-01408 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
ORA-01408 such column list already indexed 는?
ORA-01408 에러는 Oracle 데이터베이스에서 인덱스를 생성하려고 할 때, 동일한 컬럼 조합에 대한 인덱스가 이미 존재할 경우 발생하는 에러입니다. 쉽게 말해, 같은 컬럼(또는 컬럼들의 조합)을 대상으로 하는 인덱스가 이미 테이블에 정의되어 있는 상황에서 중복 인덱스를 생성하려 할 때 Oracle이 이를 거부하며 발생합니다. 이 에러는 주로 대규모 프로젝트에서 여러 DBA나 개발자가 협업할 때, 또는 스크립트를 반복 실행하거나 마이그레이션 작업 중 기존 인덱스를 확인하지 않고 무작정 CREATE INDEX를 실행할 때 빈번하게 발생합니다.
주요 발생 원인
1. 동일한 컬럼 조합으로 인덱스 중복 생성 시도
가장 흔한 원인으로, 이미 특정 컬럼이나 복합 컬럼 조합에 대해 인덱스가 존재하는데 동일한 컬럼 리스트로 새로운 인덱스를 생성하려 할 때 발생합니다. 예를 들어, EMP(DEPT_ID, JOB_ID) 컬럼 조합으로 이미 인덱스가 존재하는 상태에서 동일 컬럼 조합으로 다시 CREATE INDEX를 실행하면 Oracle은 ORA-01408을 반환합니다. 여러 팀이 병렬로 작업하거나 배포 스크립트가 멱등성(idempotency)을 보장하지 않을 때 자주 겪는 상황입니다.
2. PRIMARY KEY 또는 UNIQUE CONSTRAINT에 의해 자동 생성된 인덱스와 충돌
Oracle은 PRIMARY KEY 또는 UNIQUE CONSTRAINT를 생성할 때 자동으로 해당 컬럼에 대한 인덱스를 생성합니다. 개발자나 DBA가 이 사실을 인지하지 못하고 같은 컬럼에 별도의 인덱스를 명시적으로 생성하려 하면 ORA-01408이 발생합니다. 특히 데이터 모델 변경 작업이나 레거시 시스템 리팩토링 과정에서 제약 조건이 이미 인덱스를 암묵적으로 생성했다는 사실을 놓치는 경우가 많습니다.
3. 마이그레이션 또는 배포 스크립트의 반복 실행
CI/CD 파이프라인이나 데이터베이스 배포 스크립트를 반복적으로 실행하는 환경에서 DROP IF EXISTS 또는 사전 존재 여부 확인 없이 CREATE INDEX 구문을 그대로 실행할 때 발생합니다. 한 번 성공적으로 실행된 스크립트를 두 번째 실행하면 이미 인덱스가 생성된 상태이므로 ORA-01408이 발생하게 됩니다. 자동화 환경에서는 이 에러가 배포 파이프라인 전체를 중단시킬 수 있어 특히 주의가 필요합니다.
해결 방법
원인 1 해결: 기존 인덱스 확인 후 처리
먼저 해당 테이블에 어떤 인덱스가 존재하는지 확인합니다.
-- 특정 테이블의 모든 인덱스 및 컬럼 확인
SELECT
i.INDEX_NAME,
i.INDEX_TYPE,
i.UNIQUENESS,
ic.COLUMN_NAME,
ic.COLUMN_POSITION
FROM
USER_INDEXES i
JOIN USER_IND_COLUMNS ic ON i.INDEX_NAME = ic.INDEX_NAME
WHERE
i.TABLE_NAME = 'EMP'
ORDER BY
i.INDEX_NAME, ic.COLUMN_POSITION;
중복 인덱스가 확인되면, 기존 인덱스를 삭제하거나 새 인덱스 생성을 포기합니다.
-- 기존 중복 인덱스 삭제 후 새 인덱스 생성
DROP INDEX IDX_EMP_DEPT_JOB;
CREATE INDEX IDX_EMP_DEPT_JOB_NEW
ON EMP (DEPT_ID, JOB_ID);
원인 2 해결: 제약 조건으로 생성된 인덱스 확인
PRIMARY KEY, UNIQUE CONSTRAINT로 인해 자동 생성된 인덱스를 먼저 조회합니다.
-- 제약 조건으로 인해 생성된 인덱스 확인
SELECT
c.CONSTRAINT_NAME,
c.CONSTRAINT_TYPE,
c.INDEX_NAME,
cc.COLUMN_NAME
FROM
USER_CONSTRAINTS c
JOIN USER_CONS_COLUMNS cc ON c.CONSTRAINT_NAME = cc.CONSTRAINT_NAME
WHERE
c.TABLE_NAME = 'EMP'
AND c.CONSTRAINT_TYPE IN ('P', 'U');
이미 UNIQUE 인덱스가 존재하는 경우, 별도의 인덱스 생성 없이 해당 인덱스를 재활용합니다. 만약 성능상의 이유로 인덱스를 분리해야 한다면 컬럼 순서나 포함 컬럼을 다르게 구성하여 차별화합니다.
-- 기존 PK 인덱스를 활용하는 대신, 선두 컬럼 순서를 바꾸어 별도 인덱스 생성
-- (컬럼 조합이 완전히 다른 경우에만 유효)
CREATE INDEX IDX_EMP_JOB_DEPT
ON EMP (JOB_ID, DEPT_ID); -- 컬럼 순서를 다르게 설정
원인 3 해결: 멱등성(Idempotent) 스크립트 작성
배포 스크립트에서 인덱스 존재 여부를 먼저 확인하고 조건부로 생성하는 방식을 사용합니다.
-- PL/SQL을 사용하여 인덱스 존재 여부 확인 후 조건부 생성
DECLARE
v_count NUMBER;
BEGIN
SELECT COUNT(*)
INTO v_count
FROM USER_INDEXES
WHERE INDEX_NAME = 'IDX_EMP_DEPT_JOB'
AND TABLE_NAME = 'EMP';
IF v_count = 0 THEN
EXECUTE IMMEDIATE
'CREATE INDEX IDX_EMP_DEPT_JOB ON EMP (DEPT_ID, JOB_ID)';
DBMS_OUTPUT.PUT_LINE('인덱스가 성공적으로 생성되었습니다.');
ELSE
DBMS_OUTPUT.PUT_LINE('인덱스가 이미 존재합니다. 생성을 건너뜁니다.');
END IF;
END;
/
또는 예외 처리를 통해 ORA-01408을 우아하게 처리할 수도 있습니다.
-- 예외 처리를 통한 인덱스 중복 생성 무시
DECLARE
already_indexed EXCEPTION;
PRAGMA EXCEPTION_INIT(already_indexed, -1408);
BEGIN
EXECUTE IMMEDIATE
'CREATE INDEX IDX_EMP_DEPT_JOB ON EMP (DEPT_ID, JOB_ID)';
DBMS_OUTPUT.PUT_LINE('인덱스 생성 완료.');
EXCEPTION
WHEN already_indexed THEN
DBMS_OUTPUT.PUT_LINE('해당 컬럼 조합의 인덱스가 이미 존재합니다. 건너뜁니다.');
END;
/
예방 방법
1. 인덱스 생성 전 표준화된 사전 확인 쿼리 실행 의무화
모든 DDL 배포 전에 반드시 USER_INDEXES 및 USER_IND_COLUMNS 뷰를 조회하여 동일 컬럼 조합의 인덱스 존재 여부를 확인하는 절차를 팀 내 표준으로 정립해야 합니다. DBA 검토 체크리스트에 인덱스 중복 검사 항목을 필수 항목으로 추가하고, 가능하면 아래와 같은 공통 유틸리티 프로시저를 팀 전체가 공유하여 사용하도록 권장합니다.
-- 전체 스키마의 중복 가능성이 있는 인덱스 조회 유틸리티
SELECT
a.TABLE_NAME,
a.INDEX_NAME AS INDEX_A,
b.INDEX_NAME AS INDEX_B,
a.COLUMN_LIST
FROM (
SELECT
ic.TABLE_NAME,
ic.INDEX_NAME,
LISTAGG(ic.COLUMN_NAME, ',')
WITHIN GROUP (ORDER BY ic.COLUMN_POSITION) AS COLUMN_LIST
FROM USER_IND_COLUMNS ic
GROUP BY ic.TABLE_NAME, ic.INDEX_NAME
) a
JOIN (
SELECT
ic.TABLE_NAME,
ic.INDEX_NAME,
LISTAGG(ic.COLUMN_NAME, ',')
WITHIN GROUP (ORDER BY ic.COLUMN_POSITION) AS COLUMN_LIST
FROM USER_IND_COLUMNS ic
GROUP BY ic.TABLE_NAME, ic.INDEX_NAME
) b
ON a.TABLE_NAME = b.TABLE_NAME
AND a.COLUMN_LIST = b.COLUMN_LIST
AND a.INDEX_NAME < b.INDEX_NAME
ORDER BY a.TABLE_NAME;
2. CI/CD 파이프라인에 멱등성 보장 원칙 적용
데이터베이스 배포 스크립트는 반드시 멱등성을 보장하도록 작성해야 합니다. 즉, 동일한 스크립트를 여러 번 실행하더라도 에러 없이 동일한 결과를 내야 합니다. Flyway, Liquibase 등의 데이터베이스 마이그레이션 도구를 도입하면 각 스크립트의 실행 이력을 추적하여 이미 적용된 스크립트를 재실행하지 않도록 자동으로 관리할 수 있으므로, 이런 종류의 에러를 구조적으로 예방할 수 있습니다.
관련 에러
- ORA-00955: 동일한 이름의 객체가 이미 존재할 때 발생하는 에러로, ORA-01408과 함께 DDL 작업 시 자주 동반됩니다.
- ORA-02261: 테이블에 이미 동일한 UNIQUE 또는 PRIMARY KEY 제약 조건이 존재할 때 발생하며, 내부적으로 ORA-01408과 유사한 맥락에서 발생합니다.
- ORA-01430: 추가하려는 컬럼이 이미 테이블에 존재할 때 발생하는 에러로, 중복 DDL 실행 시 ORA-01408과 함께 나타날 수 있습니다.
- ORA-00054: 테이블에 대한 잠금이 걸려 있어 DDL 작업이 불가능할 때 발생하며, 인덱스 재생성 작업 중 자주 마주치는 에러입니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.