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

ORA-01445
2026년 07월 15일 | DBMS Error 가이드

이 글에서 다루는 내용

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

ORA-01445 cannot select ROWID from a join view without a key-preserved table 는?

ORA-01445 에러는 조인 뷰(Join View)에서 ROWID를 SELECT하거나, 키 보존 테이블(Key-Preserved Table)이 없는 조인 뷰를 통해 DML(UPDATE, DELETE) 작업을 시도할 때 발생합니다. Oracle은 조인 뷰에서 데이터를 수정하거나 ROWID를 참조하려면 반드시 하나 이상의 키 보존 테이블이 존재해야 한다는 규칙을 적용합니다. 키 보존 테이블이란 뷰의 결과 집합에서 각 행이 기본 테이블의 고유한 행과 1:1로 매핑될 수 있는 테이블을 의미합니다.


주요 발생 원인

  • 조인 뷰에서 ROWID를 직접 SELECT하는 경우

가장 흔한 원인으로, 두 개 이상의 테이블을 조인한 뷰에서 ROWID 의사 열(Pseudo Column)을 직접 조회할 때 발생합니다. 조인 결과에서는 어떤 테이블의 ROWID인지 특정할 수 없기 때문에, Oracle은 키 보존 테이블이 명확하지 않으면 이 작업을 허용하지 않습니다. 예를 들어, EMPLOYEES와 DEPARTMENTS를 조인한 뷰에서 SELECT ROWID FROM emp_dept_view를 실행하면 이 에러가 발생합니다.

  • 키 보존 테이블이 없는 조인 뷰에서 DML 수행 시

조인 뷰를 통해 UPDATE 또는 DELETE를 수행할 때, 뷰를 구성하는 테이블 중 기본 키(Primary Key) 또는 유니크 키(Unique Key)가 뷰에 포함되지 않은 경우 키 보존이 성립되지 않습니다. Oracle은 이런 상황에서 어느 행을 실제로 수정해야 하는지 결정할 수 없어 에러를 발생시킵니다. 특히 M:N 관계의 조인이나 집계 함수가 포함된 뷰에서 자주 발생합니다.

  • INSTEAD OF 트리거 없이 복잡한 뷰에 DML 적용 시

GROUP BY, DISTINCT, UNION, 또는 집계 함수(SUM, COUNT 등)가 포함된 복잡한 뷰는 본질적으로 키 보존 테이블을 가질 수 없습니다. 이런 뷰에 INSTEAD OF 트리거 없이 직접 DML을 수행하면 ORA-01445가 발생합니다. 실무에서 레거시 코드를 유지보수할 때 이 원인을 자주 마주치게 됩니다.


해결 방법

원인 1 해결: ROWID 직접 조회 대신 특정 테이블의 ROWID 명시

뷰에서 ROWID를 반드시 사용해야 한다면, 키 보존 테이블의 ROWID를 명시적으로 선택하도록 뷰를 재정의하거나 쿼리를 수정합니다.

-- 문제가 발생하는 쿼리 예시
CREATE OR REPLACE VIEW emp_dept_view AS
SELECT e.employee_id, e.last_name, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id;

-- 에러 발생
SELECT ROWID FROM emp_dept_view;
-- ORA-01445: cannot select ROWID from, a join view without a key-preserved table

-- 해결책 1: 뷰 자체에 키 보존 테이블의 ROWID를 컬럼으로 포함
CREATE OR REPLACE VIEW emp_dept_view AS
SELECT e.ROWID AS emp_rowid,
       e.employee_id,
       e.last_name,
       d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id;

-- 이제 정상 조회 가능
SELECT emp_rowid FROM emp_dept_view;

원인 2 해결: 키 보존 테이블이 포함되도록 뷰 재설계

키 보존 테이블을 확보하려면, 뷰에 기본 키 또는 유니크 키 컬럼이 반드시 포함되어야 합니다.

-- 키 보존이 되지 않는 뷰 예시 (department_id가 PK이지만 SELECT 목록에 없음)
CREATE OR REPLACE VIEW bad_view AS
SELECT e.last_name, d.department_name, d.location_id
FROM employees e
JOIN departments d ON e.department_id = d.department_id;

-- DML 시도 시 에러 발생
UPDATE bad_view SET department_name = 'SALES' WHERE last_name = 'King';
-- ORA-01445 발생

