【完全ガイド】ORA-01722: invalid number の原因と解決方法|TO_NUMBER・暗黙型変換・NLS 徹底解説

【完全ガイド】ORA-01722: invalid number の原因と解決方法|TO_NUMBER・暗黙型変換・NLS 徹底解説

Oracle 開発者・データエンジニアが最も頻繁に遭遇する型変換エラー:

SELECT TO_NUMBER('abc') FROM DUAL;
-- ORA-01722: invalid number

SELECT * FROM orders WHERE order_number = '123-456';
-- ORA-01722(暗黙変換で失敗)

「数値に変換できない」というシンプルなメッセージ。しかし、実際には変換エラー・型不一致・暗黙変換の多岐にわたる原因があり、多くのケースでハマります:

  • TO_NUMBER で明示的に変換して失敗
  • VARCHAR2 と NUMBER の暗黙比較(WHERE 句)
  • スペースや制御文字を含む値
  • 通貨記号$, ¥,
  • カンマ区切り1,000,000
  • NLS の小数点(欧州の ,
  • CSV / ETL データの汚れ
  • 予約語UID, LEVEL)が原因
  • 連結演算子||)で数値と文字列混在
  • CASE / DECODE 内での型不一致

さらに、多くの日本語記事が「文字列を数値に変換できないだけ」で終わりますが、実務では:

  • どの行が違反しているかの特定
  • Oracle 12c+ の DEFAULT ... ON CONVERSION ERROR 活用
  • VALIDATE_CONVERSION 関数(12c+)
  • REGEXP_LIKE でのフィルタ
  • NLS_NUMERIC_CHARACTERS の影響
  • ERROR_MESSAGE_DETAILS=ON(21c+ で問題値を表示)
  • is_number ユーザー関数の作成
  • Rails / Java / Python からの防御的コーディング

さらに、本当に厄介なパターンがあります:

-- 一見動くが、実は危険
CREATE TABLE t (id VARCHAR2(10));
INSERT INTO t VALUES ('123');
INSERT INTO t VALUES ('456');
INSERT INTO t VALUES ('abc');  -- ここに非数値

-- 開発時は動く
SELECT * FROM t WHERE id = 123;

-- 本番で ORA-01722('abc' の変換失敗)

「WHERE 句での暗黙変換」 は Oracle の中でも特に予測困難なエラー源です。データが増えるにつれ、いつか必ず発生します。

本記事では、ORA-01722: invalid number完全な原因と解決方法を、リファレンスとして実用的に整理します。有効な数値リテラル、10大発生パターン、5つの診断方法、5つの解決策、Oracle 12c+ の新機能、NLS の影響、Rails/Java/Python 対応、実践シナリオ、FAQまで完全網羅。この1本で ORA-01722 を根本から解決できるようになります。


目次

結論:文字列が数値に変換できない

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

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

ORA-01722: invalid number

-- Oracle 21c+ で ERROR_MESSAGE_DETAILS=ON なら
ORA-01722: unable to convert string value containing 'S' to a number: ENAME
ORA-03302: (ORA-01722 details) invalid string value: SMITH

問題の値と列名が明示される(21c+)。

最速の診断

-- ① 詳細エラー有効化(Oracle 21c+)
ALTER SESSION SET ERROR_MESSAGE_DETAILS = ON;

-- ② 変換可能性チェック(Oracle 12c+)
SELECT column_name, VALIDATE_CONVERSION(column_name AS NUMBER)
FROM my_table;
-- 1 = 変換可能, 0 = 不可

-- ③ 非数値データ検出
SELECT * FROM my_table 
WHERE NOT REGEXP_LIKE(column_name, '^-?[0-9]+(\.[0-9]+)?$');

5つの解決策

#手法使う場面
DEFAULT ON CONVERSION ERROR (12c+)エラー時デフォルト値
VALIDATE_CONVERSION (12c+)事前チェック
REGEXP_LIKE フィルタデータ絞り込み
CASE 式で判定混在データ
TRIM / REPLACE前処理

有効な数値リテラル

✅ OK
123, -456, +789
1.5, -3.14, .5
1e10, 1.5E-3   (指数表記)

❌ NG
'123abc'       (非数値文字)
'1,000'        (カンマ)
'$100'         (通貨記号)
' 123 '        (前後スペース、実は TRIM されれば OK)
'1.5.6'        (複数の小数点)
'12-34'        (ハイフン中央)

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


