【完全ガイド】ORA-00932: inconsistent datatypes の原因と解決方法|UNION・LOB・CASE・型変換 徹底解説

【完全ガイド】ORA-00932: inconsistent datatypes の原因と解決方法|UNION・LOB・CASE・型変換 徹底解説

Oracle 開発者・データエンジニアが日常的に遭遇する型不一致エラー:

SELECT id FROM t1 UNION SELECT name FROM t2;
-- ORA-00932: inconsistent datatypes: expected NUMBER got CHAR

「型が違うよ」というシンプルなメッセージ。しかし、実際には発生パターンが多岐にわたり、特に LOB 型(CLOB/BLOB/NCLOB)では根本的な制限が存在します:

  • UNION で列の型が違う
  • CASE / DECODE の分岐で戻り値の型が違う
  • INSERT / UPDATE で列の型と値が違う
  • JOIN の結合キーの型が違う
  • IN リストの型が違う
  • LOB との比較(CLOB を = で比較不可)
  • LOB を UNION(DISTINCT 発生で不可)
  • LOB を DISTINCT(ソート不可)
  • LONG 型に LIKE(LONG は制限多)
  • オブジェクト型の比較

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

  • expected X got Y のメッセージから具体的な型を読み取る
  • LOB 型(CLOB/BLOB)の根本的な制限(UNION/DISTINCT 不可)
  • DBMS_LOB.SUBSTR による回避
  • 暗黙的型変換の落とし穴
  • CAST vs TO_CHAR/TO_NUMBER/TO_DATE の使い分け
  • UNION vs UNION ALL の重要な違い
  • **オブジェクト型(Object Type)**での特殊な扱い
  • Rails / Java からの型渡し

さらに、多くの開発者を悩ませる LOB の制約:

CREATE TABLE t (id NUMBER, content CLOB);

-- ❌ 全て ORA-00932
SELECT * FROM t WHERE content = 'test';        -- 直接比較不可
SELECT DISTINCT content FROM t;                 -- DISTINCT 不可
SELECT content FROM t UNION SELECT ...;         -- UNION 不可(DISTINCT 発生)
SELECT * FROM t ORDER BY content;               -- ソート不可

-- ✅ 回避策
SELECT * FROM t WHERE DBMS_LOB.SUBSTR(content, 4000) = 'test';
SELECT * FROM t WHERE TO_CHAR(content) = 'test';   -- 4000 バイト以内
SELECT content FROM t UNION ALL SELECT ...;         -- UNION ALL なら OK

これは Oracle 特有の制限で、日本語で本質的な解説がほぼない領域です。

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


目次

結論:型を明示的に変換

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

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

ORA-00932: inconsistent datatypes: expected NUMBER got CHAR
                                    ↑          ↑
                                    期待される型   実際の型

expected と got で具体的な型が示される。LOB エラーなら CLOB / BLOB / NCLOB。

5つの解決策

#手法使う場面
明示的な型変換関数通常の型不一致
CAST標準 SQL 互換
DBMS_LOB.SUBSTRLOB → VARCHAR2
UNION → UNION ALLLOB の集合演算
LOB → VARCHAR2 変換恒久的対策

姉妹エラー3種

エラー意味
ORA-00932一般的な型不一致UNION の列型違い
ORA-01722数値変換失敗TO_NUMBER(‘abc’)
ORA-06502PL/SQL 値エラー変数への代入

LOB の重要な制限

❌ 直接比較(=, <, >, LIKE)
❌ DISTINCT
❌ UNION(DISTINCT 動作)
❌ ORDER BY
❌ GROUP BY

✅ 代替:
DBMS_LOB.SUBSTR
TO_CHAR(4000バイト以内)
UNION ALL
DBMS_LOB.COMPARE

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


Oracle の型変換ルール

暗黙的型変換

Oracle は多くの場合、自動変換:

SELECT * FROM t WHERE id = '123';  -- id は NUMBER
-- 自動的に '123' → 123

変換できない組み合わせ

NUMBER と DATE:

