ORA-01403 no data found — PL/SQL SELECT INTO 결과 없음 에러 원인·해결·예방
Oracle PL/SQL에서 SELECT INTO 구문을 사용하다 보면 ORA-01403: no data found 에러를 자주 만난다. 조회 결과가 0건일 때 PL/SQL이 예외를 던지면서 발생하는 에러로, SQL 단독 실행에서는 발생하지 않고 PL/SQL 블록 안에서만 일어난다는 점이 헷갈리기 쉬운 부분이다.
본 글은 ORA-01403의 정확한 의미와 자주 발생하는 6가지 케이스, NO_DATA_FOUND 예외 처리·집계함수 변환·CURSOR·BULK COLLECT 등 5가지 회피 패턴, 유사 에러 ORA-01422·ORA-06502·ORA-06512와의 관계까지 정리한 트러블슈팅 자료다.

이 글의 구성
01 에러 메시지와 카테고리
ORA-01403은 PL/SQL의 SELECT INTO 결과가 0건일 때, 또는 Associative Array의 미존재 키를 참조할 때 발생하는 에러다. SQLCODE 100에 매핑된 사전 정의 예외 NO_DATA_FOUND와 같다.
| 항목 | 내용 |
|---|---|
| 에러 코드 | ORA-01403 |
| 영문 메시지 | no data found |
| SQLCODE | 100 |
| 사전 정의 예외 | NO_DATA_FOUND |
| Cause (공식) | No row was returned from a SELECT INTO statement, or no rows remain in a cursor, or a referenced row in a PL/SQL table was undefined. |
| Action (공식) | SELECT INTO 결과가 0건일 가능성을 처리하거나 예외 처리 구문(EXCEPTION WHEN NO_DATA_FOUND THEN)을 추가 |
| 발생 영역 | PL/SQL 블록 (Procedure·Function·Trigger·Anonymous Block) |
핵심 관찰 — SQL과 PL/SQL의 차이
동일한 0건 결과라도 SQL 단독 SELECT는 ORA-01403을 던지지 않는다. 단순히 "rows selected" 0건으로 정상 종료된다. ORA-01403은 PL/SQL의 SELECT INTO처럼 반드시 변수에 값을 대입해야 하는 구문에서만 발생한다. 디버깅 시 이 차이를 명확히 인지하는 것이 중요하다.
02 발생 원인 6가지
원인 1 — SELECT INTO 결과 0건
가장 흔한 케이스다. WHERE 조건에 매칭되는 행이 없을 때 SELECT INTO는 변수에 값을 대입할 수 없어 NO_DATA_FOUND 예외를 던진다.
원인 2 — Associative Array 미존재 키 접근
Index-by Table(Associative Array)에서 존재하지 않는 키를 참조하면 ORA-01403이 발생한다. .EXISTS() 메서드로 사전 확인이 필요하다.
원인 3 — 명시적 커서 FETCH 후 NOTFOUND 확인 누락
CURSOR를 OPEN하고 FETCH한 후 %NOTFOUND 체크 없이 데이터를 사용하면, 마지막 행 이후에도 FETCH를 시도해 ORA-01403이 발생할 수 있다.
원인 4 — 트리거 본문 내 SELECT INTO
트리거에서 마스터 테이블 조회 시 결과가 0건이면 트리거 실행이 실패하고 원래 DML도 롤백된다. 자식 테이블 처리·검증 트리거에서 자주 발생한다.
원인 5 — Function 내부 SELECT INTO
PL/SQL Function 안의 SELECT INTO에서 NO_DATA_FOUND가 발생하면 Function을 호출한 SQL 문까지 예외가 전파되어 전체 쿼리가 실패한다.
원인 6 — 데이터 보정·이관 후 누락 행
데이터 마이그레이션·정리 작업 후 기존 PL/SQL 로직이 가정하던 행이 사라지면, 평소 잘 작동하던 코드에서 갑자기 ORA-01403이 발생한다.
03 재현 시나리오와 코드 예제
원인 1 — SELECT INTO 0건
-- ✗ id=999인 행이 없으면 NO_DATA_FOUND
DECLARE
v_name VARCHAR2(50);
BEGIN
SELECT name INTO v_name
FROM emp
WHERE id = 999;
-- ORA-01403: no data found
END;
/
원인 2 — Associative Array 미존재 키
DECLARE
TYPE t_arr IS TABLE OF VARCHAR2(50) INDEX BY VARCHAR2(10);
v_arr t_arr;
v_val VARCHAR2(50);
BEGIN
v_arr('A') := 'Apple';
-- ✗ 'X' 키 없음 → ORA-01403
v_val := v_arr('X');
-- ✓ EXISTS 체크 후 접근
IF v_arr.EXISTS('X') THEN
v_val := v_arr('X');
END IF;
END;
/
원인 3 — 커서 FETCH 후 NOTFOUND 누락
DECLARE
CURSOR c_emp IS SELECT name FROM emp;
v_name VARCHAR2(50);
BEGIN
OPEN c_emp;
LOOP
FETCH c_emp INTO v_name;
-- ✓ 반드시 NOTFOUND 체크
EXIT WHEN c_emp%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_name);
END LOOP;
CLOSE c_emp;
END;
/
원인 5 — Function 내부에서 SELECT INTO 전파
CREATE OR REPLACE FUNCTION get_dept_name(p_id NUMBER)
RETURN VARCHAR2 IS
v_name VARCHAR2(100);
BEGIN
SELECT name INTO v_name FROM dept WHERE id = p_id;
RETURN v_name;
END;
/
-- ✗ Function이 NO_DATA_FOUND 던지면 SQL 전체 실패
SELECT emp.id, get_dept_name(emp.dept_id) FROM emp;
-- ORA-01403 (특정 dept_id가 dept 테이블에 없으면)
04 회피 패턴 5가지
ORA-01403은 적절한 회피 패턴으로 사전 방어할 수 있다.
패턴 1 — NO_DATA_FOUND 예외 처리
DECLARE
v_name VARCHAR2(50);
BEGIN
SELECT name INTO v_name FROM emp WHERE id = 999;
EXCEPTION
WHEN NO_DATA_FOUND THEN
v_name := '(unknown)'; -- ✓ 기본값 설정
END;
/
패턴 2 — 집계함수로 변환 (항상 1행 반환)
-- MAX·MIN·COUNT·SUM·AVG는 결과 0건이어도 NULL 반환 → ORA-01403 미발생
SELECT MAX(name) INTO v_name FROM emp WHERE id = 999;
-- v_name이 NULL이 될 뿐, 예외는 발생하지 않음
패턴 3 — CURSOR + FETCH + NOTFOUND 체크
CURSOR 기반 접근은 0건 상황을 자연스럽게 처리한다. 위 원인 3 예시 코드 참고.
패턴 4 — COUNT(*)로 사전 검증 후 분기
DECLARE
v_cnt NUMBER;
v_name VARCHAR2(50);
BEGIN
SELECT COUNT(*) INTO v_cnt FROM emp WHERE id = 999;
IF v_cnt > 0 THEN
SELECT name INTO v_name FROM emp WHERE id = 999;
END IF;
END;
/
패턴 5 — BULK COLLECT INTO (0건 안전)
DECLARE
TYPE t_list IS TABLE OF VARCHAR2(50);
v_names t_list;
BEGIN
-- ✓ BULK COLLECT는 0건이어도 ORA-01403 미발생
SELECT name BULK COLLECT INTO v_names
FROM emp WHERE dept_id = 999;
-- v_names는 빈 컬렉션 (COUNT=0)
IF v_names.COUNT = 0 THEN
DBMS_OUTPUT.PUT_LINE('no rows');
END IF;
END;
/
05 유사 에러 비교 (01422·06502·06512)
| 에러 | 의미 | 관계 |
|---|---|---|
| ORA-01403 | no data found (0행) | SELECT INTO 결과 0건 |
| ORA-01422 | exact fetch returns more than requested (다중 행) | 01403의 정반대 — 2행 이상 |
| ORA-06502 | numeric or value error (PL/SQL 값 오류) | 별개이지만 같은 스택에 동반될 수 있음 |
| ORA-06512 | at line X (예외 발생 PL/SQL 위치) | 원인이 아닌 위치 표시 — 함께 출력됨 |
기억법: 01403=비어있음, 01422=초과(2행+), 06502=값/타입 오류, 06512=위치 표시.
06 자주 묻는 질문 5가지
Q1SQL Developer에서 SELECT만 실행하면 ORA-01403이 안 나는데 왜인가
SQL 단독 실행은 0행이 정상 결과다. ORA-01403은 PL/SQL의 SELECT INTO처럼 변수에 반드시 값을 대입해야 하는 구문에서만 발생한다. 같은 쿼리라도 PL/SQL 블록 안에서 INTO 절을 사용하면 0건이 예외로 처리된다.
Q2NO_DATA_FOUND 예외를 그냥 무시해도 되나
권장하지 않는다. EXCEPTION WHEN NO_DATA_FOUND THEN NULL;처럼 빈 처리를 두면 0건 상황이 조용히 무시되어 디버깅이 어려워진다. 기본값을 설정하거나 로그를 남기는 처리를 함께 두는 것이 안전하다.
Q3SELECT MAX 변환이 항상 안전한 회피책인가
대부분의 단일 행 조회 회피에 사용 가능하다. MAX·MIN·SUM·AVG·COUNT 같은 집계함수는 결과 행이 0건이어도 NULL을 반환하므로 ORA-01403이 발생하지 않는다. 다만 NULL 값을 받았을 때 후속 로직에서 별도 처리가 필요할 수 있다.
Q4ORA-01403과 ORA-06512가 함께 표시되는 이유는
ORA-06512는 예외가 발생한 PL/SQL의 라인 위치를 알려주는 메시지다. ORA-01403이 원인 에러이고 ORA-06512는 그 위치를 따라가는 추적 메시지다. 디버깅 시 ORA-06512의 라인 번호로 문제가 발생한 SELECT INTO 위치를 빠르게 찾을 수 있다.
Q5BULK COLLECT가 0건에 안전하다면 왜 항상 사용하지 않나
BULK COLLECT는 대량 처리에 효과적이지만, 단일 값 조회에는 컬렉션 변수와 인덱스 접근이 필요해 코드가 복잡해진다. 또한 매우 큰 결과 셋을 BULK COLLECT로 한 번에 가져오면 PGA 메모리를 많이 사용하므로 LIMIT 절로 청크 단위 처리가 권장된다. 단일 행 조회에는 예외 처리 또는 집계함수 변환이 더 단순하다.
07 결론
ORA-01403은 PL/SQL의 SELECT INTO 결과가 0건일 때 발생하는 가장 흔한 예외 중 하나다. SQL 단독 실행과 PL/SQL의 차이를 이해하고, 사전 정의 예외 NO_DATA_FOUND의 동작 방식을 익히면 빠르게 해결할 수 있다.
실무 적용 원칙은 다음과 같다.
첫째, 단일 값 조회는 EXCEPTION WHEN NO_DATA_FOUND 처리 또는 MAX·MIN 집계함수 변환을 표준으로 한다.
둘째, 다중 행 조회는 CURSOR + FETCH + NOTFOUND 패턴 또는 BULK COLLECT를 사용해 0건 상황을 자연스럽게 흡수한다.
셋째, 디버깅 시 ORA-06512가 함께 표시되면 그 라인 번호를 따라가 문제가 된 SELECT INTO 위치를 빠르게 좁힌다.
ORA-01403은 PL/SQL이 0건을 정상 결과로 받아들이지 못해 던지는 예외다. NO_DATA_FOUND·집계함수·CURSOR·BULK COLLECT 중 상황에 맞는 패턴을 적용하면 안전하다.
— SQL 단독은 0건 정상, PL/SQL의 SELECT INTO만 예외 발생
트러블슈팅 체크리스트
본 글은 Oracle Database PL/SQL 예외 ORA-01403의 일반적 원인과 해결 방법을 정리한 자료다. 운영 환경 적용 전 테스트 환경에서 충분히 검증한다.
#ORA01403 #nodatafound #오라클에러 #PLSQL #SELECTINTO #NO_DATA_FOUND #예외처리 #CURSOR #BULKCOLLECT #ORA01422 #ORA06502 #ORA06512 #PLSQL함수 #PLSQL트리거 #DB트러블슈팅
'IT' 카테고리의 다른 글
| ORA-01422 exact fetch returns more than requested rows — SELECT INTO 다중 행 에러 원인·해결 (0) | 2026.05.14 |
|---|---|
| ORA-12899 value too large for column — VARCHAR2 길이 초과 에러 원인·해결·예방 (0) | 2026.05.13 |
| ORA-01722 invalid number — 숫자 변환 실패 에러 원인·해결·예방 (0) | 2026.05.13 |
| LLM이 뭐야? — 대규모 언어 모델 작동 원리·주요 모델·활용·한계 완전 가이드 (0) | 2026.05.13 |
| DLP와 DRM이 뭐야? — 회사 보안 솔루션 차이·작동 방식·직장인 대응 가이드 (0) | 2026.05.13 |
댓글