【完全ガイド】ORA-00913: too many values の原因と解決方法|INSERT・サブクエリ・タプル比較 徹底解説

【完全ガイド】ORA-00913: too many values の原因と解決方法|INSERT・サブクエリ・タプル比較 徹底解説

Oracle 開発者が日常的に遭遇するINSERT エラー:

-- ❌ 列数 3、値の数 4
INSERT INTO employees(id, name, salary) 
VALUES (100, 'John', 50000, 10);
-- ORA-00913: too many values

**「値が多すぎる」**というシンプルなメッセージ。ORA-00947(not enough values)の姉妹エラーで、対をなす存在です:

  • INSERT VALUES で値の数が列数より多い
  • INSERT ... SELECT でサブクエリの列数超過
  • WHERE A IN (SELECT A, B)タプル比較(列数超過)
  • HAVING A IN (SELECT A, B) の同様パターン
  • CTAS 後の列削除でずれる
  • MERGE 文のINSERT部分
  • スカラーサブクエリが複数列返す
  • EXISTS と**IN** の混同
  • 動的 SQLの生成ミス
  • Rails マイグレーションでカラム削除後

このエラーはORA-00947 の裏返し、同じ原因メカニズムで**「多すぎる」**方向のミス:

CREATE TABLE t (a NUMBER, b NUMBER);

INSERT INTO t VALUES (1, 2, 3);
-- ORA-00913(2列に3値)

そして、タプル比較での典型:

-- ❌ 単一列に2列サブクエリ
SELECT * FROM employees 
WHERE dept_id IN (SELECT dept_id, name FROM departments);
-- ORA-00913

多くの日本語記事が「値を減らせ」で終わりますが、実務では:

  • 姉妹エラー3種ORA-00947, ORA-00913, ORA-01400)の厳密な区別
  • スカラーサブクエリでの単一列制約
  • IN vs EXISTS の使い分け
  • CTAS 後のカラム削除時の対処
  • MERGE 文の INSERT 節の落とし穴
  • Rails / Java / Python からの防御的コーディング
  • 列リスト明示による予防
  • CI/CD パイプラインでの事前検出

さらに、知っておくべき「意外な発生パターン」:

-- スカラーサブクエリは 1 列 1 行のみ
SELECT emp_name, 
       (SELECT dept_id, dept_name FROM departments WHERE ...) 
FROM employees;
-- ORA-00913(スカラーは 1 列のみ)

これは Oracle SQL の重要な制約で、日本語で本質的な解説がほぼない領域です。

本記事では、ORA-00913: too many values完全な原因と解決方法を、リファレンスとして実用的に整理します。10大発生パターン、姉妹エラー ORA-00947/ORA-01400 との比較、5つの解決策、Rails/Java/Python 対応、実践シナリオ、FAQまで完全網羅。この1本で ORA-00913 を根本から解決できるようになります。


目次

結論:列数と値の数を一致させる

時間がない方向けに、最速の対処を先に示します。

エラーメッセージの読み方

ORA-00913: too many values
                ↑ 
            値が多すぎる
            = 期待される数より多い

期待される数:

  • INSERT の列リスト(または全列)の数
  • タプル (A) の要素数
  • スカラーサブクエリの列数(=1)

最速の診断

-- ① テーブルの列数確認
SELECT column_id, column_name, data_type
FROM user_tab_columns 
WHERE table_name = 'EMPLOYEES'
ORDER BY column_id;

-- ② サブクエリの列数
SELECT * FROM (
  <サブクエリ>
) x WHERE 1=0;

5つの解決策

#手法使う場面
列リスト明示INSERT の保守性
値を減らす単純な過多
サブクエリ列数調整INSERT SELECT / IN
EXISTS に変更存在確認
タプル要素一致複数列 IN

姉妹エラー3種

エラー意味
ORA-00947値が足りない
ORA-00913値が多すぎる
ORA-01400NOT NULL に NULL

INSERT エラー3兄弟、対称的な関係。

詳細は以下で解説します。