SELECT * FROM orders WHERE order_date = 12345;
-- ORA-00932(暗黙変換不可)

変換可能性マトリクス

From \ ToNUMBERVARCHAR2DATELOB
NUMBER✅ TO_CHAR
VARCHAR2✅ TO_NUMBER✅ TO_DATE✅ CAST
DATE✅ TO_CHAR
LOB✅ DBMS_LOB.SUBSTR

【原因①】UNION での型不一致(最頻出)

症状

SELECT id, name FROM employees
UNION
SELECT department_id, department_name FROM departments;
-- OK: id = NUMBER, department_id = NUMBER(同じ)

SELECT id, hire_date FROM employees
UNION
SELECT department_id, department_name FROM departments;
-- ORA-00932: hire_date=DATE, department_name=VARCHAR2

UNION と UNION ALL の違い

-- UNION: DISTINCT 動作、型が完全一致必要
SELECT ... UNION SELECT ...

-- UNION ALL: 重複許可、型変換が緩い
SELECT ... UNION ALL SELECT ...

解決

明示的な型変換:

SELECT id, TO_CHAR(hire_date) FROM employees
UNION
SELECT department_id, department_name FROM departments;
-- 両方 VARCHAR2 に統一

【原因②】LOB との比較(超危険)

症状

CREATE TABLE t (id NUMBER, content CLOB);
INSERT INTO t VALUES (1, 'test');

-- ❌ 全て ORA-00932
SELECT * FROM t WHERE content = 'test';
SELECT * FROM t WHERE content LIKE 'te%';

LOB は文字列演算子で比較不可

解決A: TO_CHAR(4000 バイト以内)

SELECT * FROM t WHERE TO_CHAR(content) = 'test';
-- content が 4000 バイト超なら ORA-06502

解決B: DBMS_LOB.SUBSTR

SELECT * FROM t 
WHERE DBMS_LOB.SUBSTR(content, 4000, 1) = 'test';
-- 先頭 4000 バイトのみ比較

解決C: DBMS_LOB.INSTR

SELECT * FROM t 
WHERE DBMS_LOB.INSTR(content, 'test') > 0;
-- 部分文字列検索

解決D: DBMS_LOB.COMPARE

SELECT * FROM t
WHERE DBMS_LOB.COMPARE(content, TO_CLOB('test')) = 0;
-- LOB 同士の比較

LOB 関連は Oracle LOB(BLOB/CLOB)操作の記事も参照してください。


【原因③】LOB の UNION / DISTINCT

症状

CREATE TABLE t (id NUMBER, content CLOB);

-- ❌ ORA-00932
SELECT DISTINCT content FROM t;
SELECT content FROM t1 UNION SELECT content FROM t2;

理由: UNION と DISTINCT はソートを内部で実行、LOB はソート不可。

解決A: UNION ALL

SELECT content FROM t1 UNION ALL SELECT content FROM t2;
-- UNION ALL は DISTINCT 動作なし、OK

解決B: TO_CHAR で変換

SELECT DISTINCT TO_CHAR(content) FROM t;
-- 4000 バイト以内なら OK

解決C: DBMS_LOB.SUBSTR

SELECT DISTINCT DBMS_LOB.SUBSTR(content, 4000) FROM t;

解決D: HASH で比較

-- 一意判定に HASH 使用
SELECT DISTINCT DBMS_CRYPTO.HASH(content, DBMS_CRYPTO.HASH_MD5) 
FROM t;

【原因④】CASE の分岐の型不一致

症状

SELECT CASE 
         WHEN status = 'A' THEN 100
         WHEN status = 'B' THEN 200
         ELSE 'unknown'   -- ← 型不一致
       END
FROM orders;
-- ORA-00932: expected NUMBER got CHAR

解決

戻り値の型を統一:

-- A. 全て文字列
SELECT CASE 
         WHEN status = 'A' THEN '100'
         WHEN status = 'B' THEN '200'
         ELSE 'unknown'
       END
FROM orders;

-- B. 全て数値 + NULL
SELECT CASE 
         WHEN status = 'A' THEN 100
         WHEN status = 'B' THEN 200
         ELSE NULL
       END
