【Oracle】ORA-00933の原因と解決方法|SQL command not properly ended・LIMIT・UPDATE JOINの注意点

【Oracle】ORA-00933の原因と解決方法|SQL command not properly ended・LIMIT・UPDATE JOINの注意点 Oracle

ORA-00933: SQL command not properly ended は、SQLの構文がOracleの文法として正しく終わっていないときに発生するエラーです。日本語環境では「SQLコマンドが正しく終了されていません」と表示されます。

よくある原因は、MySQLの LIMIT、SQL ServerやPostgreSQL風の UPDATE ... FROM、MySQL風の UPDATE ... JOIN、使えない位置の ORDER BY、余分なセミコロンや複数SQLの混在です。

先に結論
ORA-00933は、Oracleで使えない句や余分な構文を取り除き、Oracleの書き方に直すと解消できます。LIMITFETCH FIRSTROWNUMUPDATE JOIN は相関サブクエリや MERGEDELETE JOINEXISTS に書き換えるのが基本です。
スポンサーリンク

ORA-00933とは

Oracle公式のエラー説明では、ORA-00933はSQL文が不適切な句で終わっている、またはサポートされない構文が含まれる場合に発生するエラーとして案内されています。つまり、SQLの意味以前に、Oracleの構文として解析できていない状態です。

症状 よくある原因 まず見る場所
MySQLでは動くSQLがOracleで落ちる LIMITUPDATE JOIN 他DB固有の構文が混ざっていないか
末尾付近でエラーになる 余分な句、余分なセミコロン、複数SQL SQLの最後、ORDER BYの位置
UPDATEやDELETEで落ちる JOIN構文の違い 相関サブクエリ、EXISTS、MERGEへの書き換え
アプリからだけ落ちる SQL文字列の連結ミス 実際にDBへ送っているSQL

まず確認するチェックリスト

チェック NG例 Oracleでの方向性
LIMIT を使っていないか SELECT * FROM t LIMIT 10 FETCH FIRST 10 ROWS ONLY にする
UPDATE ... JOIN を使っていないか UPDATE a JOIN b ... 相関サブクエリまたは MERGE にする
DELETE ... JOIN を使っていないか DELETE a FROM a JOIN b ... EXISTS を使う
使えない場所に ORDER BY がないか UPDATE ... ORDER BY ... 必要な場合はサブクエリ側へ寄せる
アプリ実行SQLにセミコロンや複数文がないか SELECT ...; SELECT ... 1回のexecuteでは1文だけ送る

LIMITをFETCH FIRSTへ書き換える

MySQLやPostgreSQLの LIMIT をOracleへそのまま持ち込むと、ORA-00933の原因になります。Oracle 12c以降では、件数制限に FETCH FIRSTOFFSET ... FETCH を使えます。

limit-to-fetch-first.sql
-- NG: OracleではLIMIT句を使わない
SELECT *
FROM employees
ORDER BY employee_id
LIMIT 10;

-- OK: Oracle 12c以降はFETCH FIRSTを使う
SELECT *
FROM employees
ORDER BY employee_id
FETCH FIRST 10 ROWS ONLY;

ページングでは OFFSET も使えます。Oracleでの件数取得は Oracleで指定件数分のデータを取得する方法、RDBMS別の違いは SQLのLIMIT句記事 も参考になります。

offset-fetch.sql
SELECT *
FROM employees
ORDER BY employee_id
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;

UPDATE JOINをOracleのUPDATEへ書き換える

MySQLの UPDATE ... JOIN はOracleのUPDATE文では使えません。別テーブルの値を使って更新する場合は、相関サブクエリ、更新可能なインラインビュー、または MERGE を検討します。

update-join-to-correlated-update.sql
-- NG: MySQL風のUPDATE JOIN
UPDATE employees e
JOIN departments d
  ON d.department_id = e.department_id
SET e.department_name = d.department_name;

-- OK: Oracleでは相関サブクエリで更新する
UPDATE employees e
SET e.department_name = (
    SELECT d.department_name
    FROM departments d
    WHERE d.department_id = e.department_id
)
WHERE EXISTS (
    SELECT 1
    FROM departments d
    WHERE d.department_id = e.department_id
);

更新対象が多い場合やUPSERTも絡む場合は MERGE が向いています。OracleのDML全体は OracleのINSERT・UPDATE・DELETE完全ガイド、MERGEは OracleのMERGE文記事 で詳しく整理しています。

UPDATE FROMをMERGEへ書き換える

