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が文字列を数値に変換(暗黙変換)しようとしたときに、変換できない文字列が含まれていると発生します。
-- 文字列 '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型の列を数値リテラルと比較する場合に起きます。
-- product_code は VARCHAR2(20) 型 -- 大部分は数値だが、一部に 'N/A' や 'SPECIAL' が含まれている SELECT * FROM products WHERE product_code = 100; -- → product_code 列全体に対して TO_NUMBER(product_code) が暗黙に実行される -- → 'N/A' を数値変換しようとして ORA-01722
-- 文字列同士の比較にする → 型変換が発生しない SELECT * FROM products WHERE product_code = '100'; -- → 全行に対して文字列比較が行われるだけなのでエラーにならない
VARCHAR2型の列と比較する場合は、比較対象も文字列リテラル(シングルクォートで囲む)にするのが原則です。
WHERE varchar_col = 100 ではなく WHERE varchar_col = '100' と書いてください。
パターン:INSERT 文の型不一致
INSERT文で列のデータ型と値の型が一致しない場合に発生します。
-- price 列は NUMBER 型 INSERT INTO products (product_id, name, price) VALUES (1, 'テスト商品', '無料'); -- → '無料' を NUMBER に変換できない → ORA-01722
INSERT INTO products (product_id, name, price) VALUES (1, 'テスト商品', 0); -- → NUMBER 型の列に数値を挿入 → 問題なし
INSERT … SELECT の場合は、SELECT側の列の型とINSERT先の列の型を確認してください。
-- 移行元テーブルの列が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の場合に発生します。
-- 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
-- 方法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) を書くと、数値変換できない行があればエラーになります。
-- code列に '001', '002', 'N/A' が混在している SELECT code, name FROM items ORDER BY TO_NUMBER(code); -- → 'N/A' の変換で ORA-01722
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 は変換可能なら 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_NUMBER に 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 での安全変換(全バージョン対応)
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_CHARやTO_NUMBERを使い、どちらの型に寄せるかを明示する - CHECK制約で入力を制限する:VARCHAR2列に数値のみ許容する場合は
CHECK (REGEXP_LIKE(col, '^[0-9]+$'))で制約をかける - データクレンジング:既存データに非数値が混入している場合は、
VALIDATE_CONVERSIONやREGEXP_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型を使い、暗黙変換に頼らない設計にすること
