【完全ガイド】ORA-00933: SQL command not properly ended の原因と解決方法|UPDATE JOIN・INSERT ORDER BY・MySQL 移行 徹底解説
- 作成日 2026.08.05
- Oracle Database
Oracle 開発者がMySQL/PostgreSQL から移行時によく遭遇するエラー:
-- ❌ MySQL では動く、Oracle では ORA-00933
UPDATE users u
INNER JOIN orders o ON u.id = o.user_id
SET u.total = o.amount;
-- ❌ JDBC で末尾セミコロン
"SELECT * FROM users;"
**「SQL 文が正しく終わっていない」**というシンプルなメッセージ。しかし、実態は「Oracle がその構文を許可していない」ケースが多数:
UPDATE ... JOINは Oracle 非対応(MySQL は OK)DELETE ... JOINもOracle 非対応INSERT ... ORDER BYは構文的に不可- JDBC / Railsからの末尾セミコロン
UNION内のORDER BY(最後にのみ許可)- JOIN 構文の混在(
(+)とANSI JOIN) - サブクエリ後の余分な文字
- クオートの不一致
- 予約語の誤配置
- CREATE VIEW に ORDER BY
このエラーの厄介さは、「なぜダメか」が分かりにくい点。多くの場合、他 DB では正常に動くコードが Oracle では拒否されます。
現場では:
- MySQL → Oracle 移行プロジェクト
- PostgreSQL → Oracle 移行
- JDBC / Java Spring Data JPA
- Rails ActiveRecord(Oracle Enhanced Adapter)
- DBeaver / DataGrip(複数文の一括実行)
- ETL ツール(自動 SQL 生成)
多くの日本語記事が「セミコロンを取り除け」で終わりますが、実務では:
UPDATE ... JOINの正しい Oracle 書き換えMERGE文によるJOIN 更新のモダンな方法EXISTSサブクエリでのフィルタ- JDBC の PreparedStatement の正しい書き方
- Rails ActiveRecord の
update_allパターン - バッチ SQL の実行方法
- DBeaver / DataGrip での複数文実行
さらに、SQL 構文エラー6兄弟(ORA-00936 / 00933 / 00907 / 00904 / 00942 / 00918)の中で、ORA-00933 は「余分な要素」型のエラーです:
ORA-00936: 何かが足りない(式)
ORA-00933: 何かが余分(末尾に不正な構文)
ORA-00907: `)` が足りない
本記事では、ORA-00933: SQL command not properly ended の完全な原因と解決方法を、リファレンスとして実用的に整理します。10大発生パターン、UPDATE JOIN の書き換え、MERGE 文、他 DB からの移行、Rails/Java/Python 対応、実践シナリオ、FAQまで完全網羅。この1本で ORA-00933 に冷静に対処できるようになります。
- 1. 結論:Oracle が許可しない構文
- 2. Oracle の SQL 構文制限
- 3. 【原因①】UPDATE … JOIN の書き換え
- 4. 【原因②】DELETE … JOIN の書き換え
- 5. 【原因③】INSERT … ORDER BY
- 6. 【原因④】JDBC / Rails の末尾セミコロン
- 7. 【原因⑤】UNION 内の ORDER BY
- 8. 【原因⑥】JOIN 構文の混在
- 9. 【原因⑦】CREATE VIEW に ORDER BY
- 10. 【原因⑧】複数 SQL 文の一括実行
- 11. 【原因⑨】GROUP BY の後の余分
- 12. 【原因⑩】サブクエリの後の余分
- 13. 5つの解決策 完全リファレンス
- 14. Rails / Java / Python 対応
- 15. 実践シナリオ
- 15.1. シナリオ1:MySQL → Oracle 移行
- 15.2. シナリオ2:PostgreSQL → Oracle 移行
- 15.3. シナリオ3:Rails でのバッチ処理
- 15.4. シナリオ4:Data Pump インポート後の一括更新
- 15.5. シナリオ5:ETL ツール(Informatica 等)
- 15.6. シナリオ6:DBeaver / DataGrip での複数文
- 15.7. シナリオ7:Docker Oracle でのテスト
- 15.8. シナリオ8:Java Spring Data JPA
- 15.9. シナリオ9:Autonomous DB
- 15.10. シナリオ10:Kamal デプロイ後の検証
- 16. トラブルシューティング
- 17. よくある質問(FAQ)
- 17.1. Q1. ORA-00933 と ORA-00936 の違い
- 17.2. Q2. なぜ UPDATE JOIN 非対応?
- 17.3. Q3. MERGE と UPDATE JOIN の違い
- 17.4. Q4. Rails 側での対応
- 17.5. Q5. JDBC でセミコロン削除の理由
- 17.6. Q6. UNION の ORDER BY
- 17.7. Q7. INSERT の ORDER BY
- 17.8. Q8. パフォーマンスへの影響
- 17.9. Q9. Autonomous DB での挙動
- 17.10. Q10. 他 DB からの移行
- 17.11. Q11. 複数文の実行
- 17.12. Q12. E-Business Suite でよく発生?
- 18. 参考リンク
- 19. まとめ
結論:Oracle が許可しない構文
時間がない方向けに、最速の対処を先に示します。
エラーメッセージの読み方
ORA-00933: SQL command not properly ended
*
ERROR at line 3, column 15:
↑
余分な文字/構文が始まる位置
アスタリスク(*)で示される**「ここが余計だ」**という位置。
Oracle が非対応の構文
-- ❌ 全て ORA-00933
UPDATE ... JOIN ... SET ...
DELETE ... JOIN ...
INSERT ... ORDER BY ...
INSERT ... LIMIT ...
UPDATE ... LIMIT ...
CREATE VIEW ... ORDER BY ... (一部制限)
最速の診断
STEP 1: エラー位置を確認(* マーク)
STEP 2: よくあるパターンをチェック
- UPDATE JOIN → サブクエリ or MERGE に書き換え
- 末尾セミコロン → 削除(JDBC/Rails 経由の場合)
- ORDER BY の位置確認(UNION の末尾、INSERT/DELETE 不可)
- クオート・カッコの対応確認
STEP 3: 段階的に SQL を短くして特定
5つの解決策
| # | 手法 | 使う場面 |
|---|---|---|
| ① | サブクエリで書き換え | UPDATE/DELETE JOIN |
| ② | MERGE 文 | 複雑な JOIN 更新 |
| ③ | EXISTS サブクエリ | フィルタ |
| ④ | セミコロン削除 | JDBC/Rails |
| ⑤ | ANSI JOIN 統一 | 混在解消 |
姉妹エラーとの厳密な違い
| エラー | 意味 | 型 |
|---|---|---|
| ORA-00933 | 余分な構文が末尾 | 「余分」 |
| ORA-00936 | 式が欠けている | 「不足」 |
| ORA-00907 | ) がない | 「対応不足」 |
| ORA-00904 | 識別子無効 | 「名前」 |
| ORA-00942 | テーブル不在 | 「存在」 |
| ORA-00918 | 列名曖昧 | 「曖昧」 |
SQL 構文エラー6兄弟、位置と型で区別。
詳細は以下で解説します。
Oracle の SQL 構文制限
Oracle 特有のルール
1. UPDATE / DELETE で JOIN 構文不可
2. INSERT で ORDER BY 不可
3. INSERT / UPDATE / DELETE で LIMIT 不可(ROWNUM は可)
4. UNION 内で ORDER BY 不可(最後だけ OK)
5. サブクエリ内の ORDER BY は限定的
他 DB との差異
| 構文 | MySQL | PostgreSQL | Oracle |
|---|---|---|---|
UPDATE ... JOIN | ✅ | ❌ | ❌ |
DELETE ... JOIN | ✅ | ❌ | ❌ |
INSERT ... ORDER BY | ✅ | ❌ | ❌ |
LIMIT | ✅ | ✅ | ❌(FETCH FIRST) |
| 末尾セミコロン | ✅ | ✅ | 削除必要(JDBC) |
Oracle は最も厳格。他 DB からの移行時は要注意。
【原因①】UPDATE … JOIN の書き換え
症状(MySQL からの移行)
-- ❌ MySQL では OK、Oracle では ORA-00933
UPDATE employees e
INNER JOIN departments d ON e.dept_id = d.dept_id
SET e.dept_name = d.dept_name;
解決A: サブクエリ(相関)
UPDATE employees e
SET e.dept_name = (
SELECT d.dept_name
FROM departments d
WHERE d.dept_id = e.dept_id
);
⚠️ 全行が更新される(該当なしは NULL)。
解決B: WHERE + EXISTS
UPDATE employees e
SET e.dept_name = (
SELECT d.dept_name
FROM departments d
WHERE d.dept_id = e.dept_id
)
WHERE EXISTS (
SELECT 1 FROM departments d WHERE d.dept_id = e.dept_id
);
-- 該当あり行のみ更新
解決C: MERGE 文(推奨)
MERGE INTO employees e
USING departments d ON (e.dept_id = d.dept_id)
WHEN MATCHED THEN
UPDATE SET e.dept_name = d.dept_name;
モダンで効率的、MERGE 推奨。
MERGE の詳細は Oracle MERGE 文 使い方の記事も参照してください。
解決D: 更新可能ビュー(レガシー)
UPDATE (
SELECT e.dept_name AS e_name, d.dept_name AS d_name
FROM employees e
JOIN departments d ON e.dept_id = d.dept_id
)
SET e_name = d_name;
JOIN が Key-Preserved である必要(制約あり)。
【原因②】DELETE … JOIN の書き換え
症状
-- ❌ Oracle 非対応
DELETE FROM orders o
INNER JOIN customers c ON o.customer_id = c.customer_id
WHERE c.status = 'INACTIVE';
解決A: サブクエリ
DELETE FROM orders
WHERE customer_id IN (
SELECT customer_id FROM customers WHERE status = 'INACTIVE'
);
解決B: EXISTS
DELETE FROM orders o
WHERE EXISTS (
SELECT 1 FROM customers c
WHERE c.customer_id = o.customer_id
AND c.status = 'INACTIVE'
);
大量削除の場合、EXISTS が速い。
【原因③】INSERT … ORDER BY
症状
-- ❌ INSERT に ORDER BY は不要(意味がない)
INSERT INTO log_table
SELECT * FROM source_table ORDER BY created_at;
-- 場合により ORA-00933
解決
-- ✅ ORDER BY を削除
INSERT INTO log_table SELECT * FROM source_table;
-- 順序が重要ならログ用の別カラムに RN 保存
INSERT INTO log_table (id, name, rn)
SELECT id, name, ROW_NUMBER() OVER (ORDER BY created_at)
FROM source_table;
【原因④】JDBC / Rails の末尾セミコロン
症状
// ❌ JDBC で末尾セミコロン
String sql = "SELECT * FROM users;";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
-- ORA-00933
解決
// ✅ セミコロン削除
String sql = "SELECT * FROM users";
JDBC / oracledb / Rails は文の終わり自動判定、セミコロン不要。
SQL*Plus / SQLcl での違い
-- SQL*Plus では末尾セミコロン必須
SELECT * FROM users;
-- JDBC / アプリ経由では削除
"SELECT * FROM users"
【原因⑤】UNION 内の ORDER BY
症状
-- ❌ UNION 内に ORDER BY
SELECT * FROM active_users ORDER BY name
UNION
SELECT * FROM inactive_users ORDER BY name;
-- ORA-00933
解決
-- ✅ 最後だけ ORDER BY
SELECT * FROM active_users
UNION
SELECT * FROM inactive_users
ORDER BY name;
UNION の ORDER BY は全体に対する1回のみ。
【原因⑥】JOIN 構文の混在
症状
-- ❌ 古い (+) と ANSI JOIN の混在
SELECT * FROM a, b LEFT JOIN c ON b.id = c.id
WHERE a.id = b.a_id(+);
-- ORA-00933 or ORA-25156
解決
ANSI JOIN に統一:
SELECT * FROM a
LEFT JOIN b ON a.id = b.a_id
LEFT JOIN c ON b.id = c.id;
【原因⑦】CREATE VIEW に ORDER BY
症状
-- 一部の Oracle バージョンで
CREATE VIEW v_active AS
SELECT * FROM users WHERE status = 'A' ORDER BY name;
-- ORA-00933(バージョン/権限による)
解決
ORDER BY を削除、SELECT 時に指定:
CREATE VIEW v_active AS
SELECT * FROM users WHERE status = 'A';
-- 使用時
SELECT * FROM v_active ORDER BY name;
【原因⑧】複数 SQL 文の一括実行
症状
// ❌ 複数文を一度に
String sql = "INSERT INTO t VALUES (1); INSERT INTO t VALUES (2);";
stmt.execute(sql);
-- ORA-00933
解決
PL/SQL ブロック:
String sql = "BEGIN " +
"INSERT INTO t VALUES (1); " +
"INSERT INTO t VALUES (2); " +
"END;";
stmt.execute(sql);
または個別実行:
stmt.execute("INSERT INTO t VALUES (1)");
stmt.execute("INSERT INTO t VALUES (2)");
【原因⑨】GROUP BY の後の余分
症状
-- ❌ HAVING の後に何もない
SELECT dept_id, COUNT(*) FROM employees
GROUP BY dept_id HAVING;
-- ORA-00933 or ORA-00936
解決
SELECT dept_id, COUNT(*) FROM employees
GROUP BY dept_id
HAVING COUNT(*) > 10;
【原因⑩】サブクエリの後の余分
症状
-- ❌ サブクエリの後に不正な文字
SELECT * FROM (SELECT * FROM users) t.name;
-- ↑ 余分
解決
SELECT t.name FROM (SELECT * FROM users) t;
5つの解決策 完全リファレンス
解決策① サブクエリで書き換え
UPDATE JOIN → 相関サブクエリ:
UPDATE t1
SET col = (SELECT val FROM t2 WHERE t2.id = t1.id)
WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.id = t1.id);
DELETE JOIN → IN サブクエリ:
DELETE FROM t1 WHERE id IN (SELECT id FROM t2 WHERE ...);
解決策② MERGE 文
MERGE INTO target t
USING source s ON (t.id = s.id)
WHEN MATCHED THEN UPDATE SET t.val = s.val;
JOIN 更新のモダンな方法、Oracle の標準。
解決策③ EXISTS サブクエリ
UPDATE t1
SET col = 'A'
WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.id = t1.id);
DELETE FROM t1
WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.id = t1.id);
大量データで効率的。
解決策④ セミコロン削除(JDBC)
// ✅ JDBC / Rails / oracledb
String sql = "SELECT * FROM users"; -- セミコロンなし
解決策⑤ ANSI JOIN 統一
-- ✅ すべて ANSI JOIN
SELECT * FROM a
INNER JOIN b ON a.id = b.a_id
LEFT JOIN c ON b.id = c.b_id;
Rails / Java / Python 対応
Rails ActiveRecord
update_all で JOIN 相当:
# ❌ Rails の JOIN 更新は SQL レベルで書けない
User.joins(:orders).update_all(...) # Oracle で失敗
# ✅ サブクエリ相当
User.where(id: Order.select(:user_id)).update_all(active: true)
# ✅ 個別 UPDATE ループ
Order.where(status: 'completed').find_each do |order|
order.user.update(total: order.amount)
end
# ✅ 生 SQL で MERGE
ActiveRecord::Base.connection.execute(<<-SQL)
MERGE INTO users u
USING (SELECT user_id, SUM(amount) AS total FROM orders GROUP BY user_id) o
ON (u.id = o.user_id)
WHEN MATCHED THEN UPDATE SET u.total = o.total
SQL
セミコロン不要:
# Rails は自動処理、セミコロン不要
User.find_by_sql("SELECT * FROM users WHERE id = 1")
Rails 8 系の詳細は Rails 8 アップグレードガイドの記事、find/find_by/where の記事、Solid Queue 使い方の記事も参照してください。
Java (JDBC)
// ✅ セミコロン削除
String sql = "SELECT * FROM users WHERE id = ?";
// ✅ UPDATE JOIN → MERGE
String merge = """
MERGE INTO users u
USING orders o ON (u.id = o.user_id)
WHEN MATCHED THEN UPDATE SET u.total = o.amount
""";
Python (oracledb)
import oracledb
# ✅ セミコロン削除
cursor.execute("SELECT * FROM users WHERE id = :1", (1,))
# ✅ UPDATE JOIN → サブクエリ
cursor.execute("""
UPDATE users u
SET u.total = (SELECT SUM(amount) FROM orders o WHERE o.user_id = u.id)
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id)
""")
実践シナリオ
シナリオ1:MySQL → Oracle 移行
-- MySQL
UPDATE users u
JOIN orders o ON u.id = o.user_id
SET u.last_order_at = o.created_at
WHERE o.created_at > '2026-06-01';
-- Oracle(MERGE)
MERGE INTO users u
USING (
SELECT user_id, MAX(created_at) AS last_at
FROM orders
WHERE created_at > DATE '2026-06-01'
GROUP BY user_id
) o ON (u.id = o.user_id)
WHEN MATCHED THEN
UPDATE SET u.last_order_at = o.last_at;
シナリオ2:PostgreSQL → Oracle 移行
-- PostgreSQL
UPDATE orders SET status = 'closed'
FROM customers
WHERE orders.customer_id = customers.id
AND customers.deleted_at IS NOT NULL;
-- Oracle
UPDATE orders o SET status = 'closed'
WHERE EXISTS (
SELECT 1 FROM customers c
WHERE c.id = o.customer_id AND c.deleted_at IS NOT NULL
);
シナリオ3:Rails でのバッチ処理
# ❌ Rails でも Oracle 環境で失敗
User.joins(:orders).where(orders: { status: 'active' }).update_all(status: 'busy')
# ✅ サブクエリ相当
User.where(id: Order.where(status: 'active').select(:user_id)).update_all(status: 'busy')
# ✅ 直接 SQL
ActiveRecord::Base.connection.execute(<<-SQL)
UPDATE users SET status = 'busy'
WHERE EXISTS (
SELECT 1 FROM orders
WHERE orders.user_id = users.id
AND orders.status = 'active'
)
SQL
シナリオ4:Data Pump インポート後の一括更新
-- インポート後、参照テーブルから値をコピー
MERGE INTO staging_orders s
USING product_catalog p ON (s.product_code = p.code)
WHEN MATCHED THEN UPDATE SET
s.product_name = p.name,
s.unit_price = p.price;
Data Pump の詳細は Oracle Data Pump 使い方の記事も参照してください。
シナリオ5:ETL ツール(Informatica 等)
-- Informatica が生成する SQL
-- UPDATE JOIN パターンを検出したら
-- ソース設計で MERGE 相当に変換
シナリオ6:DBeaver / DataGrip での複数文
-- ❌ DBeaver で複数文を一括実行
CREATE TABLE t (id NUMBER);
INSERT INTO t VALUES (1);
-- ORA-00933 の可能性(実行モード依存)
-- ✅ PL/SQL ブロック
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE t (id NUMBER)';
INSERT INTO t VALUES (1);
END;
/
シナリオ7:Docker Oracle でのテスト
docker exec -it oracle-xe sqlplus scott/tiger <<EOF
-- UPDATE JOIN テスト
UPDATE emp e
INNER JOIN dept d ON e.deptno = d.deptno
SET e.dname = d.dname;
-- ORA-00933
-- MERGE で正解
MERGE INTO emp e
USING dept d ON (e.deptno = d.deptno)
WHEN MATCHED THEN UPDATE SET e.dname = d.dname;
EOF
Docker 関連は docker daemon 接続エラーの記事、Docker no space left on device の記事も参照してください。
シナリオ8:Java Spring Data JPA
// ❌ JPQL でも Oracle 制約
@Modifying
@Query("UPDATE User u JOIN u.orders o SET u.total = o.amount")
void updateTotal();
// ✅ ネイティブ SQL + MERGE
@Modifying
@Query(value = """
MERGE INTO users u
USING orders o ON (u.id = o.user_id)
WHEN MATCHED THEN UPDATE SET u.total = o.amount
""", nativeQuery = true)
void updateTotal();
シナリオ9:Autonomous DB
-- Autonomous DB でも同じ制約
-- MERGE 推奨
シナリオ10:Kamal デプロイ後の検証
docker exec -it db-container sqlplus / as sysdba <<EOF
-- SQL 実行確認
BEGIN
EXECUTE IMMEDIATE 'MERGE INTO users u
USING new_orders o ON (u.id = o.user_id)
WHEN MATCHED THEN UPDATE SET u.total = o.amount';
END;
/
EOF
Kamal 2 デプロイの詳細は Kamal 2 デプロイの記事を参照してください。
トラブルシューティング
エラー位置が分からない
UPDATE t SET col = 1
JOIN t2 ON t.id = t2.id
*
ERROR at line 2:
ORA-00933: SQL command not properly ended
↑ JOIN が余分(Oracle 非対応)
JDBC でエラー
セミコロン削除が基本。または;\nのような複数文。
DBeaver / DataGrip
- Statement Separator 設定確認
- PL/SQL モードで複数文実行
Rails マイグレーション
# execute の中は個別に
class MyMigration < ActiveRecord::Migration[8.0]
def up
execute "CREATE TABLE ..." -- セミコロンなし
execute "INSERT INTO ..." -- セミコロンなし
end
end
Java Spring Data JPA
ネイティブ SQLは末尾セミコロン不要:
@Query(value = "SELECT * FROM users", nativeQuery = true)
PostgreSQL RETURNING
-- PG では
INSERT INTO t VALUES (...) RETURNING id;
-- Oracle
INSERT INTO t VALUES (...) RETURNING id INTO :out_id;
-- または OUTPUT 変数
よくある質問(FAQ)
Q1. ORA-00933 と ORA-00936 の違い
- 00933: 余分な構文(末尾に何かある)
- 00936: 式が欠けている
Q2. なぜ UPDATE JOIN 非対応?
Oracle の設計判断。MERGE とサブクエリで代替。
Q3. MERGE と UPDATE JOIN の違い
- UPDATE JOIN: MySQL 拡張
- MERGE: ANSI SQL 標準、Oracle 対応
Q4. Rails 側での対応
生 SQL で MERGE を書くか、個別 UPDATE ループ。
Q5. JDBC でセミコロン削除の理由
JDBC はステートメント単位、セミコロン不要。
Q6. UNION の ORDER BY
最後の SELECT の後にのみ許可。
Q7. INSERT の ORDER BY
構文的に不可。ORDER BY は SELECT 用。
Q8. パフォーマンスへの影響
MERGE > EXISTS > IN サブクエリ(一般的に)。
Q9. Autonomous DB での挙動
同じ制約。MERGE 推奨。
Q10. 他 DB からの移行
Oracle SQL Developer Migration Workbench 活用。
Q11. 複数文の実行
PL/SQL ブロック(BEGIN ... END;)で。
Q12. E-Business Suite でよく発生?
カスタムレポートの SQL でよく遭遇。
参考リンク
Oracle 公式
- Oracle Database Error Messages: ORA-00933
- Oracle Database SQL Language Reference: MERGE
- Oracle Database SQL Language Reference: UPDATE
- Oracle Database SQL Language Reference: DELETE
まとめ
ORA-00933: SQL command not properly ended の要点を再整理します。
エラーの本質
SQL 文の末尾に余分な構文がある
→ Oracle が期待しない要素
→ 他 DB では動くが Oracle では拒否
エラーメッセージの読み方
SELECT ... UPDATE ... JOIN
*
ERROR at line 3:
ORA-00933: SQL command not properly ended
↑ 余分な位置
SQL 構文エラー6兄弟
| エラー | 意味 | 型 |
|---|---|---|
| ORA-00933 | 余分な構文 | 過剰 |
| ORA-00936 | 式が欠けている | 不足 |
| ORA-00907 | ) がない | 対応 |
| ORA-00904 | 識別子無効 | 名前 |
| ORA-00942 | テーブル不在 | 存在 |
| ORA-00918 | 列名曖昧 | 曖昧 |
10大原因
| # | 原因 | 対処 |
|---|---|---|
| ① | UPDATE JOIN | MERGE or サブクエリ |
| ② | DELETE JOIN | IN サブクエリ or EXISTS |
| ③ | INSERT ORDER BY | ORDER BY 削除 |
| ④ | JDBC セミコロン | 削除 |
| ⑤ | UNION 内 ORDER BY | 最後だけ |
| ⑥ | JOIN 構文混在 | ANSI 統一 |
| ⑦ | CREATE VIEW ORDER BY | 削除 |
| ⑧ | 複数文実行 | PL/SQL ブロック |
| ⑨ | HAVING の後 | 条件追加 |
| ⑩ | サブクエリ後 | 構文修正 |
Oracle と他 DB の差異
| 構文 | MySQL | PG | Oracle |
|---|---|---|---|
UPDATE JOIN | ✅ | ❌ | ❌ |
DELETE JOIN | ✅ | ❌ | ❌ |
INSERT ORDER BY | ✅ | ❌ | ❌ |
LIMIT | ✅ | ✅ | ❌ |
末尾 ; (JDBC) | ✅ | ✅ | ❌ |
5つの解決策
-- ① サブクエリ書き換え
UPDATE t1 SET col = (SELECT val FROM t2 WHERE t2.id = t1.id);
-- ② MERGE 文(推奨)
MERGE INTO t1 USING t2 ON (t1.id = t2.id)
WHEN MATCHED THEN UPDATE SET t1.col = t2.val;
-- ③ EXISTS
UPDATE t1 SET col = 'A'
WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.id = t1.id);
-- ④ セミコロン削除(JDBC)
"SELECT * FROM t"
-- ⑤ ANSI JOIN 統一
SELECT * FROM a JOIN b ON ... LEFT JOIN c ON ...;
MERGE 文の重要性
-- Oracle の JOIN 更新の標準
MERGE INTO target t
USING source s ON (t.id = s.id)
WHEN MATCHED THEN UPDATE SET t.col = s.val
WHEN NOT MATCHED THEN INSERT (id, col) VALUES (s.id, s.val);
UPDATE JOIN の代替、より柔軟。
予防のポイント
1. 他 DB からの移行時は構文チェック
2. MERGE 文を積極的に使う
3. JDBC / Rails ではセミコロン不要
4. ANSI JOIN で統一((+)を使わない)
5. UPDATE / DELETE で JOIN は使わない
6. UNION の ORDER BY は最後のみ
7. IDE のフォーマッター活用
8. Oracle SQL Developer で構文チェック
9. CI/CD で SQL 検証
10. PL/SQL ブロックで複数文実行
これらの知識は、Oracle での MySQL/PostgreSQL からの移行・レガシー ETL・Rails / Java / Python アプリ開発・MERGE 文活用・ETL パイプライン・DBeaver/DataGrip 使用など、あらゆる場面で活用できます。本記事をブックマークしておけば、ORA-00933 に出会っても冷静に的確に対処できるようになります。
本記事は2026年6月時点の情報をもとに、Oracle Database 19c〜23ai での動作確認・公式ドキュメントに基づき作成しています。Oracle のバージョンにより挙動が異なる場合があるため、最新の情報は Oracle 公式ドキュメント(docs.oracle.com)もあわせてご確認ください。
-
前の記事
【完全ガイド】ORA-00936: missing expression の原因と解決方法|SELECT・FROM・CASE・動的 SQL 徹底解説 2026.08.04
-
次の記事
【完全ガイド】ORA-00947: not enough values の原因と解決方法|INSERT・サブクエリ・CTAS の罠 徹底解説 2026.08.06
コメントを書く