FROM orders;

CASE の詳細は Oracle CASE 式の記事等も参考にしてください。


【原因⑤】INSERT / UPDATE の型違い

症状

CREATE TABLE t (id NUMBER, dt DATE);

INSERT INTO t VALUES (1, 'abc');
-- ORA-00932 or ORA-01858

解決

INSERT INTO t VALUES (1, TO_DATE('2026-06-15', 'YYYY-MM-DD'));
-- または DATE リテラル
INSERT INTO t VALUES (1, DATE '2026-06-15');

日付関連は ORA-01843: invalid month の記事、ORA-01722: invalid number の記事も参照してください。


【原因⑥】JOIN の型違い

症状

-- users.id は NUMBER、orders.user_code は VARCHAR2
SELECT * FROM users u JOIN orders o ON u.id = o.user_code;
-- 場合により ORA-00932 or ORA-01722(暗黙変換失敗)

解決

両側を統一:

SELECT * FROM users u 
JOIN orders o ON u.id = TO_NUMBER(o.user_code);

-- または
SELECT * FROM users u 
JOIN orders o ON TO_CHAR(u.id) = o.user_code;

⚠️ インデックス無効化の可能性、事前検討。


【原因⑦】IN リストの型違い

症状

SELECT * FROM orders WHERE order_date IN ('2026-06-01', '2026-06-15');
-- ORA-00932 or 暗黙変換

解決

明示的な型変換:

SELECT * FROM orders 
WHERE order_date IN (DATE '2026-06-01', DATE '2026-06-15');

【原因⑧】DECODE の戻り値

症状

SELECT DECODE(status,
         'A', 100,        -- NUMBER
         'B', 200,        -- NUMBER
         'unknown'        -- VARCHAR2 → 型不一致
       ) FROM orders;
-- 暗黙変換で成功する場合もあるが、危険

解決

CASE 式推奨(型チェック厳密)、または統一。


【原因⑨】LONG 型の制限

症状

CREATE TABLE t (id NUMBER, description LONG);

SELECT * FROM t WHERE description LIKE 'IBM%';
-- ORA-00932 or ORA-00997

解決

LONG は CLOB に移行(Oracle は LONG 型を非推奨):

ALTER TABLE t MODIFY description CLOB;

LONG の制限一覧

❌ WHERE 節での使用
❌ ORDER BY, GROUP BY
❌ INDEX 作成
❌ SUBSTR での操作
❌ テーブル関数
❌ RECORD 型のメンバー

LONG は現代の Oracle では使うべきでない


【原因⑩】オブジェクト型(Object Type)

症状

CREATE TYPE address_t AS OBJECT (
  street VARCHAR2(100),
  city VARCHAR2(50)
);

CREATE TABLE customers (id NUMBER, addr address_t);

SELECT DISTINCT addr FROM customers;
-- ORA-00932: MAP / ORDER 関数がないため比較不可

解決

MAP / ORDER 関数を定義:

CREATE OR REPLACE TYPE address_t AS OBJECT (
  street VARCHAR2(100),
  city VARCHAR2(50),
  MAP MEMBER FUNCTION map_val RETURN VARCHAR2
);

CREATE OR REPLACE TYPE BODY address_t AS
  MAP MEMBER FUNCTION map_val RETURN VARCHAR2 IS
  BEGIN
    RETURN street || '|' || city;
  END;
END;
/

-- これで比較可能
SELECT DISTINCT addr FROM customers;

診断ツール完全リファレンス

USER_TAB_COLUMNS で列型確認

SELECT column_name, data_type, data_length
FROM user_tab_columns
WHERE table_name = 'MY_TABLE'
ORDER BY column_id;

式の型を DUMP

SELECT DUMP(expression) FROM DUAL;
-- 内部型コード表示

DBMS_LOB の情報

-- LOB のサイズ
SELECT DBMS_LOB.GETLENGTH(content) FROM t;

