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

ORA-00947
2026년 06월 21일 | DBMS Error 가이드

이 글에서 다루는 내용

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

ORA-00947 not enough values 는?

ORA-00947 에러는 Oracle에서 INSERT 또는 기타 DML 문을 실행할 때 제공된 값의 수가 대상 컬럼의 수보다 적을 경우 발생하는 에러입니다. 쉽게 말해, 테이블에 데이터를 삽입하거나 다중 행을 처리할 때 컬럼 목록과 값 목록의 개수가 일치하지 않으면 Oracle 옵티마이저가 이 에러를 던지게 됩니다. 주로 INSERT INTO … VALUES 구문이나 INSERT INTO … SELECT 구문, 또는 복잡한 서브쿼리를 활용한 DML 작업에서 자주 목격되며, 개발 초기나 스키마 변경 직후에 특히 빈번하게 발생합니다.


주요 발생 원인

1. INSERT 구문에서 컬럼 수와 VALUES 수의 불일치

가장 흔한 원인으로, INSERT INTO 테이블명 (컬럼1, 컬럼2, 컬럼3) VALUES (값1, 값2) 처럼 컬럼은 3개인데 값이 2개만 입력된 경우입니다. 개발자가 컬럼 목록을 수정했지만 VALUES 절을 함께 수정하지 않거나, 반대로 VALUES 쪽을 먼저 수정하고 컬럼 목록을 빠뜨리는 경우가 실무에서 매우 자주 발생합니다. 특히 대규모 배치 INSERT 구문이나 여러 개발자가 동시에 코드를 수정하는 환경에서 이런 실수가 두드러집니다.

2. INSERT INTO … SELECT 구문에서 SELECT 컬럼 수 부족

INSERT INTO 테이블명 (컬럼1, 컬럼2, 컬럼3) SELECT 컬럼A, 컬럼B FROM 다른테이블 처럼, 삽입 대상 컬럼은 3개인데 SELECT 절에서 가져오는 컬럼이 2개뿐인 경우에도 동일한 에러가 발생합니다. 서브쿼리가 복잡해지거나 JOIN이 포함된 SELECT 문에서 컬럼을 누락하는 경우, 혹은 테이블 스키마가 변경되어 새 컬럼이 추가된 이후 기존 INSERT SELECT 구문을 업데이트하지 않으면 이 에러가 발생합니다. 운영 환경에서 스키마 변경 이후 배치 프로그램이 실패하는 대표적인 원인 중 하나입니다.

3. 다중 행 INSERT 또는 RECORD TYPE 사용 시 값 목록 누락

Oracle에서 다중 행을 한 번에 삽입하거나 PL/SQL의 RECORD TYPE, 컬렉션을 활용한 FORALL 구문을 사용할 때, 각 행에 대한 값의 수가 맞지 않으면 이 에러가 발생합니다. 특히 PL/SQL 프로시저 내부에서 동적 SQL(EXECUTE IMMEDIATE)을 사용하거나 레코드 타입을 직접 매핑하는 경우, 컬럼 정의와 레코드 필드 수가 다를 때 런타임 에러로 이어집니다. 이런 유형의 에러는 컴파일 시점에 잡히지 않고 실행 시점에 발생하므로 디버깅이 더 까다롭습니다.


해결 방법

원인 1 해결: VALUES 절의 값 수를 컬럼 수와 일치시키기

컬럼 목록과 VALUES 절의 항목 수를 정확히 맞춰주면 해결됩니다.

-- 에러 발생 예시 (컬럼 3개, 값 2개)
INSERT INTO employees (emp_id, emp_name, dept_id)
VALUES (101, 'Hong Gil-dong');  -- ORA-00947 발생!

-- 올바른 수정 예시
INSERT INTO employees (emp_id, emp_name, dept_id)
VALUES (101, 'Hong Gil-dong', 10);  -- 컬럼 3개, 값 3개로 일치

-- 컬럼 목록을 생략할 경우 테이블의 모든 컬럼 수에 맞춰야 함
-- employees 테이블이 4개 컬럼(emp_id, emp_name, dept_id, hire_date)이라면:
INSERT INTO employees
VALUES (101, 'Hong Gil-dong', 10, SYSDATE);

원인 2 해결: INSERT INTO … SELECT 구문의 SELECT 컬럼 수 맞추기

SELECT 절에서 반환하는 컬럼 수가 INSERT 대상 컬럼 수와 반드시 같아야 합니다.

-- 에러 발생 예시 (INSERT 컬럼 3개, SELECT 컬럼 2개)
INSERT INTO employees (emp_id, emp_name, dept_id)
SELECT employee_id, first_name
FROM hr.employees
WHERE department_id = 10;  -- ORA-00947 발생!

-- 올바른 수정 예시
INSERT INTO employees (emp_id, emp_name, dept_id)
SELECT employee_id, first_name, department_id
FROM hr.employees
WHERE department_id = 10;

