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

ORA-00933
2026년 06월 19일 | DBMS Error 가이드

이 글에서 다루는 내용

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

ORA-00933 SQL command not properly ended 는?

ORA-00933 에러는 Oracle 데이터베이스에서 SQL 문장이 문법적으로 올바르게 종료되지 않았을 때 발생하는 파싱 오류입니다. 주로 SQL 문장의 끝에 불필요한 키워드가 추가되거나, 특정 SQL 구문에서 허용되지 않는 절(Clause)을 사용했을 때 나타납니다. 30년간 현장에서 수없이 마주쳐온 에러로, 대부분 문법 실수에서 비롯되므로 원인만 정확히 파악하면 빠르게 해결할 수 있습니다.


주요 발생 원인

  • INSERT/UPDATE/DELETE 문에 ORDER BY 절 사용

Oracle의 DML 문(INSERT, UPDATE, DELETE)은 기본적으로 ORDER BY 절을 지원하지 않습니다. 개발자들이 SELECT 문에 익숙해져 습관적으로 ORDER BY를 붙이는 경우가 많은데, 이는 ORA-00933을 즉시 유발합니다. 특히 다른 DBMS(MySQL 등)에서 Oracle로 마이그레이션하는 환경에서 자주 발생하는 실수입니다.

“`sql

— 잘못된 예시: DELETE에 ORDER BY 사용

DELETE FROM employees

WHERE department_id = 10

ORDER BY employee_id;

— ORA-00933: SQL command not properly ended

— 올바른 예시: ORDER BY 제거

DELETE FROM employees

WHERE department_id = 10;

“`

  • 서브쿼리 또는 인라인 뷰에서 ORDER BY 사용 시 ROWNUM/FETCH 미사용

Oracle에서는 서브쿼리(인라인 뷰) 내에서 ORDER BY를 단독으로 사용할 경우 문법 오류가 발생할 수 있습니다. ORDER BY는 최상위 SELECT 문이나 ROWNUM, FETCH FIRST 구문과 함께 사용해야 합니다. 이 규칙을 모르는 신규 개발자들이 특히 자주 범하는 실수입니다.

“`sql

— 잘못된 예시: 서브쿼리 내 ORDER BY 단독 사용

SELECT *

FROM (

SELECT employee_id, salary

FROM employees

ORDER BY salary DESC

);

— 일부 버전에서 ORA-00933 또는 ORA-00907 발생 가능

— 올바른 예시: FETCH FIRST 또는 ROWNUM과 함께 사용

SELECT *

FROM (

SELECT employee_id, salary

FROM employees

ORDER BY salary DESC

)

WHERE ROWNUM <= 10;

— Oracle 12c 이상: FETCH FIRST 사용

SELECT employee_id, salary

FROM employees

ORDER BY salary DESC

FETCH FIRST 10 ROWS ONLY;

“`

  • UPDATE 또는 INSERT 문에 잘못된 절 추가 (HAVING, GROUP BY 등)

UPDATE나 INSERT 문에 GROUP BY, HAVING 같은 집계 관련 절을 잘못 추가하는 경우에도 ORA-00933이 발생합니다. 이러한 절들은 SELECT 문에서만 유효하며, DML 문에서는 허용되지 않습니다. 복잡한 로직을 하나의 쿼리로 처리하려다 보니 잘못된 절을 혼용하는 경우가 실무에서 종종 있습니다.

“`sql

— 잘못된 예시: UPDATE에 GROUP BY 사용

UPDATE employees

SET salary = salary * 1.1

WHERE department_id = 20

GROUP BY department_id;

— ORA-00933: SQL command not properly ended

— 올바른 예시: 서브쿼리로 처리

UPDATE employees

SET salary = salary * 1.1

WHERE department_id IN (

SELECT department_id

FROM departments

WHERE location_id = 1700

);

— 잘못된 예시: INSERT에 HAVING 사용

INSERT INTO dept_summary (department_id, avg_sal)

SELECT department_id, AVG(salary)

FROM employees

GROUP BY department_id

HAVING AVG(salary) > 5000

ORDER BY department_id;

— ORDER BY를 INSERT…SELECT에 추가하면 ORA-00933 발생

— 올바른 예시: ORDER BY 제거

INSERT INTO dept_summary (department_id, avg_sal)

SELECT department_id, AVG(salary)

FROM employees

GROUP BY department_id

HAVING AVG(salary) > 5000;

“`