まず理解する:有効な数値リテラル

Oracle の数値変換ルール

含めて良いもの:

  • 数字 (0-9)
  • 符号 (+/-)
  • 小数点 (.) ※ NLS 依存
  • 指数表記 (e, E)

含めてはいけないもの:

  • 数字以外の文字(a, b, c…)
  • 通貨記号 ($, ¥, €)
  • 千位区切り(デフォルトでは カンマ不可)
  • 特殊文字(-, /, スペース等)※ 位置による

変換例

SELECT TO_NUMBER('123')      FROM DUAL;  -- 123
SELECT TO_NUMBER('-456')     FROM DUAL;  -- -456
SELECT TO_NUMBER('1.5')      FROM DUAL;  -- 1.5
SELECT TO_NUMBER('1e10')     FROM DUAL;  -- 10000000000

-- 全て ORA-01722
SELECT TO_NUMBER('abc')      FROM DUAL;
SELECT TO_NUMBER('1,000')    FROM DUAL;
SELECT TO_NUMBER('$100')     FROM DUAL;
SELECT TO_NUMBER('123abc')   FROM DUAL;

前後スペースは自動除去

SELECT TO_NUMBER('  123  ') FROM DUAL;  -- OK: 123(自動 TRIM)

-- しかし文字列中のスペースは NG
SELECT TO_NUMBER('1 23') FROM DUAL;     -- ORA-01722

NLS の影響

欧州ロケールでは小数点がカンマ:

-- ドイツ語ロケールで
ALTER SESSION SET NLS_NUMERIC_CHARACTERS = ',.';
SELECT TO_NUMBER('1,5') FROM DUAL;  -- OK: 1.5

-- 日本/US ロケール
ALTER SESSION SET NLS_NUMERIC_CHARACTERS = '.,';
SELECT TO_NUMBER('1,5') FROM DUAL;  -- ORA-01722

NLS 依存はバグの温床。フォーマットモデル明示推奨。


【原因①】TO_NUMBER で非数値文字列(最頻出)

症状

SELECT TO_NUMBER('abc') FROM DUAL;
-- ORA-01722

診断

-- 21c+ の詳細エラー
ALTER SESSION SET ERROR_MESSAGE_DETAILS = ON;
SELECT TO_NUMBER('abc') FROM DUAL;
-- ORA-01722: unable to convert string value containing 'a' to a number

解決A: DEFAULT ON CONVERSION ERROR(12c+)

-- 変換失敗時にデフォルト値
SELECT TO_NUMBER('abc' DEFAULT 0 ON CONVERSION ERROR) FROM DUAL;
-- 結果: 0(エラーなし)

-- 大量データで一括処理
SELECT id, 
       TO_NUMBER(amount_str DEFAULT -1 ON CONVERSION ERROR) AS amount
FROM orders;

Oracle 12.2+ で神機能。エラー処理が SQL 内で完結。

解決B: VALIDATE_CONVERSION(12c+)

-- 事前チェック
SELECT id, amount_str,
       CASE VALIDATE_CONVERSION(amount_str AS NUMBER)
         WHEN 1 THEN TO_NUMBER(amount_str)
         ELSE NULL
       END AS amount
FROM orders;

1 = 変換可能, 0 = 変換不可

解決C: REGEXP_LIKE でフィルタ

SELECT TO_NUMBER(amount_str)
FROM orders
WHERE REGEXP_LIKE(amount_str, '^-?[0-9]+(\.[0-9]+)?$');
-- 数値パターンにマッチするもののみ

【原因②】WHERE 句での暗黙変換(超危険)

症状

-- id は VARCHAR2 列
CREATE TABLE t (id VARCHAR2(10));
INSERT INTO t VALUES ('123');
INSERT INTO t VALUES ('456');
INSERT INTO t VALUES ('abc');  -- 非数値混在

SELECT * FROM t WHERE id = 123;
-- ORA-01722('abc' の変換失敗)

なぜ危険か

  • 開発時: データが少なく成功
  • 本番: データ増加で発生
  • オプティマイザ: 実行順序で発生タイミングが変わる

診断

-- 非数値行の特定
SELECT * FROM t 
WHERE NOT REGEXP_LIKE(id, '^-?[0-9]+(\.[0-9]+)?$');

解決A: 文字列側で比較