-- 해결책: 키 보존 테이블의 PK를 뷰에 포함
CREATE OR REPLACE VIEW good_view AS
SELECT e.employee_id,   -- employees 테이블의 PK (키 보존 보장)
       e.last_name,
       e.department_id, -- departments 테이블의 PK (키 보존 보장)
       d.department_name,
       d.location_id
FROM employees e
JOIN departments d ON e.department_id = d.department_id;

-- 이제 키 보존 테이블(employees)에 대한 DML은 정상 동작
UPDATE good_view SET last_name = 'KING' WHERE employee_id = 100;

-- 키 보존 여부 확인 방법
SELECT column_name, updatable
FROM user_updatable_columns
WHERE table_name = 'GOOD_VIEW';

원인 3 해결: INSTEAD OF 트리거 활용

복잡한 뷰에 DML이 꼭 필요하다면, INSTEAD OF 트리거를 생성하여 처리 로직을 명시적으로 정의합니다.

-- 집계가 포함된 복잡한 뷰
CREATE OR REPLACE VIEW dept_salary_view AS
SELECT d.department_id,
       d.department_name,
       AVG(e.salary) AS avg_salary,
       COUNT(e.employee_id) AS emp_count
FROM departments d
JOIN employees e ON d.department_id = e.department_id
GROUP BY d.department_id, d.department_name;

-- INSTEAD OF 트리거로 DML 로직 처리
CREATE OR REPLACE TRIGGER trg_dept_salary_upd
INSTEAD OF UPDATE ON dept_salary_view
FOR EACH ROW
BEGIN
    -- department_name 변경 시 departments 테이블 업데이트
    IF :NEW.department_name != :OLD.department_name THEN
        UPDATE departments
        SET department_name = :NEW.department_name
        WHERE department_id = :OLD.department_id;
    END IF;
END;
/

-- 트리거 생성 후 정상 동작
UPDATE dept_salary_view
SET department_name = 'Finance & Accounting'
WHERE department_id = 100;

빠른 진단 쿼리

-- 뷰의 업데이트 가능 컬럼 확인
SELECT table_name, column_name, updatable, insertable, deletable
FROM user_updatable_columns
WHERE table_name = 'YOUR_VIEW_NAME'
ORDER BY column_name;

-- 뷰 정의 확인
SELECT text
FROM user_views
WHERE view_name = 'YOUR_VIEW_NAME';

예방 방법

  • 뷰 설계 시 키 보존 가능 여부를 사전에 검토하라

조인 뷰를 설계할 때는 반드시 USER_UPDATABLE_COLUMNS 딕셔너리 뷰를 통해 각 컬럼의 수정 가능 여부를 미리 확인하는 습관을 가져야 합니다. 뷰를 생성한 직후 아래 쿼리를 실행하여 키 보존 테이블이 올바르게 인식되고 있는지 검증하고, DML이 필요한 뷰라면 기본 키 컬럼이 반드시 SELECT 목록에 포함되도록 뷰 설계 가이드라인을 팀 내 표준으로 수립하세요.

“`sql

— 뷰 생성 후 반드시 검증

SELECT column_name, updatable, insertable, deletable

FROM user_updatable_columns

WHERE table_name = ‘NEW_VIEW_NAME’;

“`

  • 복잡한 뷰에는 처음부터 INSTEAD OF 트리거를 계획하라

GROUP BY, DISTINCT, UNION, 집계 함수 등이 포함된 뷰를 설계할 때는, 처음부터 해당 뷰가 읽기 전용인지 DML이 필요한지 명확히 결정하고 문서화해야 합니다. DML이 필요한 복잡한 뷰라면 INSTEAD OF 트리거를 아키텍처 설계 단계에서 포함시켜, 운영 중 갑작스러운 에러로 인한 장애를 사전에 예방하세요.


관련 에러

  • ORA-01779: cannot modify a column which maps to a non key-preserved table — 조인 뷰에서 키 보존이 안 된 테이블의 컬럼을 수정하려 할 때 발생하며, ORA-01445와 매우 유사한 상황에서 함께 나타납니다.
  • ORA-01732: data manipulation operation not legal on this view — 집계 함수나 DISTINCT가 포함된 뷰에 DML을 시도할 때 발생합니다.
  • ORA-01736: only simple views can be used for INSERT/UPDATE/DELETE — 복합 뷰에 DML 작업이 허용되지 않을 때 발생합니다.

DBMS 에러 코드 시리즈

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

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

댓글 남기기