Oracle の INSERT ルール(復習)

INSERT の基本構文

-- 列リスト明示
INSERT INTO t (col1, col2, col3) VALUES (v1, v2, v3);
-- 列数 = 値数 が必須

-- 列リスト省略
INSERT INTO t VALUES (v1, v2, v3);
-- テーブルの全列と同数の値が必要

スカラーサブクエリの制約

1 列 1 行のみ:

SELECT (SELECT dept_id FROM departments WHERE id = 10)
FROM DUAL;
-- OK: 1列

複数列は不可:

SELECT (SELECT dept_id, name FROM departments WHERE id = 10)
FROM DUAL;
-- ORA-00913

タプル比較

-- 単一列 IN は複数列サブクエリ不可
WHERE dept_id IN (SELECT dept_id FROM t);   -- OK: 1列
WHERE dept_id IN (SELECT dept_id, name FROM t);  -- ORA-00913

【原因①】INSERT VALUES で値過多(最頻出)

症状

CREATE TABLE employees (
  id NUMBER, 
  name VARCHAR2(100), 
  salary NUMBER
);

-- ❌ 3列に4値
INSERT INTO employees(id, name, salary) 
VALUES (100, 'John', 50000, 10);
-- ORA-00913

解決

A. 値を削除:

INSERT INTO employees(id, name, salary) 
VALUES (100, 'John', 50000);

B. 列リスト拡張(列が存在すれば):

INSERT INTO employees(id, name, salary, dept_id) 
VALUES (100, 'John', 50000, 10);

【原因②】列リスト省略の罠(列削除後)

症状

CREATE TABLE t (a NUMBER, b NUMBER, c NUMBER);

-- 動くコード
INSERT INTO t VALUES (1, 2, 3);

-- 列削除後
ALTER TABLE t DROP COLUMN c;

-- 既存の INSERT が壊れる
INSERT INTO t VALUES (1, 2, 3);
-- ORA-00913(2列に3値)

解決:常に列リスト明示

INSERT INTO t (a, b) VALUES (1, 2);
-- 列リスト明示で保守性向上

INSERT の値不足エラーの詳細は ORA-00947: not enough values の記事も参照してください。


【原因③】INSERT … SELECT の列過多

症状

CREATE TABLE dst (a NUMBER, b NUMBER);
CREATE TABLE src (a NUMBER, b NUMBER, c NUMBER);

-- ❌ SELECT が 3 列、INSERT は 2 列想定
INSERT INTO dst SELECT * FROM src;
-- ORA-00913

解決

A. 列リスト明示:

INSERT INTO dst (a, b) SELECT a, b FROM src;

B. 全列指定:

INSERT INTO dst SELECT a, b FROM src;

C. dst に列追加(もし想定される場合):

ALTER TABLE dst ADD (c NUMBER);
INSERT INTO dst SELECT * FROM src;

【原因④】タプル比較の失敗(超危険)

症状

-- ❌ 単一列 IN に複数列サブクエリ
SELECT * FROM employees 
WHERE dept_id IN (SELECT dept_id, name FROM departments);
-- ORA-00913

解決

A. サブクエリを1列に:

SELECT * FROM employees 
WHERE dept_id IN (SELECT dept_id FROM departments);

B. タプル比較に:

SELECT * FROM employees 
WHERE (dept_id, name) IN (SELECT dept_id, name FROM departments);
-- 両側 2 列で一致

HAVING でも同様

-- ❌
SELECT dept_id, COUNT(*) FROM employees
GROUP BY dept_id
HAVING dept_id IN (SELECT dept_id, name FROM departments);
-- ORA-00913

【原因⑤】スカラーサブクエリで複数列

症状

-- ❌ スカラー位置に複数列
SELECT emp_id,
       (SELECT dept_id, dept_name FROM departments WHERE ...)
FROM employees;
-- ORA-00913

解決

A. 個別サブクエリ:

SELECT emp_id,
       (SELECT dept_id FROM departments WHERE ...) AS dept_id,
       (SELECT dept_name FROM departments WHERE ...) AS dept_name