-- 数値を文字列化
SELECT * FROM t WHERE id = '123';
-- OK: 完全一致検索

-- または TO_CHAR
SELECT * FROM t WHERE id = TO_CHAR(123);

解決B: 数値列側で比較

-- t.id を数値化(ただし非数値行があれば ORA-01722)
SELECT * FROM t WHERE TO_NUMBER(id) = 123;
-- 対処: WHERE 句に事前フィルタ

解決C: 事前フィルタ

SELECT * FROM t 
WHERE REGEXP_LIKE(id, '^-?[0-9]+(\.[0-9]+)?$')
  AND TO_NUMBER(id) = 123;

サブクエリの評価順に注意(オプティマイザが並び変える可能性)。

解決D: DEFAULT ON CONVERSION ERROR

SELECT * FROM t 
WHERE TO_NUMBER(id DEFAULT NULL ON CONVERSION ERROR) = 123;
-- 変換失敗は NULL、比較で FALSE

【原因③】通貨記号・カンマ

症状

SELECT TO_NUMBER('$1,000.50') FROM DUAL;
-- ORA-01722

解決A: FORMAT MODEL 指定

SELECT TO_NUMBER('$1,000.50', '$999,999.99') FROM DUAL;
-- OK: 1000.5

解決B: REPLACE で除去

SELECT TO_NUMBER(REPLACE(REPLACE('$1,000.50', '$'), ',')) FROM DUAL;
-- OK: 1000.5

FORMAT モデル完全リファレンス

-- 千位区切り
TO_NUMBER('1,000,000', '999,999,999')

-- 通貨記号(ロケール依存)
TO_NUMBER('$1000', 'L999999')     -- L = ロケール通貨
TO_NUMBER('$1000', '$999999')     -- $ 固定

-- 前ゼロ
TO_NUMBER('00123', '000000')      -- 123

-- 小数点(NLS)
TO_NUMBER('1,5', '9D9', 'NLS_NUMERIC_CHARACTERS = '',.''')

-- パーセント
TO_NUMBER('50%', '99%')  -- 不可、REPLACE で除去

【原因④】スペース・制御文字

症状

-- 前後スペースは自動 TRIM されるが、中央は NG
SELECT TO_NUMBER('1 2 3') FROM DUAL;  -- ORA-01722

-- タブや制御文字
SELECT TO_NUMBER('123' || CHR(9)) FROM DUAL;  -- ORA-01722(タブ含む)

診断

-- 非印字文字の検出
SELECT id, DUMP(id) FROM t WHERE NOT REGEXP_LIKE(id, '^-?[0-9]+$');
-- DUMP で ASCII コード確認

解決

-- 全空白除去
SELECT TO_NUMBER(REGEXP_REPLACE('1 2 3', '\s')) FROM DUAL;
-- 123

-- ASCII 制御文字除去
SELECT TO_NUMBER(REGEXP_REPLACE(id, '[[:cntrl:]]')) FROM t;

【原因⑤】CSV / ETL データ

症状(現実的なケース)

CSV:
id,name,amount
1,John,1000
2,Mary,2000
3,Bob,N/A       ← 非数値
4,Alice,       ← 空文字(実は OK になる)
5,Tom,$500     ← 通貨記号

解決:全部を耐性のある処理に

-- ステージングテーブル
CREATE TABLE staging (id VARCHAR2(10), name VARCHAR2(50), amount VARCHAR2(20));

-- クリーニング付き移行
INSERT INTO final_orders (id, name, amount)
SELECT 
  TO_NUMBER(id DEFAULT NULL ON CONVERSION ERROR),
  name,
  TO_NUMBER(
    REPLACE(REPLACE(amount, '$'), ',')
    DEFAULT NULL ON CONVERSION ERROR
  )
FROM staging;

エラー行のログ

-- エラー行を別テーブルへ
INSERT INTO staging_errors
SELECT * FROM staging
WHERE VALIDATE_CONVERSION(
  REPLACE(REPLACE(amount, '$'), ',') AS NUMBER
) = 0
  AND amount IS NOT NULL;

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


【原因⑥】予約語による誤変換

症状(罠)

CREATE TABLE t (uid VARCHAR2(50));
INSERT INTO t VALUES ('abc123');

SELECT * FROM t WHERE uid = '4a29d12025012995c231f18eb7704009';
-- ORA-01722

原因

UID は Oracle の予約疑似列(現在のユーザーIDを返す NUMBER):