-- 특정 컬럼에 고정값을 넣고 싶다면 리터럴 값 사용
INSERT INTO employees (emp_id, emp_name, dept_id)
SELECT employee_id, first_name, 99  -- 고정값으로 대체
FROM hr.employees
WHERE department_id = 10;

원인 3 해결: PL/SQL FORALL 및 동적 SQL에서의 수정

PL/SQL에서 RECORD TYPE이나 FORALL을 사용할 때는 필드 수를 명확히 확인하고 맞춰야 합니다.

-- 에러 발생 예시: RECORD TYPE 필드 수 불일치
DECLARE
  TYPE emp_rec IS RECORD (
    emp_id   NUMBER,
    emp_name VARCHAR2(100)
  );
  v_emp emp_rec;
BEGIN
  v_emp.emp_id   := 101;
  v_emp.emp_name := 'Hong Gil-dong';

  -- employees 테이블에 dept_id가 필수 컬럼인 경우 ORA-00947 발생 가능
  INSERT INTO employees (emp_id, emp_name, dept_id)
  VALUES (v_emp.emp_id, v_emp.emp_name);  -- 값 2개만 제공!
END;
/

-- 올바른 수정 예시
DECLARE
  TYPE emp_rec IS RECORD (
    emp_id   NUMBER,
    emp_name VARCHAR2(100),
    dept_id  NUMBER         -- 필드 추가
  );
  v_emp emp_rec;
BEGIN
  v_emp.emp_id   := 101;
  v_emp.emp_name := 'Hong Gil-dong';
  v_emp.dept_id  := 10;    -- 값 추가

  INSERT INTO employees (emp_id, emp_name, dept_id)
  VALUES (v_emp.emp_id, v_emp.emp_name, v_emp.dept_id);
END;
/

-- FORALL 사용 시 올바른 예시
DECLARE
  TYPE emp_tab IS TABLE OF employees%ROWTYPE;
  v_emps emp_tab := emp_tab();
BEGIN
  v_emps.EXTEND(3);
  FOR i IN 1..3 LOOP
    v_emps(i).emp_id   := i;
    v_emps(i).emp_name := 'Employee_' || i;
    v_emps(i).dept_id  := 10;
  END LOOP;

  FORALL i IN v_emps.FIRST..v_emps.LAST
    INSERT INTO employees VALUES v_emps(i);  -- %ROWTYPE 활용 시 안전
  COMMIT;
END;
/

예방 방법

1. 컬럼 목록을 항상 명시적으로 작성하고 코드 리뷰 체계 도입

INSERT 문을 작성할 때 컬럼 목록을 절대 생략하지 않는 습관을 팀 전체에 정착시키는 것이 중요합니다. 컬럼 목록을 명시적으로 작성하면 스키마 변경 시 에러를 즉시 감지할 수 있고, 코드 가독성도 높아집니다. 또한 테이블 스키마가 변경될 때마다 관련 DML 구문을 함께 검토하는 코드 리뷰 체크리스트를 마련하면 운영 환경에서의 사고를 예방할 수 있습니다.

-- 나쁜 습관: 컬럼 목록 생략 (스키마 변경에 매우 취약)
INSERT INTO employees VALUES (101, 'Hong Gil-dong', 10, SYSDATE);

-- 좋은 습관: 컬럼 목록 명시 (스키마 변경 시 에러 즉시 인지 가능)
INSERT INTO employees (emp_id, emp_name, dept_id, hire_date)
VALUES (101, 'Hong Gil-dong', 10, SYSDATE);

2. %ROWTYPE 활용 및 자동화 테스트(단위 테스트) 도입

PL/SQL 코드에서는 가능한 한 하드코딩된 컬럼 목록 대신 %ROWTYPE을 활용하면 테이블 스키마 변경에 유연하게 대응할 수 있습니다. 아울러 CI/CD 파이프라인에 DML 단위 테스트를 포함시켜 스키마 변경 직후 자동으로 INSERT/UPDATE 구문을 검증하는 체계를 갖추면 ORA-00947을 포함한 다양한 DML 에러를 운영 배포 전에 사전에 차단할 수 있습니다.


관련 에러

  • ORA-00913: too many values — ORA-00947의 반대 상황으로, 컬럼 수보다 값이 더 많을 때 발생합니다. ORA-00947과 함께 쌍으로 기억해두면 좋습니다.
  • ORA-00932: inconsistent datatypes — 컬럼 수는 맞지만 데이터 타입이 맞지 않을 때 발생하는 에러로, INSERT 문 작성 시 함께 주의해야 합니다.
  • ORA-01400: cannot insert NULL into — NOT NULL 제약 조건이 있는 컬럼에 값을 제공하지 않거나 NULL을 삽입하려 할 때 발생하며, ORA-00947 수정 후 이어서 마주치는 경우가 많습니다.
  • ORA-00904: invalid identifier — INSERT SELECT 구문에서 존재하지 않는 컬럼명을 참조할 때 발생하며, 스키마 변경 후 자주 함께 나타납니다.

DBMS 에러 코드 시리즈

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

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

댓글 남기기