Oracle ORA-14016 오류 원인과 해결 방법 완벽 가이드

ORA-14016
2026년 09월 19일 | DBMS Error 가이드

이 글에서 다루는 내용

ORA-14016 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.

ORA-14016 underlying table of a local index must be partitioned 는?

ORA-14016 에러는 로컬 인덱스(Local Index)를 생성하려는 대상 테이블이 파티션 테이블이 아닐 때 발생하는 오류입니다. Oracle에서 로컬 인덱스는 파티션 테이블의 파티션 구조와 1:1로 대응되는 인덱스로, 반드시 파티션 테이블 위에만 생성할 수 있습니다. 즉, 일반(Non-Partitioned) 테이블에 LOCAL 키워드를 사용하여 인덱스를 생성하려 할 경우 이 에러가 반드시 발생하게 됩니다.


주요 발생 원인

  • 일반 테이블에 LOCAL 인덱스 생성 시도

가장 흔한 원인으로, 개발자 또는 DBA가 파티셔닝 여부를 확인하지 않고 CREATE INDEX ... LOCAL 구문을 사용하는 경우입니다. 특히 타 시스템에서 DDL 스크립트를 그대로 가져와 실행할 때 파티션 테이블이 아닌 일반 테이블에 동일한 스크립트를 적용하면서 빈번하게 발생합니다.

“`sql

— 에러 재현 예시: 일반 테이블에 LOCAL 인덱스 생성 시도

CREATE TABLE employees_normal (

emp_id NUMBER,

emp_name VARCHAR2(100),

hire_date DATE

);

— 아래 구문 실행 시 ORA-14016 발생

CREATE INDEX idx_emp_local ON employees_normal(hire_date) LOCAL;

— ORA-14016: underlying table of a local index must be partitioned

“`

  • DDL 스크립트 마이그레이션 또는 자동화 도구 사용 시 환경 불일치

운영 환경에서 파티션 테이블로 존재하던 객체가 개발/테스트 환경에서는 비파티션 테이블로 생성되어 있는 경우, 동일한 인덱스 생성 DDL을 실행하면 이 에러가 발생합니다. 또한 데이터 마이그레이션 툴(Oracle Data Pump, GoldenGate 등)이나 배포 자동화 스크립트가 파티션 속성을 누락하고 테이블을 생성한 뒤 LOCAL 인덱스를 생성하려 할 때도 동일하게 발생합니다.

“`sql

— 환경 간 테이블 파티션 여부 확인

SELECT table_name, partitioned

FROM user_tables

WHERE table_name = ‘EMPLOYEES_NORMAL’;

— PARTITIONED 컬럼이 ‘NO’이면 LOCAL 인덱스 생성 불가

“`

  • 파티션 테이블 변환(Conversion) 후 인덱스 재생성 누락 또는 순서 오류

파티션 테이블을 일반 테이블로 변환(DBMS_REDEFINITION 등)하거나, 반대로 일반 테이블을 파티션 테이블로 전환하는 작업 도중 인덱스를 먼저 생성하려 할 때 발생합니다. 테이블 구조 변경 작업 중 단계별 순서를 지키지 않으면 이 에러가 발생하므로, 반드시 테이블 생성 및 파티셔닝 완료 후 인덱스를 생성해야 합니다.

“`sql

— 파티션 테이블 여부 및 파티션 목록 확인

SELECT tp.table_name,

tp.partition_name,

tp.partition_position

FROM user_tab_partitions tp

WHERE tp.table_name = ‘EMPLOYEES_NORMAL’

ORDER BY tp.partition_position;

— 결과가 없으면 파티션 테이블이 아님

“`


해결 방법

해결책 1: LOCAL 대신 GLOBAL(일반) 인덱스로 변경

테이블을 파티션으로 변경하기 어려운 경우, LOCAL 키워드를 제거하고 일반 글로벌 인덱스로 생성합니다.

-- LOCAL 키워드 제거 후 일반 인덱스로 생성
CREATE INDEX idx_emp_hiredate ON employees_normal(hire_date);

-- 인덱스 생성 확인
SELECT index_name, table_name, partitioned
FROM   user_indexes
WHERE  table_name = 'EMPLOYEES_NORMAL';

해결책 2: 테이블을 파티션 테이블로 재생성 후 LOCAL 인덱스 생성

비즈니스 요건상 로컬 인덱스가 반드시 필요한 경우, 테이블을 파티션 테이블로 재생성한 후 LOCAL 인덱스를 생성합니다.

-- 파티션 테이블로 재생성 (Range Partition 예시)
CREATE TABLE employees_part (
    emp_id    NUMBER,
    emp_name  VARCHAR2(100),
    hire_date DATE
)
PARTITION BY RANGE (hire_date) (
    PARTITION p_2022 VALUES LESS THAN (DATE '2023-01-01'),
    PARTITION p_2023 VALUES LESS THAN (DATE '2024-01-01'),
    PARTITION p_2024 VALUES LESS THAN (DATE '2025-01-01'),
    PARTITION p_max  VALUES LESS THAN (MAXVALUE)
);

-- 기존 데이터 이관
INSERT INTO employees_part SELECT * FROM employees_normal;
COMMIT;