FROM employees;

B. JOIN に変更(推奨):

SELECT e.emp_id, d.dept_id, d.dept_name
FROM employees e
LEFT JOIN departments d ON d.id = e.dept_id;

【原因⑥】CTAS 後の列削除

シナリオ

-- 元テーブル
CREATE TABLE src (a NUMBER, b NUMBER, c NUMBER);

-- CTAS
CREATE TABLE dst AS SELECT * FROM src;
-- dst も 3 列

-- dst の列削除
ALTER TABLE dst DROP COLUMN c;

-- 既存の INSERT が壊れる
INSERT INTO dst SELECT * FROM src;
-- ORA-00913

解決

INSERT INTO dst (a, b) SELECT a, b FROM src;

【原因⑦】MERGE 文の INSERT 節

症状

MERGE INTO dst USING src ON (dst.id = src.id)
WHEN NOT MATCHED THEN 
  INSERT (id, name) VALUES (src.id, src.name, src.salary);
-- ORA-00913(2列に3値)

解決

MERGE INTO dst USING src ON (dst.id = src.id)
WHEN NOT MATCHED THEN 
  INSERT (id, name, salary) VALUES (src.id, src.name, src.salary);
-- 列リストに salary 追加

MERGE の詳細は Oracle MERGE 文 使い方の記事も参照してください。


【原因⑧】EXISTS と IN の混同

症状

-- ❌ EXISTS の中の SELECT 列は意味ない(NULL でもOK)
-- が、IN と混同して:
SELECT * FROM employees 
WHERE dept_id IN (
  SELECT 1 FROM departments WHERE id = 10
);
-- 動くが意図と違う

-- 逆に、EXISTS を IN のように書くとエラー
SELECT * FROM employees 
WHERE dept_id EXISTS (SELECT dept_id, name FROM departments);
-- 構文エラー

解決

IN と EXISTS の正しい使い分け:

-- IN: 値の一致
WHERE dept_id IN (SELECT dept_id FROM departments);

-- EXISTS: 存在確認(相関)
WHERE EXISTS (
  SELECT 1 FROM departments d 
  WHERE d.dept_id = employees.dept_id
);

【原因⑨】動的 SQL のミス

症状

DECLARE
  v_cols VARCHAR2(100) := 'id, name';  -- 2列
  v_vals VARCHAR2(100) := ':1, :2, :3'; -- 3値
BEGIN
  EXECUTE IMMEDIATE 
    'INSERT INTO t (' || v_cols || ') VALUES (' || v_vals || ')'
    USING 100, 'John', 50000;
-- ORA-00913
END;
/

解決

動的 SQL 前に列/値数チェック:

DECLARE
  v_col_count PLS_INTEGER;
  v_val_count PLS_INTEGER;
BEGIN
  v_col_count := REGEXP_COUNT(v_cols, ',') + 1;
  v_val_count := REGEXP_COUNT(v_vals, ',') + 1;
  
  IF v_col_count != v_val_count THEN
    RAISE_APPLICATION_ERROR(-20001, '列数と値数不一致');
  END IF;
END;
/

【原因⑩】Rails マイグレーションで列削除

シナリオ

# Rails migration
class RemoveDeptFromUsers < ActiveRecord::Migration[8.0]
  def change
    remove_column :users, :dept_id
  end
end

その後、生 SQL で INSERT していると:

# ❌ 削除された列を含む
ActiveRecord::Base.connection.execute(
  "INSERT INTO users (id, name, dept_id) VALUES (1, 'John', 10)"
)
-- ORA-00913

解決

A. ActiveRecord 使用:

User.create!(id: 1, name: 'John')

B. 列リスト更新:

execute "INSERT INTO users (id, name) VALUES (1, 'John')"

Rails 8 系の詳細は Rails 8 アップグレードガイドの記事、rails db:migrate 使い方の記事、pending migration エラーの記事も参照してください。


5つの解決策 完全リファレンス

解決策① 列リスト明示(推奨)