SELECT UID FROM DUAL;  -- 数値(例: 84)

Oracle が uid を疑似列と解釈 → NUMBER と比較 → 文字列 '4a29...' を数値化しようとして失敗。

解決

A. 列名を変更:

ALTER TABLE t RENAME COLUMN uid TO user_uid;

B. テーブルエイリアスで修飾:

SELECT * FROM t x WHERE x.uid = '4a29...';
-- x. で明示的に列参照

C. 引用符:

SELECT * FROM t WHERE "uid" = '4a29...';

予約語関連は ORA-00904: invalid identifier の記事も参照してください。


【原因⑦】INSERT で数値列に文字列

症状

CREATE TABLE t (id NUMBER, amount NUMBER(10, 2));

INSERT INTO t VALUES (1, 'abc');
-- ORA-01722

解決

A. 数値を渡す:

INSERT INTO t VALUES (1, 1000.50);

B. アプリ側で事前バリデーション:

# Rails
validates :amount, numericality: true

【原因⑧】CASE / DECODE 内の型不一致

症状

SELECT CASE 
         WHEN status = 'A' THEN 100
         WHEN status = 'B' THEN 200
         ELSE 'N/A'   -- ← 型不一致
       END
FROM orders;
-- ORA-01722(オプティマイザが数値へ暗黙変換)

解決

戻り値の型を統一:

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

または全て数値 + NULL:

SELECT CASE 
         WHEN status = 'A' THEN 100
         WHEN status = 'B' THEN 200
         ELSE NULL
       END
FROM orders;

【原因⑨】連結演算子(||)

症状

-- 意図しない暗黙変換
SELECT '合計: ' || total FROM orders;
-- 通常は OK

-- しかし WHERE で
SELECT * FROM orders WHERE 'ID_' || id = 'ID_123';
-- id が NUMBER なら OK
-- id が VARCHAR2 で非数値混在 → 危険なパターン

解決

明示的な型変換:

SELECT * FROM orders WHERE 'ID_' || TO_CHAR(id) = 'ID_123';

【原因⑩】NLS 依存の小数点

症状

-- ドイツ語ロケール(1,5 = 1.5)
ALTER SESSION SET NLS_NUMERIC_CHARACTERS = ',.';
SELECT TO_NUMBER('1.5') FROM DUAL;
-- ORA-01722(. が千位区切り扱い、整数化失敗)

解決A: NLS 明示