-- LOB の一部
SELECT DBMS_LOB.SUBSTR(content, 100, 1) FROM t;

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

解決策① 明示的な型変換関数

-- NUMBER → VARCHAR2
TO_CHAR(num_col)

-- VARCHAR2 → NUMBER
TO_NUMBER(str_col)

-- VARCHAR2 → DATE
TO_DATE(str_col, 'YYYY-MM-DD')

-- DATE → VARCHAR2
TO_CHAR(date_col, 'YYYY-MM-DD')

-- 12c+ の DEFAULT ON CONVERSION ERROR
TO_NUMBER(str DEFAULT NULL ON CONVERSION ERROR)
TO_DATE(str DEFAULT NULL ON CONVERSION ERROR, 'YYYY-MM-DD')

解決策② CAST

-- 標準 SQL 互換
CAST(num_col AS VARCHAR2(10))
CAST(str_col AS NUMBER)
CAST(str_col AS DATE)

解決策③ DBMS_LOB 関連

-- SUBSTR で先頭部分取得
DBMS_LOB.SUBSTR(clob_col, 4000, 1)

-- INSTR で部分検索
DBMS_LOB.INSTR(clob_col, 'search')

-- COMPARE で LOB 同士
DBMS_LOB.COMPARE(clob_col, TO_CLOB('str'))

解決策④ UNION → UNION ALL

-- UNION(DISTINCT、LOB 不可)
SELECT clob_col FROM t1 UNION SELECT clob_col FROM t2;
-- ORA-00932

-- UNION ALL(重複許可、LOB OK)
SELECT clob_col FROM t1 UNION ALL SELECT clob_col FROM t2;

解決策⑤ LOB → VARCHAR2 恒久的変換

-- LOB が実際に短い場合、VARCHAR2 に移行
ALTER TABLE t ADD (new_col VARCHAR2(4000));
UPDATE t SET new_col = DBMS_LOB.SUBSTR(clob_col, 4000);
ALTER TABLE t DROP COLUMN clob_col;
ALTER TABLE t RENAME COLUMN new_col TO clob_col;

Rails / Java / Python 対応

Rails ActiveRecord

カラム型の変更:

class ChangeContentToText < ActiveRecord::Migration[8.0]
  def change
    change_column :articles, :content, :string, limit: 4000
    # CLOB → VARCHAR2
  end
end

エラーハンドリング:

begin
  Article.where("content = ?", "test")
rescue ActiveRecord::StatementInvalid => e
  if e.message.include?("ORA-00932")
    Rails.logger.error "型不一致: #{e.message}"
    # 対処: DBMS_LOB.SUBSTR 使用
    Article.where("DBMS_LOB.SUBSTR(content, 4000) = ?", "test")
  end
end

Rails 8 系の詳細は Rails 8 アップグレードガイドの記事、find/find_by/where の記事も参照してください。

Java (JDBC)

// CLOB との比較
PreparedStatement ps = conn.prepareStatement(
    "SELECT * FROM articles WHERE DBMS_LOB.SUBSTR(content, 4000) = ?"
);
ps.setString(1, "test");

Python (oracledb)

import oracledb

# CLOB の扱い
cursor.execute("""
    SELECT * FROM articles 
    WHERE DBMS_LOB.SUBSTR(content, 4000) = :1
""", ("test",))

# CLOB を文字列として読む
oracledb.defaults.fetch_lobs = False
# CLOB を str として自動変換

実践シナリオ

シナリオ1:UNION での型統一

-- ❌ エラー
SELECT id, name FROM employees
UNION
SELECT id, hire_date FROM employees;

-- ✅ 型統一
SELECT id, name FROM employees
UNION ALL
SELECT id, TO_CHAR(hire_date, 'YYYY-MM-DD') FROM employees;

シナリオ2:レガシー LONG → CLOB 移行

-- 1. 新列作成
ALTER TABLE legacy_t ADD (content_new CLOB);

-- 2. データ移行
UPDATE legacy_t SET content_new = content;

-- 3. 旧列削除
ALTER TABLE legacy_t DROP COLUMN content;