-- ✅ 常に列リスト
INSERT INTO t (col1, col2) VALUES (v1, v2);

-- ❌ 列リスト省略は危険
INSERT INTO t VALUES (v1, v2);

解決策② 値を減らす

INSERT INTO t (a, b) VALUES (1, 2);
-- 3値だったのを2値に

解決策③ サブクエリ列数調整

-- 列数を合わせる
INSERT INTO dst (a, b) SELECT a, b FROM src;

-- IN の複数列は タプル比較
WHERE (a, b) IN (SELECT c1, c2 FROM t);

解決策④ EXISTS に変更

-- 存在確認は EXISTS
WHERE EXISTS (
  SELECT 1 FROM t 
  WHERE t.id = main.id
);

解決策⑤ タプル要素一致

-- 両側同じ列数
WHERE (a, b, c) IN (SELECT c1, c2, c3 FROM t);

Rails / Java / Python 対応

Rails ActiveRecord

基本:

# ✅ ActiveRecord は自動処理
User.create!(id: 1, name: 'John')

# エラーハンドリング
begin
  User.create!(attributes)
rescue ActiveRecord::StatementInvalid => e
  if e.message.include?("ORA-00913")
    Rails.logger.error "値過多: #{e.message}"
  end
end

Java (JDBC)

// ✅ PreparedStatement + 列リスト
PreparedStatement ps = conn.prepareStatement(
    "INSERT INTO users (id, name) VALUES (?, ?)"
);
ps.setInt(1, 1);
ps.setString(2, "John");
ps.executeUpdate();

// エラーハンドリング
try {
    ps.executeUpdate();
} catch (SQLException e) {
    if (e.getErrorCode() == 913) {
        logger.error("値過多: " + e.getMessage());
    }
}

Python (oracledb)

import oracledb

# ✅ 列リスト明示
cursor.execute("""
    INSERT INTO users (id, name) VALUES (:1, :2)
""", (1, 'John'))

# エラーハンドリング
try:
    cursor.execute(sql, params)
except oracledb.DatabaseError as e:
    error_obj, = e.args
    if error_obj.code == 913:
        print(f"値過多: {error_obj.message}")

実践シナリオ

シナリオ1:CTAS 後の列削除運用

-- 1. テーブル作成
CREATE TABLE report AS 
SELECT id, name, amount, region FROM orders WHERE 1=0;

-- 2. データ投入(列リスト明示)
INSERT INTO report (id, name, amount, region) 
SELECT id, name, amount, region FROM orders;

-- 3. 列削除
ALTER TABLE report DROP COLUMN region;

-- 4. 既存 INSERT を修正
INSERT INTO report (id, name, amount) 
SELECT id, name, amount FROM orders;

シナリオ2:ETL パイプラインでの防御

CREATE OR REPLACE PROCEDURE etl_load IS
  v_col_count PLS_INTEGER;
BEGIN
  SELECT COUNT(*) INTO v_col_count 
  FROM user_tab_columns 
  WHERE table_name = 'DST';
  
  IF v_col_count != 2 THEN
    RAISE_APPLICATION_ERROR(-20001, 
      '想定と列数不一致: ' || v_col_count);
  END IF;
  
  INSERT INTO dst (a, b) SELECT a, b FROM src;
END;
/

シナリオ3:タプル比較の適切な使用

-- 複数列の一致検索
SELECT * FROM orders
WHERE (customer_id, order_date) IN (
  SELECT customer_id, MAX(order_date) 
  FROM orders 
  GROUP BY customer_id
);
-- 各顧客の最新注文

シナリオ4:Rails でのモデル対応

class User < ApplicationRecord
  # スキーマ変更に強い
  # ActiveRecord が列名を自動処理
end

# ✅ 属性ハッシュで作成
User.create!(attributes.slice(*User.column_names))

シナリオ5:Data Pump インポート

# 列不一致の事前確認
impdp system/pw dumpfile=data.dmp SQLFILE=preview.sql

# preview.sql を目視、列数チェック

