【Oracle】ORA-01722: 数値が無効です の原因と解決方法完全ガイド|暗黙変換・WHERE句・INSERT・JOINまで解説

【Oracle】ORA-01722: 数値が無効です の原因と解決方法完全ガイド|暗黙変換・WHERE句・INSERT・JOINまで解説 Oracle

ORA-01722: 数値が無効です(英語: invalid number)は、Oracle環境で非常によく遭遇するエラーです。文字列を数値に変換しようとしたときに、変換できない値が含まれていると発生します。WHERE句・INSERT文・JOIN条件・ORDER BYなどあらゆる場面で起こり得るため、発生箇所の特定が難しいこともあります。本記事では発生パターンごとの原因と対処法を体系的に解説します。

この記事でわかること

  • ORA-01722 の発生メカニズム(暗黙の型変換とは)
  • WHERE 句での比較が原因のパターンと対処法
  • INSERT 文の型不一致が原因のパターンと対処法
  • JOIN 条件での発生パターンと対処法
  • TO_NUMBER が失敗するパターンと安全な変換方法
  • 問題のある行(データ)を特定するSQL
  • VALIDATE_CONVERSION(12c R2+)による事前チェック
  • DEFAULT ON CONVERSION ERROR(12c R2+)による安全な変換
スポンサーリンク

ORA-01722 の発生メカニズム

このエラーは、Oracleが文字列を数値に変換(暗黙変換)しようとしたときに、変換できない文字列が含まれていると発生します。

ORA-01722 が発生する最もシンプルな例
-- 文字列 'ABC' を数値として扱おうとしている
SELECT TO_NUMBER('ABC') FROM dual;
-- → ORA-01722: 数値が無効です

Oracleは異なるデータ型同士の比較・代入時に暗黙の型変換を行います。VARCHAR2列とNUMBER列を比較すると、OracleはVARCHAR2を数値に変換しようとします。このとき、VARCHAR2列に数値に変換できない値(空白・アルファベット・記号等)が1行でも含まれているとエラーになります。

暗黙変換の方向に注意
OracleはVARCHAR2とNUMBERを比較する場合、VARCHAR2側を数値に変換します(NUMBER側を文字列にするのではありません)。そのため、VARCHAR2列に1行でも数値以外の値があるとエラーになります。

パターン:WHERE 句での文字列と数値の比較

最も多い発生パターンです。VARCHAR2型の列を数値リテラルと比較する場合に起きます。

NG: VARCHAR2列を数値リテラルと比較(ORA-01722の原因)
-- product_code は VARCHAR2(20) 型
-- 大部分は数値だが、一部に 'N/A' や 'SPECIAL' が含まれている
SELECT * FROM products
WHERE product_code = 100;
-- → product_code 列全体に対して TO_NUMBER(product_code) が暗黙に実行される
-- → 'N/A' を数値変換しようとして ORA-01722
OK: 文字列リテラルで比較する(暗黙変換を回避)
-- 文字列同士の比較にする → 型変換が発生しない
SELECT * FROM products
WHERE product_code = '100';
-- → 全行に対して文字列比較が行われるだけなのでエラーにならない
対処法のポイント
VARCHAR2型の列と比較する場合は、比較対象も文字列リテラル(シングルクォートで囲む)にするのが原則です。WHERE varchar_col = 100 ではなく WHERE varchar_col = '100' と書いてください。

パターン:INSERT 文の型不一致

INSERT文で列のデータ型と値の型が一致しない場合に発生します。

NG: NUMBER列に文字列値を挿入
-- price 列は NUMBER 型
INSERT INTO products (product_id, name, price)
VALUES (1, 'テスト商品', '無料');
-- → '無料' を NUMBER に変換できない → ORA-01722
OK: 適切な数値を指定する
INSERT INTO products (product_id, name, price)
VALUES (1, 'テスト商品', 0);
-- → NUMBER 型の列に数値を挿入 → 問題なし

INSERT … SELECT の場合は、SELECT側の列の型とINSERT先の列の型を確認してください。

INSERT … SELECT での型不一致を確認する
-- 移行元テーブルの列がVARCHAR2で、移行先がNUMBERの場合
INSERT INTO new_products (product_id, name, price)
SELECT
    product_id,
    name,
    TO_NUMBER(price_text)   -- 明示的に変換する
FROM old_products
WHERE VALIDATE_CONVERSION(price_text AS NUMBER) = 1;  -- 変換可能な行だけ

パターン:JOIN 条件での型不一致

結合条件で一方がVARCHAR2、他方がNUMBERの場合に発生します。

NG: VARCHAR2列とNUMBER列のJOIN
-- orders.customer_code は VARCHAR2(20)
-- customers.customer_id は NUMBER
SELECT o.order_id, c.name
FROM orders o
JOIN customers c ON o.customer_code = c.customer_id;
-- → customer_code が暗黙にTO_NUMBERされる
-- → customer_code に 'GUEST' のような値があると ORA-01722
OK: 明示的に型を揃える
-- 方法1: NUMBER側を文字列に変換する(VARCHAR2列にインデックスがある場合に有利)
SELECT o.order_id, c.name
FROM orders o
JOIN customers c ON o.customer_code = TO_CHAR(c.customer_id);

-- 方法2: VARCHAR2側を数値に変換し、変換できない行を除外する
SELECT o.order_id, c.name
FROM orders o
JOIN customers c ON TO_NUMBER(o.customer_code) = c.customer_id
WHERE REGEXP_LIKE(o.customer_code, '^[0-9]+$');

パターン:ORDER BY での暗黙変換

VARCHAR2型の列を数値順にソートしようとして ORDER BY TO_NUMBER(col) を書くと、数値変換できない行があればエラーになります。