-- LOCAL 인덱스 생성 (정상 처리)
CREATE INDEX idx_emp_part_local ON employees_part(hire_date) LOCAL;

-- 로컬 인덱스 파티션 확인
SELECT index_name, partition_name, status
FROM   user_ind_partitions
WHERE  index_name = 'IDX_EMP_PART_LOCAL'
ORDER BY partition_name;

해결책 3: DBMS_REDEFINITION을 활용한 무중단 전환

운영 중인 테이블을 파티션 테이블로 전환할 때는 DBMS_REDEFINITION 패키지를 사용하여 서비스 중단 없이 작업합니다.

-- 1단계: Redefinition 가능 여부 확인
EXEC DBMS_REDEFINITION.CAN_REDEF_TABLE('HR', 'EMPLOYEES_NORMAL', DBMS_REDEFINITION.CONS_USE_PK);

-- 2단계: 임시 파티션 테이블 생성
CREATE TABLE employees_part_interim (
    emp_id    NUMBER,
    emp_name  VARCHAR2(100),
    hire_date DATE
)
PARTITION BY RANGE (hire_date) (
    PARTITION p_2022 VALUES LESS THAN (DATE '2023-01-01'),
    PARTITION p_2023 VALUES LESS THAN (DATE '2024-01-01'),
    PARTITION p_max  VALUES LESS THAN (MAXVALUE)
);

-- 3단계: Redefinition 시작
EXEC DBMS_REDEFINITION.START_REDEF_TABLE('HR', 'EMPLOYEES_NORMAL', 'EMPLOYEES_PART_INTERIM');

-- 4단계: 동기화 (선택적으로 중간 동기화 수행)
EXEC DBMS_REDEFINITION.SYNC_INTERIM_TABLE('HR', 'EMPLOYEES_NORMAL', 'EMPLOYEES_PART_INTERIM');

-- 5단계: Redefinition 완료 (테이블 스왑)
EXEC DBMS_REDEFINITION.FINISH_REDEF_TABLE('HR', 'EMPLOYEES_NORMAL', 'EMPLOYEES_PART_INTERIM');

-- 6단계: 완료 후 LOCAL 인덱스 생성
CREATE INDEX idx_emp_local_new ON employees_normal(hire_date) LOCAL;

예방 방법

  • 인덱스 생성 전 테이블 파티션 여부 자동 검증 스크립트 운영

DDL 배포 파이프라인 또는 인덱스 생성 스크립트 실행 전, 반드시 대상 테이블의 파티션 여부를 확인하는 검증 단계를 추가하는 것을 권장합니다. 아래 스크립트를 CI/CD 파이프라인이나 배포 전 점검 항목으로 활용하면 ORA-14016 에러를 사전에 방지할 수 있습니다.

“`sql

— 인덱스 생성 전 파티션 여부 검증 프로시저 예시

CREATE OR REPLACE PROCEDURE check_partition_before_local_index (

p_table_name IN VARCHAR2

) AS

v_partitioned VARCHAR2(3);

BEGIN

SELECT partitioned

INTO v_partitioned

FROM user_tables

WHERE table_name = UPPER(p_table_name);

IF v_partitioned = ‘NO’ THEN

RAISE_APPLICATION_ERROR(-20001,

‘Table ‘ || p_table_name || ‘ is NOT partitioned. LOCAL index cannot be created.’);

ELSE

DBMS_OUTPUT.PUT_LINE(‘Table ‘ || p_table_name || ‘ is partitioned. Safe to create LOCAL index.’);

END IF;

EXCEPTION

WHEN NO_DATA_FOUND THEN

RAISE_APPLICATION_ERROR(-20002, ‘Table ‘ || p_table_name || ‘ does not exist.’);

END;

/

— 사용 예시

EXEC check_partition_before_local_index(‘EMPLOYEES_NORMAL’);

“`

  • 표준 DDL 템플릿 및 코드 리뷰 프로세스 도입

팀 내 DDL 작성 표준을 문서화하고, 인덱스 생성 시 LOCAL 키워드 사용 여부와 대상 테이블의 파티션 여부를 반드시 코드 리뷰 체크리스트 항목에 포함시켜야 합니다. 환경별(개발/스테이징/운영) 테이블 구조 차이로 인한 문제를 방지하기 위해, 테이블 생성 DDL에 파티셔닝 여부를 명확히 주석으로 기재하는 컨벤션을 수립하는 것도 좋은 방법입니다.


관련 에러

  • ORA-14017: 파티션 테이블의 인덱스 파티션 관련 에러로, 로컬 인덱스 파티션 속성이 올바르지 않을 때 발생합니다.
  • ORA-14006: 파티션 이름이 잘못되었거나 존재하지 않을 때 발생하는 에러입니다.
  • ORA-14019: 파티션 경계 값이 잘못 지정되었을 때 발생하며, 파티션 테이블 생성 시 함께 주의해야 합니다.
  • ORA-02149: 지정한 파티션이 존재하지 않을 때 발생하는 에러로, 로컬 인덱스 파티션 접근 시 연관될 수 있습니다.

DBMS 에러 코드 시리즈

주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.

본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.

댓글 남기기