해결 방법

원인 1 해결 – DML 문에서 ORDER BY 제거

DML 문(INSERT, UPDATE, DELETE)에서 ORDER BY 절을 무조건 제거하세요. 만약 특정 순서로 처리해야 한다면, PL/SQL 커서나 서브쿼리를 활용하는 방식으로 대체해야 합니다.

-- PL/SQL로 순서를 보장하며 삭제하는 예시
BEGIN
    FOR rec IN (
        SELECT employee_id
        FROM employees
        WHERE department_id = 10
        ORDER BY hire_date
    ) LOOP
        DELETE FROM employees WHERE employee_id = rec.employee_id;
    END LOOP;
    COMMIT;
END;
/

원인 2 해결 – 올바른 페이징 처리

Oracle 버전에 따라 적절한 페이징 문법을 선택하세요.

-- Oracle 11g 이하: ROWNUM 방식
SELECT *
FROM (
    SELECT e.*, ROWNUM AS rn
    FROM (
        SELECT employee_id, first_name, salary
        FROM employees
        ORDER BY salary DESC
    ) e
    WHERE ROWNUM <= 20
)
WHERE rn >= 11;

-- Oracle 12c 이상: OFFSET-FETCH 방식 (권장)
SELECT employee_id, first_name, salary
FROM employees
ORDER BY salary DESC
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;

원인 3 해결 – 잘못된 절 제거 및 구조 변경

INSERT…SELECT 문에는 ORDER BY를 사용하지 말고, 필요한 집계는 SELECT 안에서 처리하세요.

-- MERGE 문을 활용한 복잡한 DML 처리 예시
MERGE INTO dept_summary tgt
USING (
    SELECT department_id, AVG(salary) AS avg_sal, COUNT(*) AS emp_cnt
    FROM employees
    GROUP BY department_id
    HAVING AVG(salary) > 5000
) src
ON (tgt.department_id = src.department_id)
WHEN MATCHED THEN
    UPDATE SET tgt.avg_sal = src.avg_sal, tgt.emp_cnt = src.emp_cnt
WHEN NOT MATCHED THEN
    INSERT (department_id, avg_sal, emp_cnt)
    VALUES (src.department_id, src.avg_sal, src.emp_cnt);

예방 방법

  • SQL 작성 전 DML별 허용 절(Clause) 체크리스트 확인

팀 내에 DML 문별로 사용 가능한 절과 불가능한 절을 정리한 체크리스트를 공유하고 코드 리뷰 단계에서 반드시 점검하는 문화를 정착시키세요. SQL Developer, Toad, DBeaver 같은 IDE의 문법 강조(Syntax Highlighting) 기능과 SQL 린터(Linter) 도구를 도입하면 작성 시점에 바로 에러를 감지할 수 있어 배포 후 장애를 예방할 수 있습니다.

  • 다른 DBMS 쿼리의 Oracle 이식 시 반드시 문법 검증 수행

MySQL, PostgreSQL, SQL Server 등에서 작성된 쿼리를 Oracle 환경으로 이식할 때는 반드시 Oracle 문법 기준으로 전면 검토하세요. 특히 LIMIT, TOP, ORDER BY in DML 등 DBMS마다 다른 문법 요소를 Oracle 표준으로 변환하는 변환 가이드 문서를 작성해 팀 전체가 공유하면, 마이그레이션 프로젝트에서 발생하는 반복적인 ORA-00933 에러를 크게 줄일 수 있습니다.


관련 에러

  • ORA-00907: missing right parenthesis — 괄호 누락으로 발생하며, 서브쿼리 작성 시 ORA-00933과 함께 자주 등장합니다.
  • ORA-00936: missing expression — 필수 표현식이 빠진 경우로, 잘못된 절 배치와 함께 발생하는 경우가 많습니다.
  • ORA-00904: invalid identifier — 잘못된 컬럼명이나 별칭 사용 시 발생하며, 복잡한 쿼리 수정 과정에서 ORA-00933과 동반 발생할 수 있습니다.
  • ORA-00911: invalid character — SQL 문장 내 허용되지 않는 특수문자 사용 시 발생하며, 문장 종료 문제와 유사한 맥락에서 나타납니다.

DBMS 에러 코드 시리즈

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

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

댓글 남기기