NG: 数値変換できない行がある列でTO_NUMBERソート
-- code列に '001', '002', 'N/A' が混在している
SELECT code, name FROM items
ORDER BY TO_NUMBER(code);
-- → 'N/A' の変換で ORA-01722
OK: CASE式で安全にソートする
SELECT code, name FROM items
ORDER BY
    CASE WHEN REGEXP_LIKE(code, '^[0-9]+$') THEN TO_NUMBER(code) ELSE 999999 END,
    code;
-- 数値変換できない値は末尾に配置される

問題のある行(データ)を特定するSQL

「どの行のどの値が数値変換できないのか」を特定するSQLです。

REGEXP_LIKE で非数値の行を抽出する

数値以外の値が含まれる行を検出する
-- 整数のみ許容する場合
SELECT product_code, name
FROM products
WHERE NOT REGEXP_LIKE(product_code, '^[0-9]+$');
-- 結果: N/A, SPECIAL, (空文字列) 等が検出される

-- 小数・負数を含む数値を許容する場合
SELECT product_code, name
FROM products
WHERE NOT REGEXP_LIKE(product_code, '^-?[0-9]+(\.[0-9]+)?$');

VALIDATE_CONVERSION で変換可否を判定する(12c R2+)

VALIDATE_CONVERSION ── 数値変換の可否を判定
-- VALIDATE_CONVERSION は変換可能なら 1、不可なら 0 を返す
SELECT
    product_code,
    name,
    VALIDATE_CONVERSION(product_code AS NUMBER) AS is_numeric
FROM products
WHERE VALIDATE_CONVERSION(product_code AS NUMBER) = 0;
-- → 数値に変換できない行だけ抽出される

安全な数値変換方法

DEFAULT ON CONVERSION ERROR(12c R2 以降)

Oracle 12c R2 以降では、TO_NUMBERDEFAULT 値 ON CONVERSION ERROR を追加できます。変換失敗時にエラーにせずデフォルト値を返します。

DEFAULT ON CONVERSION ERROR ── 変換失敗時にデフォルト値を返す
SELECT
    product_code,
    TO_NUMBER(product_code DEFAULT 0 ON CONVERSION ERROR) AS numeric_code
FROM products;
-- 'N/A' → 0, '100' → 100, 'SPECIAL' → 0
-- ORA-01722 は発生しない

CASE + REGEXP_LIKE での安全変換(全バージョン対応)

CASE + REGEXP_LIKE ── 全バージョンで使える安全変換
SELECT
    product_code,
    CASE
        WHEN REGEXP_LIKE(product_code, '^-?[0-9]+(\.[0-9]+)?$')
        THEN TO_NUMBER(product_code)
        ELSE NULL
    END AS numeric_code
FROM products;
-- 数値変換可能な場合のみ変換し、それ以外はNULLを返す

ORA-01722 の発生場面と対処法まとめ

発生場面 原因 対処法
WHERE varchar_col = 数値 VARCHAR2列が暗黙にTO_NUMBERされる 数値リテラルを文字列リテラルに変更(= '100'
INSERT VALUES NUMBER列に文字列値を挿入 正しいデータ型の値を指定する
INSERT … SELECT SELECT側のVARCHAR2列にNUMBER変換できない値 VALIDATE_CONVERSIONで変換可能行のみ挿入
JOIN条件 VARCHAR2列とNUMBER列のJOIN TO_CHAR(number_col)で文字列に揃える
ORDER BY TO_NUMBER(col) 列に数値以外の値が含まれる CASE + REGEXP_LIKEで安全にソート
TO_NUMBER(‘文字列’) 変換できない文字列を明示変換 DEFAULT ON CONVERSION ERROR(12c R2+)を使う

ORA-01722 を予防する設計指針

  • 数値データにはNUMBER型を使う:「数値しか入らない」列をVARCHAR2で定義すると、将来的に非数値データが混入するリスクがある
  • バインド変数の型を正しく指定する:アプリケーションからバインド変数を使う場合、NUMBER列にはsetInt/setLong等で数値型をバインドする
  • 暗黙変換に頼らない:型が異なる列同士の比較には明示的に TO_CHARTO_NUMBER を使い、どちらの型に寄せるかを明示する
  • CHECK制約で入力を制限する:VARCHAR2列に数値のみ許容する場合は CHECK (REGEXP_LIKE(col, '^[0-9]+$')) で制約をかける
  • データクレンジング:既存データに非数値が混入している場合は、VALIDATE_CONVERSIONREGEXP_LIKE で検出・修正する

まとめ

ORA-01722 は「文字列を数値に変換できない」という単純な原因ですが、Oracleの暗黙変換によってWHERE句・JOIN・ORDER BY等あらゆる場所で発生し得るため、原因箇所の特定が難しいことがあります。

  • VARCHAR2型の列と数値リテラルを比較すると、Oracle はVARCHAR2側をTO_NUMBERで変換する。1行でも変換できない値があるとエラー
  • 対処の基本は型を明示的に揃えること。VARCHAR2列には文字列リテラル('100')で比較する
  • JOIN条件では TO_CHAR(number_col) で文字列側に寄せるのが安全
  • 問題行の特定には REGEXP_LIKE(col, '^[0-9]+$') の否定、または VALIDATE_CONVERSION(col AS NUMBER) = 0 が使える
  • Oracle 12c R2 以降は TO_NUMBER(col DEFAULT 0 ON CONVERSION ERROR) で変換失敗時のデフォルト値を指定できる
  • 根本的な予防策は、数値データにはNUMBER型を使い、暗黙変換に頼らない設計にすること