-- 4. リネーム
ALTER TABLE legacy_t RENAME COLUMN content_new TO content;

シナリオ3:CLOB の全文検索

-- Oracle Text 使用
CREATE INDEX idx_articles_content ON articles(content) 
INDEXTYPE IS CTXSYS.CONTEXT;

SELECT * FROM articles 
WHERE CONTAINS(content, 'oracle') > 0;

シナリオ4:CASE 式の型統一

-- 全て VARCHAR2 に統一
SELECT 
  CASE type
    WHEN 'N' THEN TO_CHAR(numeric_val)
    WHEN 'D' THEN TO_CHAR(date_val, 'YYYY-MM-DD')
    WHEN 'S' THEN string_val
    ELSE 'unknown'
  END AS unified_value
FROM mixed_table;

シナリオ5:Rails からの CLOB 検索

# app/models/article.rb
class Article < ApplicationRecord
  scope :search_content, ->(term) {
    where("DBMS_LOB.INSTR(content, ?) > 0", term)
  }
end

# 使用
Article.search_content('Oracle')

Rails 8 系の詳細は Rails 8 アップグレードガイドの記事、find/find_by/where の記事、Solid Queue 使い方の記事も参照してください。

シナリオ6:Data Pump インポート後の型変換

-- 移行元と型が違う
CREATE TABLE staging (id VARCHAR2(20), amount VARCHAR2(20));

-- 型変換して本テーブルへ
INSERT INTO orders (id, amount)
SELECT TO_NUMBER(id), TO_NUMBER(amount) FROM staging;

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

シナリオ7:MERGE での型注意

MERGE INTO orders dst
USING (
  SELECT 100 AS id, TO_DATE('2026-06-15', 'YYYY-MM-DD') AS dt 
  FROM DUAL
) src
ON (dst.id = src.id)
WHEN MATCHED THEN UPDATE SET dt = src.dt;

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

シナリオ8:Docker Oracle での再現

docker exec -it oracle-xe sqlplus scott/tiger <<EOF
CREATE TABLE t (id NUMBER, c CLOB);
INSERT INTO t VALUES (1, 'test');

-- エラー
SELECT * FROM t WHERE c = 'test';

-- 解決
SELECT * FROM t WHERE DBMS_LOB.SUBSTR(c, 4000) = 'test';
EOF

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

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

@Repository
public class ArticleRepo {
    @Autowired
    private JdbcTemplate jdbc;
    
    public List<Article> searchContent(String keyword) {
        return jdbc.query(
            "SELECT * FROM articles WHERE DBMS_LOB.INSTR(content, ?) > 0",
            new Object[]{keyword},
            new ArticleRowMapper()
        );
    }
}

シナリオ10:Autonomous DB での対応

-- Autonomous DB (23ai)
-- LOB 制限は同じ
-- JSON 型と Object 型は 23ai で改善
CREATE TABLE t (data JSON);

トラブルシューティング

「expected」「got」が読めない

ORA-00932: inconsistent datatypes: expected - got CLOB

空欄 の場合、UNION / DISTINCT で LOB が原因。

UNION でエラー、UNION ALL で動く

UNION は DISTINCT 動作、LOB / LONG は不可。

暗黙変換に頼るとインデックス無効化

-- ❌ 暗黙変換
WHERE id = '123';  -- id は NUMBER

-- 索引スキャン: TO_CHAR(id) = '123' に変換
-- → 索引無効化

DECODE の型が予測困難

CASE 使用(型チェック厳密)。

Rails でのマッピングエラー

# CLOB → String マッピング
config.active_record.oracle_enhanced_column_type_map = {
  clob: :text
}

PostgreSQL からの移行

  • PG の TEXT → Oracle の VARCHAR2(4000) or CLOB
  • PG の DATE / TIMESTAMP → Oracle の DATE / TIMESTAMP

よくある質問(FAQ)

Q1. ORA-00932 と ORA-01722 の違い

  • 00932: 一般的な型不一致
  • 01722: 数値変換の失敗

Q2. LOB を UNION したい