Oracle 19c以前を含む多くの現場では、PostgreSQLやSQL Server風の UPDATE ... FROM をそのまま使えません。結合結果をもとに更新したい場合は、MERGE INTO にすると意図が明確です。

update-from-to-merge.sql
-- NG: Oracle 19c以前ではUPDATE FROMをそのまま使えない
UPDATE employees e
SET salary = s.new_salary
FROM salary_work s
WHERE s.employee_id = e.employee_id;

-- OK: MERGEで一致行を更新する
MERGE INTO employees e
USING salary_work s
ON (s.employee_id = e.employee_id)
WHEN MATCHED THEN
  UPDATE SET e.salary = s.new_salary;

Oracle 23aiの直接JOINとの違い

Oracle Database 23aiでは、UPDATE文やDELETE文で対象表を別表とJOINする直接JOIN構文がサポートされています。そのため、すべてのOracleで常に UPDATE ... FROM が使えない、というわけではありません。

ただし、Oracle 19c以前の環境、古い互換性設定、現場で使っているSQL方針によっては、従来どおり相関サブクエリや MERGEEXISTS へ書き換えるほうが安全です。ORA-00933が出たら、まず接続先のOracleバージョンと実際に使える構文を確認します。

環境 JOIN付きDMLの考え方 安全な代替
Oracle 19c以前 UPDATE ... FROMDELETE ... JOIN は避ける 相関サブクエリ、MERGEEXISTS
Oracle 23ai以降 直接JOINが使える場合がある 構文を公式ドキュメントと実環境で確認する
複数バージョンをまたぐSQL 新構文に寄せすぎると移植性が落ちる 従来構文で書くか、バージョン別に分ける

DELETE JOINをEXISTSへ書き換える

MySQL風の DELETE ... JOIN もOracleでは使えません。別テーブルに一致する行を削除したい場合は、EXISTS を使って削除対象を絞ります。

delete-join-to-exists.sql
-- NG: MySQL風のDELETE JOIN
DELETE e
FROM employees e
JOIN retired_employees r
  ON r.employee_id = e.employee_id;

-- OK: OracleではEXISTSで削除対象を指定する
DELETE FROM employees e
WHERE EXISTS (
    SELECT 1
    FROM retired_employees r
    WHERE r.employee_id = e.employee_id
);

EXISTS を使った絞り込みは、OracleのIN句・EXISTSの使い分け記事 とも考え方が近いです。

ORDER BYを使えない場所に書いている

ORDER BY はSELECT結果を並べるための句です。UPDATEやDELETEの末尾にそのまま付けたり、Oracleが許可しない位置へ置いたりすると、ORA-00933の原因になります。

bad-order-by-position.sql
-- NG: UPDATE文の末尾にORDER BYを付ける
UPDATE employees
SET status = 'CHECKED'
WHERE department_id = 10
ORDER BY employee_id;

-- OK: 更新対象を条件で明確にする。順番が必要なら事前SELECTで確認する
UPDATE employees
SET status = 'CHECKED'
WHERE department_id = 10;

「上位10件だけ更新したい」のような要件では、単に ORDER BY をUPDATEへ付けるのではなく、サブクエリや ROW_NUMBER で対象行を決めてから更新します。

セミコロンや複数SQLをアプリから送っている

SQL DeveloperやSQL*Plusでは、セミコロンはSQL文の終端として扱われます。一方、アプリケーションのDBドライバへSQL文字列を渡す場合、末尾のセミコロンや複数文の連結がエラーの原因になることがあります。環境によってはORA-00933ではなくORA-00911などになる場合もあります。

app-sql-semicolon.sql
-- NG: アプリのexecuteに複数文をまとめて渡す
SELECT * FROM employees; SELECT * FROM departments;

-- OK: executeは1文ずつ実行する
SELECT * FROM employees

SELECT * FROM departments

アプリ側でSQLを動的に組み立てている場合は、ログに出したSQLをそのままコピーして確認します。不要なカンマ、閉じ括弧の不足、条件連結時の AND / WHERE の重複も一緒に確認しましょう。

INSERTでORA-00933が出る場合

INSERTでは、列リストと値の数が合わない場合は別のエラーになることもありますが、余分な句や他DB構文が混ざるとORA-00933になることがあります。特に INSERT ... VALUES に不要な ORDER BY を付けるような書き方は避けます。

