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の書き方に直すと解消できます。
LIMIT は FETCH FIRST や ROWNUM、UPDATE JOIN は相関サブクエリや MERGE、DELETE JOIN は EXISTS に書き換えるのが基本です。ORA-00933とは
Oracle公式のエラー説明では、ORA-00933はSQL文が不適切な句で終わっている、またはサポートされない構文が含まれる場合に発生するエラーとして案内されています。つまり、SQLの意味以前に、Oracleの構文として解析できていない状態です。
| 症状 | よくある原因 | まず見る場所 |
|---|---|---|
| MySQLでは動くSQLがOracleで落ちる | LIMIT や UPDATE 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 FIRST や OFFSET ... FETCH を使えます。
-- 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句記事 も参考になります。
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 を検討します。
-- 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 にすると意図が明確です。
-- 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方針によっては、従来どおり相関サブクエリや MERGE、EXISTS へ書き換えるほうが安全です。ORA-00933が出たら、まず接続先のOracleバージョンと実際に使える構文を確認します。
| 環境 | JOIN付きDMLの考え方 | 安全な代替 |
|---|---|---|
| Oracle 19c以前 | UPDATE ... FROM や DELETE ... JOIN は避ける |
相関サブクエリ、MERGE、EXISTS |
| Oracle 23ai以降 | 直接JOINが使える場合がある | 構文を公式ドキュメントと実環境で確認する |
| 複数バージョンをまたぐSQL | 新構文に寄せすぎると移植性が落ちる | 従来構文で書くか、バージョン別に分ける |
DELETE JOINをEXISTSへ書き換える
MySQL風の DELETE ... JOIN もOracleでは使えません。別テーブルに一致する行を削除したい場合は、EXISTS を使って削除対象を絞ります。
-- 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の原因になります。
-- 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などになる場合もあります。
-- 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 を付けるような書き方は避けます。
-- 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 + 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構文 | LIMIT、UPDATE JOIN、不適切な ORDER BY |
ORA-00936 |
SELECT項目、条件式、関数引数 | SELECT FROM employees、WHERE 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構文を探す | LIMIT、UPDATE JOIN、UPDATE FROM、DELETE JOIN |
| 4 | SELECT単体で動かす | サブクエリやWITH句だけ先に確認する |
| 5 | Oracleバージョンを確認する | 23aiの直接JOINを使えるか、19c以前の構文に合わせるかを決める |
| 6 | Oracle構文へ置き換える | FETCH FIRST、相関サブクエリ、MERGE、EXISTS |
よくある質問
ORA-00933はなぜ発生しますか?
OracleのSQL構文として認められない句や余分な文字が含まれるためです。特に、MySQLの LIMIT、UPDATE 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固有の構文が混ざっていないかを確認しましょう。
LIMIT は FETCH FIRST、UPDATE JOIN は相関サブクエリや MERGE、DELETE JOIN は EXISTS へ置き換えます。長いSQLでは、小さなSELECTに分解してからDMLへ組み込むと原因を見つけやすくなります。
参考
ORA-00933 – Oracle Database Error Help
SELECT – Oracle SQL Language Reference