Data Pump の詳細は Oracle Data Pump 使い方の記事も参照してください。

シナリオ6:MERGE での列一致

MERGE INTO users u
USING (
  SELECT 1 AS id, 'John' AS name, 100 AS salary FROM DUAL
) s ON (u.id = s.id)
WHEN NOT MATCHED THEN
  INSERT (id, name, salary) 
  VALUES (s.id, s.name, s.salary);
-- 列数と値数を同数に

シナリオ7:Java Spring での対処

@Repository
public class UserRepo {
    public void insertUser(User user) {
        String sql = """
            INSERT INTO users (id, name, salary) 
            VALUES (?, ?, ?)
            """;
        jdbc.update(sql, 
            user.getId(), 
            user.getName(), 
            user.getSalary()
        );
    }
}

シナリオ8:Python での動的 INSERT

def insert_row(cursor, table: str, data: dict):
    columns = list(data.keys())
    placeholders = [f":{i+1}" for i in range(len(columns))]
    
    sql = f"""
        INSERT INTO {table} ({', '.join(columns)})
        VALUES ({', '.join(placeholders)})
    """
    cursor.execute(sql, list(data.values()))

シナリオ9:Docker Oracle でのテスト

docker exec -it oracle-xe sqlplus scott/tiger <<EOF
CREATE TABLE t (a NUMBER, b NUMBER);

-- ORA-00913
INSERT INTO t VALUES (1, 2, 3);

-- OK
INSERT INTO t (a, b) VALUES (1, 2);
EOF

Docker 関連は docker daemon 接続エラーの記事、Docker no space left on device の記事も参照してください。

シナリオ10:CI/CD スキーマ検証

- name: Check schema consistency
  run: |
    sqlplus -s $DB_USER/$DB_PW <<EOF
    SELECT table_name, COUNT(*) col_count
    FROM user_tab_columns
    GROUP BY table_name;
    EOF

Kamal 2 デプロイの詳細は Kamal 2 デプロイの記事を参照してください。


トラブルシューティング

列削除後のトラブル

カラム削除で既存 INSERT が壊れた:

-- 影響範囲確認
SELECT * FROM user_source
WHERE UPPER(text) LIKE '%INSERT INTO T%';
-- 全 PL/SQL コードを検索

動的 SQL のデバッグ

DBMS_OUTPUT.PUT_LINE('SQL: ' || v_sql);

CTAS で列がずれた

SELECT column_id, column_name 
FROM user_tab_columns 
WHERE table_name IN ('SRC', 'DST')
ORDER BY table_name, column_id;

PostgreSQL からの移行

PG では INSERT … RETURNING * が使える。Oracle は個別変数指定。

JOIN で自己参照

自己 JOIN 時のエイリアス漏れ:

-- ❌ エイリアスなし
SELECT * FROM t, t WHERE ...;

-- ✅
SELECT * FROM t a, t b WHERE ...;

スカラーサブクエリの制限

1列1行、複数列返さない:

-- ❌
SELECT (SELECT a, b FROM t WHERE id = 1) FROM DUAL;

-- ✅
SELECT (SELECT a FROM t WHERE id = 1),
       (SELECT b FROM t WHERE id = 1)
FROM DUAL;

よくある質問(FAQ)

Q1. ORA-00913 と ORA-00947 の違い

  • 00913: 値が多すぎる
  • 00947: 値が足りない

Q2. ORA-01400 との違い

  • 01400: NOT NULL 列に NULL
  • 00913: 列数と値数の不一致

Q3. 列リスト明示のメリット

  • テーブル変更に強い
  • 保守性向上
  • 意図が明確

Q4. Rails での対応

ActiveRecord::Base は自動で列名処理。

Q5. タプル比較の使い所

複数列の一致検索。両側同じ要素数。

Q6. スカラーサブクエリの制約

1 列 1 行のみ。複数列は個別 or JOIN。

Q7. EXISTS と IN の違い

  • IN: 値の一致(複数値可)
  • EXISTS: 存在確認(相関)