insert-ora00933.sql
-- NG: VALUESにORDER BYを付けても意味がなく、構文エラーの原因になる
INSERT INTO employees (employee_id, employee_name)
VALUES (1001, 'Sato')
ORDER BY employee_id;

-- OK: INSERTは値を登録するだけにする
INSERT INTO employees (employee_id, employee_name)
VALUES (1001, 'Sato');

WITH句やサブクエリの位置を確認する

WITH 句やサブクエリを使う場合も、句の順序や括弧の位置が崩れるとORA-00933になります。長いSQLでは、まずSELECT単体で動かし、UPDATEやINSERTへ組み込む前に構文を小さく確認します。

with-check.sql
-- まずWITH + SELECTだけで確認する
WITH target_employees AS (
    SELECT employee_id
    FROM employees
    WHERE department_id = 10
)
SELECT *
FROM target_employees;

-- 確認後にDMLへ組み込む場合は、サブクエリとして書く
UPDATE employees
SET status = 'CHECKED'
WHERE employee_id IN (
    SELECT employee_id
    FROM employees
    WHERE department_id = 10
);

ORA-00936との違い

ORA-00933 は、SQL文の終わり方や句の位置が不正なときに出やすいエラーです。一方、ORA-00936: missing expression は、SELECT句の項目、WHERE条件の右辺、関数引数など、必要な式が抜けているときに出やすいです。

エラー 見やすい場所
ORA-00933 SQLの末尾、句の順序、他DB構文 LIMITUPDATE JOIN、不適切な ORDER BY
ORA-00936 SELECT項目、条件式、関数引数 SELECT FROM employeesWHERE employee_id =

ORA-00911との違い

ORA-00911: invalid character は、SQL中にOracleが受け付けない文字が含まれるときに出やすいエラーです。アプリからSQLを実行するときの末尾セミコロン、全角記号、不要な制御文字などが原因になることがあります。

ORA-00933 は句の順序やサポートされない構文、ORA-00911 は不正文字、ORA-00936 は必要な式の不足、と分けて見ると切り分けやすくなります。

修正手順

ORA-00933は、エラーメッセージだけでは原因箇所が分かりにくいことがあります。次の順番で切り分けると、長いSQLでも直しやすくなります。

手順 やること 見るポイント
1 実際にDBへ送っているSQLをログ出力する アプリ側の文字列連結で崩れていないか
2 末尾から確認する 余分なセミコロン、ORDER BY、LIMIT、複数文
3 他DB構文を探す LIMITUPDATE JOINUPDATE FROMDELETE JOIN
4 SELECT単体で動かす サブクエリやWITH句だけ先に確認する
5 Oracleバージョンを確認する 23aiの直接JOINを使えるか、19c以前の構文に合わせるかを決める
6 Oracle構文へ置き換える FETCH FIRST、相関サブクエリ、MERGEEXISTS

よくある質問

ORA-00933はなぜ発生しますか?

OracleのSQL構文として認められない句や余分な文字が含まれるためです。特に、MySQLの LIMITUPDATE JOIN、使えない位置の ORDER BY が原因になりやすいです。

OracleでLIMITの代わりに何を使いますか?

Oracle 12c以降では FETCH FIRST n ROWS ONLY、ページングでは OFFSET ... FETCH を使います。古いバージョンでは ROWNUM を使うことがあります。

UPDATE JOINはOracleで使えますか?

Oracle 19c以前ではMySQL風の UPDATE ... JOIN は使えません。相関サブクエリ、更新可能なインラインビュー、または MERGE に書き換えます。Oracle 23ai以降では直接JOINが使えるケースがあるため、接続先バージョンも確認します。

セミコロンが原因になることはありますか?

SQLツールでは終端記号として使えますが、アプリのDBドライバへ渡すSQL文字列では不要な場合があります。環境によってORA-00933やORA-00911の原因になるため、アプリからは1文だけをセミコロンなしで渡すのが無難です。

まとめ

ORA-00933は、SQL文がOracleの構文として正しく終わっていないときに発生します。まずSQLの末尾、句の順序、他DB固有の構文が混ざっていないかを確認しましょう。

LIMITFETCH FIRSTUPDATE JOIN は相関サブクエリや MERGEDELETE JOINEXISTS へ置き換えます。長いSQLでは、小さなSELECTに分解してからDMLへ組み込むと原因を見つけやすくなります。

参考

ORA-00933 – Oracle Database Error Help

SELECT – Oracle SQL Language Reference

UPDATE – Oracle SQL Language Reference

Oracle Database 23ai Feature Highlights