2026년 09월 22일 | DBMS Error 가이드
이 글에서 다루는 내용
ORA-14096 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
ORA-14096 tables in ALTER TABLE EXCHANGE PARTITION must have the same number of columns 는?
ORA-14096 에러는 ALTER TABLE ... EXCHANGE PARTITION 구문을 실행할 때, 파티션 테이블과 교환 대상 일반 테이블의 컬럼 수가 일치하지 않을 경우 발생하는 에러입니다. Oracle의 파티션 교환(Partition Exchange) 기능은 두 테이블의 구조가 완전히 동일해야만 정상적으로 작동하며, 컬럼 수, 데이터 타입, 컬럼 순서까지 모두 일치해야 합니다. 대용량 데이터 마이그레이션이나 데이터 웨어하우스 환경에서 파티션 스왑(Partition Swap) 작업을 수행할 때 자주 마주치는 에러로, 사전에 테이블 구조를 정확히 검증하지 않으면 운영 환경에서 예기치 않게 발생할 수 있습니다.
주요 발생 원인
1. 교환 대상 테이블의 컬럼 수 불일치
가장 직접적인 원인으로, 파티션 테이블과 일반 테이블의 컬럼 개수 자체가 다를 때 발생합니다. 예를 들어 파티션 테이블에는 10개의 컬럼이 있는데 교환 대상 테이블에는 9개 또는 11개의 컬럼이 존재하는 경우입니다. 운영 중 파티션 테이블에 컬럼이 추가되거나 삭제되었는데, 교환용 스테이징 테이블에는 해당 변경이 반영되지 않은 상황에서 흔히 발생합니다.
2. 스테이징 테이블 생성 시 잘못된 DDL 사용
교환용 일반 테이블을 생성할 때 CREATE TABLE AS SELECT(CTAS) 구문을 잘못 사용하거나, 특정 컬럼만 선택하여 생성한 경우 컬럼 수가 맞지 않게 됩니다. 또한 오래된 DDL 스크립트를 재사용하거나, 수동으로 테이블을 생성하는 과정에서 일부 컬럼을 누락하는 실수가 발생하기도 합니다. 이 경우 에러 메시지만으로는 정확히 어떤 컬럼이 문제인지 파악하기 어렵기 때문에 반드시 메타데이터를 조회하여 확인해야 합니다.
3. 파티션 테이블의 숨겨진 컬럼(Hidden Column) 또는 가상 컬럼(Virtual Column) 존재
Oracle 11g 이상에서는 가상 컬럼(Virtual Column)이나 숨겨진 컬럼이 존재할 수 있으며, 이 경우 DESC 명령어나 단순 쿼리로는 해당 컬럼이 보이지 않아 컬럼 수를 잘못 파악할 수 있습니다. 특히 함수 기반 인덱스(Function-Based Index)를 생성할 때 내부적으로 숨겨진 가상 컬럼이 추가될 수 있으며, 교환 대상 테이블에는 이 컬럼이 존재하지 않아 ORA-14096이 발생하는 경우가 있습니다. ALL_TAB_COLS 또는 USER_TAB_COLS 뷰를 통해 숨겨진 컬럼까지 포함하여 조회해야 정확한 컬럼 수를 파악할 수 있습니다.
해결 방법
1단계: 두 테이블의 컬럼 수 비교 확인
먼저 파티션 테이블과 교환 대상 테이블의 컬럼 정보를 조회하여 차이를 확인합니다.
-- 파티션 테이블의 컬럼 목록 조회 (숨겨진 컬럼 포함)
SELECT column_id, column_name, data_type, data_length, hidden_column, virtual_column
FROM user_tab_cols
WHERE table_name = 'SALES_PART' -- 파티션 테이블명
ORDER BY column_id;
-- 교환 대상 일반 테이블의 컬럼 목록 조회
SELECT column_id, column_name, data_type, data_length, hidden_column, virtual_column
FROM user_tab_cols
WHERE table_name = 'SALES_STAGE' -- 교환 대상 테이블명
ORDER BY column_id;
-- 두 테이블의 컬럼 수 비교
SELECT
(SELECT COUNT(*) FROM user_tab_cols WHERE table_name = 'SALES_PART') AS part_col_count,
(SELECT COUNT(*) FROM user_tab_cols WHERE table_name = 'SALES_STAGE') AS stage_col_count
FROM dual;
2단계: 누락된 컬럼 식별
어떤 컬럼이 다른지 정확하게 찾아냅니다.
-- 파티션 테이블에는 있으나 스테이징 테이블에는 없는 컬럼
SELECT column_name, data_type, data_length, data_precision, nullable
FROM user_tab_cols
WHERE table_name = 'SALES_PART'
MINUS
SELECT column_name, data_type, data_length, data_precision, nullable
FROM user_tab_cols
WHERE table_name = 'SALES_STAGE';
-- 스테이징 테이블에는 있으나 파티션 테이블에는 없는 컬럼
SELECT column_name, data_type, data_length, data_precision, nullable
FROM user_tab_cols
WHERE table_name = 'SALES_STAGE'
MINUS
SELECT column_name, data_type, data_length, data_precision, nullable
FROM user_tab_cols
WHERE table_name = 'SALES_PART';
3단계-A: 스테이징 테이블에 누락된 컬럼 추가
파티션 테이블에만 있는 컬럼을 교환 대상 테이블에 추가하여 컬럼 수를 일치시킵니다.
-- 누락된 컬럼 추가 예제
ALTER TABLE sales_stage ADD (
region_code VARCHAR2(10),
last_modified DATE DEFAULT SYSDATE
);
-- 컬럼 추가 후 파티션 교환 재시도
ALTER TABLE sales_part
EXCHANGE PARTITION p_2024_q1
WITH TABLE sales_stage
WITHOUT VALIDATION;
3단계-B: 파티션 테이블 구조 기반으로 스테이징 테이블 재생성
구조 불일치가 심각한 경우 스테이징 테이블을 새로 생성하는 것이 가장 확실한 방법입니다.
-- 기존 스테이징 테이블 삭제
DROP TABLE sales_stage PURGE;
-- 파티션 테이블의 구조를 그대로 복사하여 빈 스테이징 테이블 생성
CREATE TABLE sales_stage
AS
SELECT * FROM sales_part WHERE 1 = 0;
-- 또는 특정 파티션의 데이터를 포함하여 생성
CREATE TABLE sales_stage
AS
SELECT * FROM sales_part PARTITION (p_2024_q1);
-- 이제 파티션 교환 수행
ALTER TABLE sales_part
EXCHANGE PARTITION p_2024_q1
WITH TABLE sales_stage
INCLUDING INDEXES
WITHOUT VALIDATION;
3단계-C: 가상 컬럼(Virtual Column) 문제 해결
파티션 테이블에 가상 컬럼이 존재하는 경우 스테이징 테이블에도 동일하게 추가해야 합니다.
-- 가상 컬럼 확인
SELECT column_name, data_default, virtual_column
FROM user_tab_cols
WHERE table_name = 'SALES_PART'
AND virtual_column = 'YES';
-- 스테이징 테이블에 가상 컬럼 추가
ALTER TABLE sales_stage
ADD (annual_revenue AS (monthly_revenue * 12));
-- 파티션 교환 재시도
ALTER TABLE sales_part
EXCHANGE PARTITION p_2024_q1
WITH TABLE sales_stage;
예방 방법
1. 파티션 교환 전 자동 구조 검증 스크립트 실행
파티션 교환 작업을 수행하기 전에 두 테이블의 구조를 자동으로 비교하는 검증 스크립트를 표준 절차로 정착시켜야 합니다. 단순히 컬럼 수만 비교하는 것이 아니라 데이터 타입, 컬럼 순서, 가상 컬럼 여부까지 모두 포함하는 종합적인 검증 로직을 배포 전 체크리스트에 포함시키는 것이 좋습니다.
-- 배포 전 검증용 구조 비교 프로시저 예제
CREATE OR REPLACE PROCEDURE validate_exchange_tables (
p_part_table IN VARCHAR2,
p_stage_table IN VARCHAR2
) AS
v_part_count NUMBER;
v_stage_count NUMBER;
BEGIN
SELECT COUNT(*) INTO v_part_count
FROM user_tab_cols
WHERE table_name = UPPER(p_part_table);
SELECT COUNT(*) INTO v_stage_count
FROM user_tab_cols
WHERE table_name = UPPER(p_stage_table);
IF v_part_count <> v_stage_count THEN
RAISE_APPLICATION_ERROR(-20001,
'Column count mismatch: ' || p_part_table || ' has ' || v_part_count ||
' columns, ' || p_stage_table || ' has ' || v_stage_count || ' columns.');
ELSE
DBMS_OUTPUT.PUT_LINE('Validation passed: Both tables have ' || v_part_count || ' columns.');
END IF;
END;
/
-- 사용 예
EXEC validate_exchange_tables('SALES_PART', 'SALES_STAGE');
2. 스테이징 테이블을 항상 파티션 테이블로부터 파생하여 생성
스테이징 테이블을 직접 DDL로 작성하지 말고, 반드시 원본 파티션 테이블의 구조를 기반으로 CREATE TABLE AS SELECT ... WHERE 1=0 패턴을 사용하여 생성하는 것을 팀 내 표준으로 정해야 합니다. 이렇게 하면 파티션 테이블의 구조 변경이 발생했을 때 스테이징 테이블을 재생성하는 것만으로 항상 최신 구조를 유지할 수 있어 구조 불일치를 근본적으로 예방할 수 있습니다.
관련 에러
- ORA-14097:
ALTER TABLE EXCHANGE PARTITION에서 컬럼의 데이터 타입 또는 길이가 일치하지 않을 때 발생합니다. ORA-14096이 컬럼 수 문제라면, ORA-14097은 컬럼 타입 불일치 문제입니다. - ORA-14098: 파티션 테이블과 교환 대상 테이블 간 ROW MOVEMENT 설정이 일치하지 않을 때 발생합니다.
- ORA-14019: 지정된 파티션이 존재하지 않거나 잘못된 파티션 경계값을 지정했을 때 발생합니다.
- ORA-14400: 삽입하려는 값이 파티션 경계 범위를 벗어났을 때 발생하며, 파티션 교환 후 데이터 재삽입 시 나타날 수 있습니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.