2026년 08월 02일 | DBMS Error 가이드
이 글에서 다루는 내용
ORA-01795 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
ORA-01795 maximum number of expressions in a list is 1000 는?
ORA-01795 에러는 SQL 쿼리의 IN 절 또는 기타 리스트 표현식에 1,000개를 초과하는 값을 입력했을 때 발생하는 Oracle 데이터베이스의 제한 오류입니다. Oracle은 단일 IN 리스트에 최대 1,000개의 표현식만 허용하며, 이를 초과하면 즉시 에러를 반환합니다. 특히 대용량 데이터를 처리하는 배치 프로그램이나 동적 SQL을 생성하는 애플리케이션에서 자주 발생하며, 개발 단계보다 운영 환경에서 데이터가 증가한 이후에 예상치 못하게 나타나는 경우가 많습니다.
주요 발생 원인
1. IN 절에 1,000개 초과의 바인드 변수 또는 리터럴 값 사용
가장 흔한 원인으로, 애플리케이션 레이어에서 동적으로 SQL을 생성할 때 IN 절에 수천 개의 ID 값을 직접 나열하는 경우입니다. 예를 들어 Java나 Python 코드에서 리스트를 그대로 SQL에 삽입하는 방식으로 구현되면, 데이터가 증가함에 따라 어느 순간 1,000개를 초과하게 됩니다. 개발 초기에는 소량의 데이터로 테스트하기 때문에 문제가 드러나지 않다가, 실제 운영 환경에서 처음 발생하는 경우가 매우 많습니다.
2. 임시 결과셋을 IN 절로 전달하는 잘못된 쿼리 설계
서브쿼리 대신 애플리케이션 코드에서 1차 조회 결과를 가져와 그 값들을 다시 IN 절로 구성하는 2단계 조회 방식을 사용할 때 발생합니다. 이는 ORM(Object Relational Mapping) 프레임워크나 자체 개발 DAL(Data Access Layer)에서 흔히 볼 수 있는 안티패턴으로, 데이터가 적을 때는 문제없이 동작하다가 운영 중에 한계를 초과합니다. 근본적으로는 조인이나 서브쿼리로 해결할 수 있는 문제를 불필요하게 애플리케이션 레이어로 끌어올린 설계 결함입니다.
3. 배치 처리에서 청크 분리 없이 전체 키 목록을 단일 쿼리로 처리
대량 데이터 배치 작업에서 처리 대상 키(ID) 목록을 별도로 관리하다가 이를 한 번에 IN 절에 넣어 처리하는 경우입니다. 1,000건 이하일 때는 문제없이 작동하다가 처리 데이터가 증가하면 에러가 발생합니다. 배치 프로그램에서 청크 사이즈에 대한 고려 없이 개발된 경우 운영 환경에서 반드시 이 문제를 만나게 됩니다.
해결 방법
해결책 1: IN 절을 여러 개로 분리하여 OR로 연결
가장 빠른 임시 방편으로, IN 절 하나에 최대 999개씩 나누고 OR로 연결합니다.
-- 잘못된 예 (1,000개 초과 시 ORA-01795 발생)
SELECT employee_id, employee_name
FROM employees
WHERE employee_id IN (1, 2, 3, ..., 1500); -- 1,500개 값 → 에러!
-- 수정된 예: 999개씩 분리 후 OR로 연결
SELECT employee_id, employee_name
FROM employees
WHERE employee_id IN (1, 2, 3, ..., 999) -- 첫 번째 그룹 (999개)
OR employee_id IN (1000, 1001, ..., 1500); -- 두 번째 그룹 (501개)
해결책 2: 임시 테이블(Global Temporary Table) 활용 (권장)
대용량 ID 목록은 임시 테이블에 INSERT한 후 조인으로 처리하는 방법입니다. 이 방법은 성능도 우수하고 유지보수도 쉬워 실무에서 가장 권장되는 방식입니다.
-- 1단계: Global Temporary Table 생성 (최초 1회)
CREATE GLOBAL TEMPORARY TABLE tmp_emp_ids (
emp_id NUMBER
) ON COMMIT DELETE ROWS;
-- 2단계: 처리 대상 ID를 임시 테이블에 INSERT
INSERT INTO tmp_emp_ids (emp_id)
SELECT COLUMN_VALUE
FROM TABLE(SYS.ODCINUMBERLIST(1001, 1002, 1003, /* ... 수천 개 */));
COMMIT;
-- 3단계: 임시 테이블과 조인으로 처리
SELECT e.employee_id, e.employee_name, e.salary
FROM employees e
JOIN tmp_emp_ids t ON e.employee_id = t.emp_id;
해결책 3: 서브쿼리로 대체 (근본적 해결)
2단계 조회 구조를 하나의 SQL로 통합하는 방법입니다. 애플리케이션에서 ID 목록을 직접 전달하는 대신, DB 레벨에서 서브쿼리로 조건을 표현합니다.
-- 기존 문제 코드 (애플리케이션에서 ID 1,500개를 IN 절로 전달)
SELECT order_id, order_date, amount
FROM orders
WHERE customer_id IN (/* 1,500개의 customer_id 직접 나열 */);
-- 개선된 방법: 서브쿼리로 대체
SELECT o.order_id, o.order_date, o.amount
FROM orders o
WHERE o.customer_id IN (
SELECT c.customer_id
FROM customers c
WHERE c.region = 'SEOUL'
AND c.grade = 'VIP'
AND c.reg_date >= DATE '2023-01-01'
);
해결책 4: SYS.ODCINUMBERLIST / TABLE() 함수 활용
Oracle Collection 타입을 활용하면 1,000개 제한을 우회하면서도 단일 SQL로 처리할 수 있습니다.
-- ODCINUMBERLIST를 이용한 동적 IN 절 대체
SELECT e.employee_id, e.employee_name
FROM employees e
WHERE e.employee_id IN (
SELECT COLUMN_VALUE
FROM TABLE(
SYS.ODCINUMBERLIST(
1001, 1002, 1003, 1004, 1005,
-- ... 1,000개 초과 가능
2001, 2002, 2003
)
)
);
-- VARCHAR 타입의 경우 ODCIVARCHAR2LIST 사용
SELECT d.dept_id, d.dept_name
FROM departments d
WHERE d.dept_code IN (
SELECT COLUMN_VALUE
FROM TABLE(
SYS.ODCIVARCHAR2LIST('HR', 'IT', 'FIN', 'MKT' /*, ... 1,000개 초과 가능 */)
)
);
해결책 5: 배치 처리 시 청크(Chunk) 단위 분할 처리
배치 프로그램에서는 처리 대상 목록을 999개 단위로 잘라서 반복 처리하도록 로직을 수정합니다.
-- PL/SQL을 이용한 청크 단위 배치 처리 예제
DECLARE
TYPE t_id_list IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
v_ids t_id_list;
v_chunk NUMBER := 999; -- 청크 사이즈
v_start NUMBER := 1;
v_end NUMBER;
v_sql VARCHAR2(32767);
BEGIN
-- 처리 대상 ID를 컬렉션에 로드 (예시)
SELECT employee_id
BULK COLLECT INTO v_ids
FROM employees
WHERE status = 'PENDING';
-- 999개씩 청크 처리
WHILE v_start <= v_ids.COUNT LOOP
v_end := LEAST(v_start + v_chunk - 1, v_ids.COUNT);
-- 청크 범위에 해당하는 레코드 업데이트
FORALL i IN v_start .. v_end
UPDATE employees
SET status = 'PROCESSED',
updated_at = SYSDATE
WHERE employee_id = v_ids(i);
COMMIT;
v_start := v_end + 1;
END LOOP;
DBMS_OUTPUT.PUT_LINE('처리 완료: ' || v_ids.COUNT || '건');
END;
/
예방 방법
1. 코드 리뷰 및 정적 분석 단계에서 동적 IN 절 생성 패턴 감지
개발 단계에서 IN 절에 동적으로 값을 삽입하는 코드 패턴을 코드 리뷰 체크리스트에 포함시켜야 합니다. 특히 반복문으로 SQL 문자열을 조립하거나, 컬렉션/리스트를 그대로 IN 절로 변환하는 로직은 반드시 검토 대상으로 지정하세요. SonarQube 등의 정적 분석 도구에 커스텀 룰을 추가하거나, 팀 내 SQL 작성 가이드라인에 “IN 절의 값 목록은 서브쿼리 또는 임시 테이블로 대체”하는 원칙을 명문화하는 것이 효과적입니다.
2. 개발/테스트 환경에서 운영 수준의 데이터 볼륨으로 테스트 의무화
ORA-01795는 개발 환경의 소량 데이터에서는 절대 나타나지 않고 운영 환경에서만 드러나는 특성이 있습니다. 성능 테스트 및 통합 테스트 단계에서 운영 환경과 유사한 데이터 볼륨(최소 운영 데이터의 70% 이상)으로 테스트하는 것을 의무화하세요. 또한 배치 프로그램의 경우 처리 건수의 상한값에 대한 가정을 명시적으로 문서화하고, 해당 상한값을 초과할 경우 자동으로 청크 처리로 전환되도록 방어 로직을 구현하는 것이 좋습니다.
관련 에러
- ORA-00913: too many values — INSERT 또는 비교 구문에서 예상보다 많은 값이 제공될 때 발생하며, IN 절 설계 오류와 함께 나타나는 경우가 있습니다.
- ORA-01460: unimplemented or unreasonable conversion requested — 바인드 변수 타입 불일치로 발생하며, 동적 SQL로 ORA-01795를 우회하려다 잘못된 타입 변환을 시도할 때 연관되어 나타날 수 있습니다.
- ORA-00907: missing right parenthesis — IN 절을 동적으로 생성할 때 괄호 처리 오류로 발생하며, ORA-01795 수정 과정에서 SQL 분리 작업 중 함께 발생하는 경우가 있습니다.
- ORA-04031: unable to allocate shared memory — 지나치게 큰 IN 리스트를 가진 SQL이 공유 풀을 과도하게 점유할 때 간접적으로 연관될 수 있습니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.