SELECT TO_NUMBER('1.5', '9D9', 'NLS_NUMERIC_CHARACTERS = ''.,'''') 
FROM DUAL;

解決B: セッションで統一

ALTER SESSION SET NLS_NUMERIC_CHARACTERS = '.,';

解決C: NLS 非依存に

-- REPLACE で正規化
SELECT TO_NUMBER(REPLACE('1,5', ',', '.')) FROM DUAL;

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

VALIDATE_CONVERSION(Oracle 12c+)

-- 変換可能性チェック
SELECT amount_str,
       VALIDATE_CONVERSION(amount_str AS NUMBER) AS is_valid
FROM orders;
-- 1 = 変換可能
-- 0 = 変換不可

-- 非数値行の抽出
SELECT * FROM orders 
WHERE VALIDATE_CONVERSION(amount_str AS NUMBER) = 0;

DEFAULT ON CONVERSION ERROR(Oracle 12.2+)

-- 変換失敗時のデフォルト値
SELECT TO_NUMBER('abc' DEFAULT 0 ON CONVERSION ERROR)   FROM DUAL;  -- 0
SELECT TO_NUMBER('abc' DEFAULT NULL ON CONVERSION ERROR) FROM DUAL;  -- NULL

REGEXP_LIKE

-- 標準的な数値パターン
REGEXP_LIKE(col, '^-?[0-9]+(\.[0-9]+)?$')

-- 指数表記も許容
REGEXP_LIKE(col, '^-?[0-9]+(\.[0-9]+)?([eE][-+]?[0-9]+)?$')

-- 千位区切り許容
REGEXP_LIKE(col, '^-?[0-9]{1,3}(,[0-9]{3})*(\.[0-9]+)?$')

is_number ユーザー関数

CREATE OR REPLACE FUNCTION is_number(p_str VARCHAR2) 
RETURN VARCHAR2 DETERMINISTIC
IS
  v_num NUMBER;
BEGIN
  v_num := TO_NUMBER(p_str);
  RETURN 'Y';
EXCEPTION
  WHEN VALUE_ERROR THEN RETURN 'N';
  WHEN INVALID_NUMBER THEN RETURN 'N';
END;
/

-- 使用
SELECT id, amount_str, is_number(amount_str) AS valid
FROM orders;

ERROR_MESSAGE_DETAILS(Oracle 21c+)

ALTER SESSION SET ERROR_MESSAGE_DETAILS = ON;

SELECT TO_NUMBER(name) FROM emp;
-- ORA-01722: unable to convert string value containing 'S' to a number: NAME
-- ORA-03302: (ORA-01722 details) invalid string value: SMITH

問題の値と列名が特定できる。

DUMP 関数(非印字文字検出)

SELECT id, DUMP(id, 1010) FROM t 
WHERE VALIDATE_CONVERSION(id AS NUMBER) = 0;
-- DUMP で ASCII コード表示
-- 制御文字(\t \n 等)検出

Rails / Java / Python 対応

Rails ActiveRecord

モデル側のバリデーション(第一防衛線):

class Order < ApplicationRecord
  validates :amount, numericality: true
  validates :amount, presence: true
  
  # カスタム
  validate :amount_is_reasonable
  
  private
  
  def amount_is_reasonable
    return unless amount.present?
    errors.add(:amount, "too large") if amount > 10_000_000
  end
end

エラーハンドリング:

begin
  Order.where("amount > 100").to_a
rescue ActiveRecord::StatementInvalid => e
  if e.message.include?("ORA-01722")
    Rails.logger.error "型変換失敗: #{e.message}"
  end
end

型キャスト:

# 明示的な型変換
amount = params[:amount].to_i  # Integer に
amount = params[:amount].to_d  # BigDecimal に

# 検証してからクエリ
if params[:id].to_s.match?(/^\d+$/)
  Order.find(params[:id])
end

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

Java (JDBC)

// 事前バリデーション
public boolean isValidNumber(String s) {
    if (s == null) return false;
    try {
        new BigDecimal(s.trim());
        return true;
    } catch (NumberFormatException e) {
        return false;
    }
}

// クエリ実行
try {
    stmt.executeQuery("SELECT * FROM t WHERE id = 123");
} catch (SQLException e) {
    if (e.getErrorCode() == 1722) {
        logger.error("数値変換失敗: " + e.getMessage());
        // 対処
    }
}

Python (oracledb)

import oracledb

def is_number(s):
    try:
        float(s)
        return True
    except (ValueError, TypeError):
        return False

try:
    cursor.execute("SELECT * FROM t WHERE id = :1", (123,))
except oracledb.DatabaseError as e:
    error_obj, = e.args
    if error_obj.code == 1722:
        print(f"数値変換失敗: {error_obj.message}")

実践シナリオ

シナリオ1:本番で突然発生する ORA-01722

-- 症状: 開発では動く、本番で ORA-01722
-- 原因: データ増加で非数値が混入

-- 診断
ALTER SESSION SET ERROR_MESSAGE_DETAILS = ON;
SELECT * FROM orders WHERE order_number = 12345;
-- ORA-01722: unable to convert 'ABC-001' to number: ORDER_NUMBER

-- 対処
SELECT * FROM orders 
WHERE order_number = '12345';  -- 文字列で比較

-- 恒久対策
UPDATE orders SET order_number = '0' 
WHERE VALIDATE_CONVERSION(order_number AS NUMBER) = 0;

シナリオ2:CSV データ移行

-- ステージング
CREATE TABLE staging_orders (
  id VARCHAR2(20),
  amount_str VARCHAR2(50)
);

-- 移行 with クレンジング
INSERT INTO orders (id, amount)
SELECT 
  TO_NUMBER(id DEFAULT NULL ON CONVERSION ERROR),
  TO_NUMBER(
    REPLACE(REPLACE(TRIM(amount_str), '$'), ',')
    DEFAULT NULL ON CONVERSION ERROR
  )
FROM staging_orders
WHERE TO_NUMBER(id DEFAULT NULL ON CONVERSION ERROR) IS NOT NULL;

-- エラー行を別ログ
INSERT INTO staging_errors
SELECT * FROM staging_orders
WHERE VALIDATE_CONVERSION(id AS NUMBER) = 0;

シナリオ3:レポート集計での対応

-- 平均金額(非数値行は除外)
SELECT AVG(TO_NUMBER(amount DEFAULT NULL ON CONVERSION ERROR))
FROM orders;

-- 分析関数と組み合わせ
SELECT id, amount,
       AVG(TO_NUMBER(amount DEFAULT NULL ON CONVERSION ERROR)) 
         OVER (PARTITION BY category) AS category_avg
FROM orders;

分析関数の詳細は Oracle 分析関数(OVER/PARTITION BY)の記事も参照してください。

シナリオ4:Rails でのバリデーション設計

class Order < ApplicationRecord
  # 数値バリデーション
  validates :amount, 
    numericality: { greater_than_or_equal_to: 0 },
    presence: true
  
  # 数値以外の文字を許可しない
  before_save :sanitize_amount
  
  private
  
  def sanitize_amount
    self.amount = amount.to_s.gsub(/[^\d.-]/, '').to_d
  end
end

Solid Queue の詳細は Solid Queue 使い方の記事も参照してください。

シナリオ5:MERGE でのクレンジング

MERGE INTO final_orders dst
USING (
  SELECT 
    TO_NUMBER(id DEFAULT NULL ON CONVERSION ERROR) AS id,
    TO_NUMBER(amount DEFAULT NULL ON CONVERSION ERROR) AS amount,
    name
  FROM staging
  WHERE VALIDATE_CONVERSION(id AS NUMBER) = 1
) src
ON (dst.id = src.id)
WHEN MATCHED THEN UPDATE SET dst.amount = src.amount
WHEN NOT MATCHED THEN INSERT VALUES (src.id, src.amount, src.name);

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

シナリオ6:予約語との衝突回避

-- ORA-01722 の隠れた原因: 列名 UID
-- 対処: 列名変更
ALTER TABLE user_ids RENAME COLUMN uid TO user_identifier;

-- または引用符(非推奨)
SELECT "uid" FROM user_ids;

シナリオ7:NLS を無視した堅牢なコード

-- NLS 依存を避ける
CREATE OR REPLACE FUNCTION safe_to_number(p_str VARCHAR2)
RETURN NUMBER DETERMINISTIC
IS
BEGIN
  RETURN TO_NUMBER(
    REPLACE(REPLACE(TRIM(p_str), ',', ''), ' ', '')
    DEFAULT NULL ON CONVERSION ERROR
  );
END;
/

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

docker exec -it oracle-xe sqlplus scott/tiger <<EOF
ALTER SESSION SET ERROR_MESSAGE_DETAILS = ON;

SELECT TO_NUMBER('abc') FROM DUAL;
-- 詳細なエラーメッセージ
EOF

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

シナリオ9:AWS RDS Oracle での対処

-- Parameter Group で ERROR_MESSAGE_DETAILS 設定
ALTER SYSTEM SET error_message_details = 'ON' SCOPE = BOTH;

パラメータ関連は Oracle パラメータ確認(V$PARAMETER)の記事も参照してください。

シナリオ10:Kamal デプロイ後の型検証

docker exec -it db-container sqlplus / as sysdba <<EOF
-- 全 VARCHAR2 列で非数値だけをチェック
SELECT column_name FROM user_tab_columns 
WHERE table_name = 'ORDERS' AND data_type = 'VARCHAR2';
EOF

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


トラブルシューティング

エラーが特定できない

21c+ の詳細エラー有効化:

ALTER SESSION SET ERROR_MESSAGE_DETAILS = ON;

VALIDATE_CONVERSION が使えない

12c 未満: is_number ユーザー関数を作成。

DEFAULT ON CONVERSION ERROR が使えない

12.2 未満: CASE VALIDATE_CONVERSION or ユーザー関数。

実行計画で意図しない変換

EXPLAIN PLAN FOR SELECT * FROM t WHERE id = 123;
-- INTERNAL_FUNCTION(id) = 123 が表示されたら暗黙変換

EXPLAIN PLAN は Oracle EXPLAIN PLAN 見方の記事も参照してください。

インデックス無効化

暗黙変換で関数索引となり、通常のインデックス無効化:

-- 索引が使われない
WHERE varchar_col = 123

-- 関数ベース索引で対応
CREATE INDEX idx ON t(TO_NUMBER(varchar_col));

21c の UNISTR エラー

ORA-01722: unable to convert string value containing UNISTR(...) 

キャラセット互換性なし。DB キャラセットを確認。


よくある質問(FAQ)

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

  • ORA-01722: SQL レベルの数値変換失敗
  • ORA-06502: PL/SQL レベルの数値変換失敗

Q2. DEFAULT ON CONVERSION ERROR が使えるバージョン

Oracle 12.2+

Q3. VALIDATE_CONVERSION のバージョン

Oracle 12.1+

Q4. NULL は数値変換で問題ないか

問題なし。NULL のまま。

Q5. 空文字 ”

NULL 扱い(Oracle の空文字は NULL)。

Q6. 千位区切り

FORMAT モデルREPLACE で除去

Q7. 前後スペース

自動 TRIM。中央のスペースは NG。

Q8. Rails での対応

validates :col, numericality: true で事前検証。

Q9. Autonomous DB での挙動

同じ。ERROR_MESSAGE_DETAILS デフォルト ON。

Q10. エラー詳細を常に見たい

ALTER SYSTEM SET error_message_details = 'ON';

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

VALIDATE_CONVERSION は各行評価、大量データで注意。

Q12. PostgreSQL からの移行

PG の CAST(... AS NUMERIC) は失敗時例外、Oracle と類似。


参考リンク

Oracle 公式


まとめ

ORA-01722: invalid number の要点を再整理します。

エラーの本質

文字列を数値に変換しようとして失敗
→ 明示的変換 (TO_NUMBER) or 暗黙変換 (WHERE 句)
→ 非数値文字が含まれる

エラーメッセージ(21c+)

ORA-01722: unable to convert string value containing 'S' 
to a number: COLUMN_NAME
ORA-03302: (ORA-01722 details) invalid string value: STRING_VALUE

ERROR_MESSAGE_DETAILS = ON で詳細取得。

有効な数値リテラル

✅ 123, -456, 1.5, 1e10
❌ 'abc', '1,000', '$100', '1 2 3'

前後スペース: 自動 TRIM で OK
中央スペース: NG

10大原因

#原因対処
TO_NUMBER 非数値DEFAULT ON CONVERSION ERROR
WHERE 暗黙変換明示的型
通貨/カンマFORMAT モデル/REPLACE
スペース/制御文字REGEXP_REPLACE
CSV/ETLクレンジング
予約語 (UID)列名変更
INSERT 文字列事前検証
CASE 型不一致統一
連結演算子TO_CHAR
NLS 依存明示指定

5つの解決策

-- ① DEFAULT ON CONVERSION ERROR (12.2+)
SELECT TO_NUMBER(x DEFAULT 0 ON CONVERSION ERROR) FROM t;

-- ② VALIDATE_CONVERSION (12.1+)
WHERE VALIDATE_CONVERSION(x AS NUMBER) = 1

-- ③ REGEXP_LIKE フィルタ
WHERE REGEXP_LIKE(x, '^-?[0-9]+(\.[0-9]+)?$')

-- ④ CASE 判定
CASE VALIDATE_CONVERSION(x AS NUMBER)
  WHEN 1 THEN TO_NUMBER(x)
  ELSE NULL
END

-- ⑤ TRIM / REPLACE
TO_NUMBER(REPLACE(REPLACE(x, '$'), ','))

診断コマンド Top 3

-- ① 詳細エラー (21c+)
ALTER SESSION SET ERROR_MESSAGE_DETAILS = ON;

-- ② 変換可能性
SELECT VALIDATE_CONVERSION(x AS NUMBER) FROM t;

-- ③ 非数値検出
SELECT * FROM t 
WHERE NOT REGEXP_LIKE(x, '^-?[0-9]+(\.[0-9]+)?$');

日付エラー3部作との対比

ORA-01722: 数値変換失敗
ORA-01843: 月が無効
ORA-01830: 日付フォーマット早期終了
ORA-01858: 数値文字が期待される

型変換系の姉妹エラー

暗黙変換の危険性

-- ❌ データ増加で必ず問題
SELECT * FROM t WHERE varchar_col = 123;

-- ✅ 明示的比較
SELECT * FROM t WHERE varchar_col = '123';

これらの知識は、Oracle でのアプリ開発・データ移行・ETL・API 開発・レポート・Rails / Java / Python 開発・データクレンジング・本番トラブル対応など、あらゆる場面で活用できます。本記事をブックマークしておけば、ORA-01722 に出会っても冷静に的確に対処できるようになります。


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