UNION ALL に変更、または DBMS_LOB.SUBSTR 使用。

Q3. LOB を DISTINCT

不可。HASH や SUBSTR で回避。

Q4. TO_CHAR と CAST の違い

  • TO_CHAR: Oracle 特有、フォーマット指定可
  • CAST: 標準 SQL、シンプル

Q5. LONG は使うべき?

NO。CLOB に移行推奨。

Q6. 暗黙変換の危険性

インデックス無効化、パフォーマンス低下、意図しない動作。

Q7. CASE の型統一

戻り値の型を全て同じに、または NULL 併用。

Q8. Rails での対応

CLOB 列は DBMS_LOB.* 関数使用。

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

TO_CHAR / TO_NUMBER は各行実行、大量データで注意。

Q10. NULL の扱い

NULL は型を持たない、他の型と組み合わせ可能。

Q11. Autonomous DB での対応

同じ。JSON 型は 23ai で強化。

Q12. Object Type の比較

MAP / ORDER 関数定義が必須。


参考リンク

Oracle 公式


まとめ

ORA-00932: inconsistent datatypes の要点を再整理します。

エラーの本質

SQL 実行時の型不一致
- 比較・代入・UNION・CASE 等で型が合わない
- LOB 特有の根本的な制限
- 暗黙的型変換の限界

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

ORA-00932: inconsistent datatypes: expected X got Y
                                    ↑        ↑
                                期待される型  実際の型

expected が空欄 = LOB 関連の可能性
got が CLOB/BLOB/NCLOB = LOB の制限

姉妹エラー3種

エラー意味
ORA-00932一般的な型不一致
ORA-01722数値変換失敗
ORA-06502PL/SQL 値エラー

10大原因

#原因対処
UNION 型不一致TO_CHAR / TO_NUMBER
LOB との比較DBMS_LOB.SUBSTR
LOB の UNION/DISTINCTUNION ALL
CASE 分岐の型統一
INSERT/UPDATE 型違い明示的変換
JOIN の型違い両側統一
IN リスト型違いDATE/TO_DATE
DECODE の型CASE に変更
LONG の LIKECLOB 移行
Object TypeMAP/ORDER 関数

5つの解決策

-- ① 明示的型変換
TO_CHAR(num)
TO_NUMBER(str)
TO_DATE(str, 'YYYY-MM-DD')

-- ② CAST
CAST(x AS VARCHAR2(10))

-- ③ DBMS_LOB
DBMS_LOB.SUBSTR(clob, 4000)
DBMS_LOB.INSTR(clob, 'text')
DBMS_LOB.COMPARE(clob1, clob2)

-- ④ UNION → UNION ALL
SELECT clob FROM t1 UNION ALL SELECT clob FROM t2;

-- ⑤ LOB → VARCHAR2 移行
ALTER TABLE ... (4000 バイト以内なら)

LOB の重要な制限

❌ 直接比較(=, <, >, LIKE)
❌ DISTINCT(内部ソート)
❌ UNION(DISTINCT 動作)
❌ ORDER BY, GROUP BY

✅ 代替:
DBMS_LOB.SUBSTR で VARCHAR2 化
TO_CHAR(4000 バイト以内)
UNION ALL
DBMS_LOB.COMPARE / INSTR

CAST vs TO_CHAR/TO_NUMBER

CAST:        標準 SQL、シンプル
TO_XXX:      Oracle 特有、フォーマット指定可、細かい制御

暗黙変換の落とし穴

-- ❌ インデックス無効化
WHERE varchar_col = 123;

-- ✅ 明示的変換
WHERE varchar_col = '123';
-- または
WHERE TO_NUMBER(varchar_col) = 123;  -- 関数索引が必要

これらの知識は、Oracle でのアプリ開発・データ移行・ETL・レガシー LONG → CLOB 移行・BLOB/CLOB 操作・Rails / Java / Python 開発・レポート・分析など、あらゆる場面で活用できます。本記事をブックマークしておけば、ORA-00932 に出会っても冷静に的確に対処できるようになります。


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