Q8. 動的 SQL

生成 SQL を出力して確認。

Q9. 列削除時の影響

列リスト明示なら影響なし。省略なら要修正。

Q10. パフォーマンスへの影響

構文エラーは実行前、パフォーマンス影響なし。

Q11. 予防策

  • 列リスト明示
  • CI/CD でスキーマ検証
  • コードレビュー

Q12. 23ai の新機能

INSERT WHEN NOT EXISTS 対応(一部)。


参考リンク

Oracle 公式


まとめ

ORA-00913: too many values の要点を再整理します。

エラーの本質

SQL 文で期待される値の数より
実際の値の数が多い
→ INSERT / タプル比較 / スカラーサブクエリ

エラーメッセージの読み方

ORA-00913: too many values
                ↑
        期待より値が多い

INSERT エラー3兄弟

エラー意味
ORA-00913値が多すぎる
ORA-00947値が足りない
ORA-01400NOT NULL に NULL

10大原因

#原因対処
INSERT VALUES 値過多値削除 or 列拡張
列リスト省略 + 列削除列リスト明示
INSERT SELECT 列過多列合わせ
タプル比較 過多サブクエリ列数
スカラー複数列個別 or JOIN
CTAS 後の列削除列リスト明示
MERGE INSERT全列指定
EXISTS/IN 混同正しい使い分け
動的 SQL生成確認
Rails migration 後ActiveRecord

5つの解決策

-- ① 列リスト明示(推奨)
INSERT INTO t (a, b) VALUES (1, 2);

-- ② 値削減
INSERT INTO t (a, b) VALUES (1, 2);

-- ③ サブクエリ列数調整
INSERT INTO dst (a, b) SELECT a, b FROM src;

-- ④ EXISTS 使用
WHERE EXISTS (SELECT 1 FROM t WHERE ...);

-- ⑤ タプル要素一致
WHERE (a, b) IN (SELECT c1, c2 FROM t);

診断クエリ

-- ① 列構造確認
SELECT column_id, column_name, data_type
FROM user_tab_columns 
WHERE table_name = 'T'
ORDER BY column_id;

-- ② サブクエリ列数
SELECT * FROM (<subquery>) WHERE 1=0;

スカラーサブクエリの制約

-- ❌ 複数列
(SELECT a, b FROM t WHERE id = 1)

-- ✅ 単一列
(SELECT a FROM t WHERE id = 1)

タプル比較

-- 両側同じ要素数
WHERE (a, b) IN (SELECT c1, c2 FROM t);

-- 単一列
WHERE a IN (SELECT c1 FROM t);

IN vs EXISTS

-- IN: 値の集合との比較
WHERE dept_id IN (SELECT dept_id FROM departments);

-- EXISTS: 相関サブクエリの存在確認
WHERE EXISTS (
  SELECT 1 FROM departments d
  WHERE d.dept_id = employees.dept_id
);

予防のポイント

1. INSERT には必ず列リスト明示
2. INSERT SELECT でも列を明示
3. Rails: ActiveRecord 使用
4. Java: PreparedStatement + 列リスト
5. Python: バインド変数 + 列リスト
6. CI/CD でスキーマ検証
7. 列削除時は既存 SQL 確認
8. Data Pump は SQLFILE で事前確認
9. コードレビューで INSERT 確認
10. スカラーサブクエリは 1 列 1 行を意識

これらの知識は、Oracle でのアプリ開発・ETL パイプライン・マイグレーション設計・Rails / Java / Python 開発・データ移行・CTAS 運用・CI/CD 検証など、あらゆる場面で活用できます。本記事をブックマークしておけば、ORA-00913 に出会っても冷静に的確に対処できるようになります。


本記事は2026年6月時点の情報をもとに、Oracle Database 19c〜23ai での動作確認・公式ドキュメントに基づき作成しています。Oracle のバージョンにより挙動が異なる場合があるため、最新の情報は Oracle 公式ドキュメント(docs.oracle.com)もあわせてご確認ください。