【完全ガイド】ORA-01858: a non-numeric character was found where a numeric was expected の原因と解決方法|TO_DATE の罠・NLS_DATE_FORMAT まで徹底解説
- 作成日 2026.07.22
- Oracle Database
Oracle で日付変換の際、必ず遭遇する第3の日付エラー:
SELECT TO_DATE('ABCD-06-15', 'YYYY-MM-DD') FROM DUAL;
ORA-01858: a non-numeric character was found where a numeric was expected
翻訳: 「数値が期待された位置に、非数値の文字があった」
Oracle の日付変換エラー3兄弟の一つ:
- ORA-01843: 「無効な月」(月の位置に解釈できない値)
- ORA-01830: 「フォーマット終了後に余り」(入力が長すぎ)
- ORA-01858: 「数値位置に非数値」(形式が根本的に違う)
現場では:
- CSV インポートで一部の行だけエラー
TO_DATE(SYSDATE, 'YYYY-MM-DD')で発生(!)- 全角スペース・全角数字の混入
- 空文字
''を渡してエラー - NLS_DATE_FORMAT との不一致
- 他 DB(Informatica/ODBC)経由でエラー
MONフォーマットに数値月を渡すエラー
このエラーは、フォーマットと入力の型が根本的に合わないことを示します。「余分がある」ORA-01830 とは違い、**「そもそも数値じゃない」**という状況。
さらに、Oracle 開発者が驚くほどよくハマる罠があります:
-- 一見正しそうだが実は誤り
SELECT TO_DATE(SYSDATE, 'YYYY-MM-DD') FROM DUAL;
-- ORA-01858
理由: SYSDATE は DATE 型なので、内部的に文字列に変換される際、NLS_DATE_FORMAT に依存。もし DD-MON-RR だと 15-JUN-26 になり、YYYY-MM-DD フォーマットの Y 位置に 1(数値OK)ではなく - があると混乱。この暗黙変換の罠は、日本語圏でほぼ解説されていません。
本記事では、ORA-01858: a non-numeric character was found where a numeric was expected の完全な原因と解決方法を、リファレンスとして実用的に整理します。エラーの本質、3種類の日付エラー比較、7大発生パターン、TO_DATE(SYSDATE) の罠、REGEXP_LIKE 事前チェック、12c+ 新機能、Rails/Java/Python 対応、実践シナリオ、予防策、FAQまで完全網羅。この1本で ORA-01858 を根本から解決できるようになります。
- 1. 結論:フォーマットと入力の型を一致させる
- 2. まず理解する:エラーの本質
- 3. 【原因①】数値位置に文字
- 4. 【原因②】TO_DATE(SYSDATE) の罠
- 5. 【原因③】MON と MM の混同
- 6. 【原因④】全角文字の混入
- 7. 【原因⑤】空文字・空白のみ
- 8. 【原因⑥】NULL 混入
- 9. 【原因⑦】NLS_DATE_FORMAT との不一致
- 10. 診断ツール
- 11. Rails / Java / Python 対応
- 12. 実践シナリオ
- 13. 予防のベストプラクティス
- 14. トラブルシューティング
- 15. よくある質問(FAQ)
- 15.1. Q1. ORA-01858 と ORA-01830 と ORA-01843 の違い
- 15.2. Q2. TO_DATE(SYSDATE) の代替
- 15.3. Q3. 全角数字の変換
- 15.4. Q4. Oracle は空文字を NULL 扱い
- 15.5. Q5. NLS_DATE_FORMAT の変更
- 15.6. Q6. Rails での対応
- 15.7. Q7. Autonomous DB での対応
- 15.8. Q8. AWS RDS Oracle
- 15.9. Q9. 空文字を扱う設計
- 15.10. Q10. 大量データの変換失敗
- 15.11. Q11. 数値カラムでも発生?
- 15.12. Q12. 12c+ の DEFAULT ON CONVERSION ERROR
- 16. 参考リンク
- 17. まとめ
結論:フォーマットと入力の型を一致させる
時間がない方向けに、最速の対処を先に示します。
基本原則
フォーマットが「数値」を期待する位置に「非数値」があると発生。
3つの日付エラー比較
| エラー | 意味 |
|---|---|
| ORA-01843 | 月の位置に無効な値(例: 13月) |
| ORA-01830 | 入力が長すぎる(フォーマット後に余り) |
| ORA-01858 | 数値位置に非数値(型の根本ミスマッチ) |
7大原因
| # | 原因 | 例 |
|---|---|---|
| ① | 数値位置に文字 | TO_DATE('ABCD-06-15', 'YYYY-MM-DD') |
| ② | TO_DATE(SYSDATE) の罠 | TO_DATE(SYSDATE, 'YYYY-MM-DD') |
| ③ | MON と MM の混同 | TO_DATE('06', 'MON') |
| ④ | 全角文字 | TO_DATE('2026-06-15', 'YYYY-MM-DD') |
| ⑤ | 空文字・空白 | TO_DATE('', 'YYYY-MM-DD') |
| ⑥ | NULL 混入 | TO_DATE(v_str, 'YYYY-MM-DD') |
| ⑦ | NLS_DATE_FORMAT 不一致 | セッション設定違い |
最速解決コード
-- ① 明示的フォーマット指定
TO_DATE('2026-06-15', 'YYYY-MM-DD')
-- ② DATE の再変換は避ける(そのまま使う)
-- ❌ TO_DATE(SYSDATE, 'YYYY-MM-DD')
-- ✅ SYSDATE または TRUNC(SYSDATE)
-- ③ 事前チェック(REGEXP_LIKE)
CASE
WHEN REGEXP_LIKE(v_str, '^\d{4}-\d{2}-\d{2}$')
THEN TO_DATE(v_str, 'YYYY-MM-DD')
ELSE NULL
END
-- ④ 12c+ の DEFAULT ON CONVERSION ERROR
TO_DATE(v_str DEFAULT NULL ON CONVERSION ERROR, 'YYYY-MM-DD')
詳細は以下で解説します。
まず理解する:エラーの本質
メッセージの意味
ORA-01858: a non-numeric character was found where a numeric was expected
↑
「数値が期待された位置に」
フォーマット指定で数値位置(YYYY, MM, DD 等)に、数字以外の文字があると発生。
Oracle 公式の説明
Cause: The input data to be converted using a date format model was incorrect.
The input data did not contain a number where a number was required
by the format model.
Action: Fix the input data or the date format model to make sure the elements
match in number and type.
翻訳: 「入力データがフォーマットモデルの数値要求位置に数値がなかった」
具体例
入力: 'ABCD-06-15'
フォーマット: 'YYYY-MM-DD'
Y Y Y Y | - | M M | - | D D
↓ ↓ ↓ ↓
A B C D ← 数値じゃない!
→ ORA-01858
3つの日付エラー完全比較
-- ORA-01858: 数値位置に非数値
TO_DATE('ABCD-06-15', 'YYYY-MM-DD')
-- [ABCD が YYYY 位置] → 非数値
-- ORA-01830: 入力が長すぎる
TO_DATE('2026-06-15 14:30', 'YYYY-MM-DD')
-- [変換OK] [余分] → 長い
-- ORA-01843: 月の位置に無効な値
TO_DATE('31/13/1985', 'DD/MM/YYYY')
-- [DD] [??] → 13月は無効
エラーが発生するタイミング
- INSERT/UPDATE の値変換
- WHERE 句の比較
- SELECT 句での計算
- PL/SQL の代入
DML 全般で発生の可能性。
【原因①】数値位置に文字
症状
TO_DATE('ABCD-06-15', 'YYYY-MM-DD')
-- ORA-01858
原因
YYYY 位置に ABCD という文字。明らかに不正な入力データ。
診断
-- 不正データを検出
SELECT date_str
FROM staging_data
WHERE NOT REGEXP_LIKE(date_str, '^\d{4}-\d{2}-\d{2}$');
解決
入力データを修正、またはバリデーションで除外:
-- 事前チェック付き変換
CASE
WHEN REGEXP_LIKE(date_str, '^\d{4}-\d{2}-\d{2}$')
THEN TO_DATE(date_str, 'YYYY-MM-DD')
ELSE NULL
END
【原因②】TO_DATE(SYSDATE) の罠
症状
一見正しく見えるコード:
-- 深夜バッチで発生
SELECT TO_DATE(SYSDATE, 'YYYY-MM-DD') FROM DUAL;
-- ORA-01858
なぜ発生するか
SYSDATE は DATE 型。TO_DATE は文字列を DATE に変換する関数なので、SYSDATE は内部的に文字列に変換される:
SYSDATE
↓ 暗黙変換
NLS_DATE_FORMAT に従い文字列化
↓ 例: '15-JUN-26'(DD-MON-RR)
TO_DATE('15-JUN-26', 'YYYY-MM-DD')
↓
'YYYY' 位置に '15-JU' → 数値じゃない!
↓
ORA-01858
診断
-- NLS_DATE_FORMAT 確認
SHOW PARAMETER nls_date_format
-- SYSDATE の文字列表現
SELECT SYSDATE FROM DUAL;
解決
そもそも DATE を DATE に変換する必要はない:
-- ❌ 不要な変換
TO_DATE(SYSDATE, 'YYYY-MM-DD')
-- ✅ そのまま使う
SYSDATE
-- ✅ 時刻部分を切り捨てたいなら
TRUNC(SYSDATE)
-- ✅ 文字列にしたい場合
TO_CHAR(SYSDATE, 'YYYY-MM-DD')
-- ✅ 別の DATE に変換したい場合(レア)
TO_DATE(TO_CHAR(SYSDATE, 'YYYY-MM-DD'), 'YYYY-MM-DD')
この罠が現場で発生するパターン
-- レガシー PL/SQL でよくある
BEGIN
IF v_input_date = TO_DATE(SYSDATE, 'YYYY-MM-DD') THEN
-- 同じ日付判定したいが、エラー
END IF;
END;
-- 正しくは
BEGIN
IF TRUNC(v_input_date) = TRUNC(SYSDATE) THEN
-- 日付部分だけ比較
END IF;
END;
【原因③】MON と MM の混同
症状
-- 数値月を MON フォーマットに
TO_DATE('06', 'MON')
-- ORA-01858
-- 月名を MM フォーマットに
TO_DATE('JUN', 'MM')
-- ORA-01858
動作の違い
- MM: 数値月(01-12)を期待
- MON: 月名略称(JAN, FEB…)を期待
- MONTH: 月名フル(JANUARY…)を期待
解決
入力とフォーマットを一致:
-- 数値月
TO_DATE('06', 'MM') -- OK
-- 月名略称
TO_DATE('JUN', 'MON') -- OK
-- 月名フル
TO_DATE('JUNE', 'MONTH') -- OK
NLS_LANGUAGE の影響
-- 英語環境
TO_DATE('15-JAN-26', 'DD-MON-RR') -- OK
-- 日本語環境
TO_DATE('15-1月-26', 'DD-MON-RR') -- 環境依存
-- 明示的に言語指定
TO_DATE('15-JAN-26', 'DD-MON-RR', 'NLS_DATE_LANGUAGE = AMERICAN')
言語設定関連は Oracle パラメータ確認(V$PARAMETER)の記事、月フォーマットの詳細は ORA-01843: not a valid month の記事も参照してください。
【原因④】全角文字の混入
症状
TO_DATE('2026-06-15', 'YYYY-MM-DD') -- 全角数字
-- ORA-01858
TO_DATE('2026-06-15 ', 'YYYY-MM-DD') -- 全角スペース
-- ORA-01858
診断
-- 全角混入を検出
SELECT date_str
FROM t
WHERE date_str <> ASCIISTR(date_str);
-- ASCII 変換で違いがあれば非 ASCII 文字含む
-- 特定の全角文字検出
SELECT date_str
FROM t
WHERE REGEXP_LIKE(date_str, '[^\x00-\x7F]');
解決
アプリ側で全角→半角変換、または SQL で:
-- CONVERT で ASCII 変換
TO_DATE(
CONVERT(REGEXP_REPLACE(date_str, '[^\x00-\x7F]', ''), 'US7ASCII'),
'YYYY-MM-DD'
)
-- TRANSLATE で個別変換
TO_DATE(
TRANSLATE(date_str, '0123456789', '0123456789'),
'YYYY-MM-DD'
)
Java 側での対処(推奨):
String halfWidth = Normalizer.normalize(input, Normalizer.Form.NFKC);
【原因⑤】空文字・空白のみ
症状
TO_DATE('', 'YYYY-MM-DD')
-- ORA-01858
TO_DATE(' ', 'YYYY-MM-DD')
-- ORA-01858
解決A: NULLIF で NULL 化
TO_DATE(NULLIF(TRIM(date_str), ''), 'YYYY-MM-DD')
-- 空文字なら NULL、それ以外は変換
解決B: CASE で分岐
CASE
WHEN TRIM(date_str) IS NULL THEN NULL
WHEN REGEXP_LIKE(date_str, '^\d{4}-\d{2}-\d{2}$')
THEN TO_DATE(date_str, 'YYYY-MM-DD')
ELSE NULL
END
空文字と NULL の Oracle 挙動
Oracle は空文字を NULL として扱う(他 DB と違う):
SELECT * FROM t WHERE col = ''; -- 常に0行(col = NULL と同じ)
SELECT * FROM t WHERE col IS NULL; -- 空文字も含む
移行時に注意。
【原因⑥】NULL 混入
症状
-- v_date_str が NULL
SELECT TO_DATE(v_date_str, 'YYYY-MM-DD') FROM DUAL;
-- 通常は NULL を返すが、環境によりエラー
動作
- 通常:
TO_DATE(NULL, ...)は NULL を返す - 稀に: 実装依存でエラー
対処
-- NULL チェック
CASE
WHEN v_date_str IS NULL THEN NULL
ELSE TO_DATE(v_date_str, 'YYYY-MM-DD')
END
-- NVL で明示的
TO_DATE(NVL(v_date_str, '1900-01-01'), 'YYYY-MM-DD')
-- 12c+ の DEFAULT ON CONVERSION ERROR
TO_DATE(v_date_str DEFAULT NULL ON CONVERSION ERROR, 'YYYY-MM-DD')
【原因⑦】NLS_DATE_FORMAT との不一致
症状
-- セッション設定: NLS_DATE_FORMAT = 'DD-MON-RR'
SELECT * FROM t WHERE date_col = '2026-06-15';
-- 内部的に TO_DATE('2026-06-15', 'DD-MON-RR')
-- YYYY-MM-DD 形式なのに DD-MON-RR で解釈 → ORA-01858
診断
-- 現在の設定
SHOW PARAMETER nls_date_format
-- または
SELECT SYS_CONTEXT('USERENV', 'NLS_DATE_FORMAT') FROM DUAL;
-- ODBC/JDBC 経由での確認
SELECT * FROM nls_session_parameters WHERE parameter = 'NLS_DATE_FORMAT';
解決A: 明示的フォーマット
-- 常に明示
TO_DATE('2026-06-15', 'YYYY-MM-DD')
-- NLS_DATE_FORMAT に依存しない
解決B: セッション統一
-- 起動時 or 接続時に統一
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
Rails / JDBC / ODBC 全て統一。
他 DB 連携(Informatica 等)
Informatica の ODBC ドライバはデフォルト 'YYYY-MM-DD'
Oracle デフォルトは環境により 'DD-MON-RR'
→ フォーマット不一致で ORA-01858
対処: 初期化文字列で NLS 設定
Initialization String = alter session set NLS_DATE_FORMAT='YYYY-MM-DD'
診断ツール
問題データの検出
-- 数値以外の文字を含む行
SELECT date_str
FROM staging_data
WHERE NOT REGEXP_LIKE(date_str, '^[0-9\-/: .]+$');
空文字・空白のみ
SELECT date_str
FROM t
WHERE TRIM(date_str) IS NULL AND date_str IS NOT NULL;
NULL vs 空文字
SELECT date_str,
CASE
WHEN date_str IS NULL THEN 'NULL'
WHEN TRIM(date_str) IS NULL THEN 'EMPTY'
WHEN NOT REGEXP_LIKE(date_str, '^\d') THEN 'INVALID'
ELSE 'OK'
END AS status
FROM t;
バルク処理でエラー行分離
INSERT INTO target
SELECT TO_DATE(date_str, 'YYYY-MM-DD')
FROM source
LOG ERRORS INTO source_err REJECT LIMIT UNLIMITED;
SELECT ora_err_number$, ora_err_mesg$, date_str
FROM source_err
WHERE ora_err_number$ = 1858;
LOG ERRORS の詳細は ORA-00001: unique constraint violated の記事も参考にしてください。
Rails / Java / Python 対応
Rails ActiveRecord
# ✅ ActiveRecord は日付型を直接変換
Order.create!(order_date: Date.parse('2026-06-15'))
# ❌ 生 SQL で文字列
sql = "INSERT INTO orders (order_date) VALUES ('#{date_str}')"
# ✅ 生 SQL で明示的
sql = "INSERT INTO orders (order_date) VALUES (TO_DATE(:d, 'YYYY-MM-DD'))"
ActiveRecord::Base.connection.execute(sql, d: '2026-06-15')
# 初期化スクリプト
Rails.application.config.after_initialize do
ActiveRecord::Base.connection.execute(
"ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS'"
)
end
Rails 8 系の詳細は Rails 8 アップグレードガイドの記事、rails db:migrate 使い方の記事、Solid Queue 使い方の記事も参照してください。
Java (JDBC)
// ❌ 文字列で渡す(エラーの温床)
ps.setString(1, dateStr);
// ✅ java.sql.Date で渡す
ps.setDate(1, java.sql.Date.valueOf("2026-06-15"));
// ✅ LocalDate(Java 8+)
ps.setObject(1, LocalDate.of(2026, 6, 15));
// バリデーション
if (!input.matches("\\d{4}-\\d{2}-\\d{2}")) {
throw new IllegalArgumentException("Invalid date");
}
Python (oracledb)
import oracledb
from datetime import date
# ✅ date 型を直接
cursor.execute(
"INSERT INTO orders (order_date) VALUES (:d)",
d=date(2026, 6, 15)
)
# ❌ 文字列
cursor.execute(
"INSERT INTO orders (order_date) VALUES (:d)",
d="2026-06-15"
)
# → NLS_DATE_FORMAT 依存でエラーの可能性
実践シナリオ
シナリオ1:CSV インポート
-- staging から検証しながら投入
INSERT INTO orders (id, order_date)
SELECT id,
CASE
WHEN REGEXP_LIKE(order_date_str, '^\d{4}-\d{2}-\d{2}$')
THEN TO_DATE(order_date_str, 'YYYY-MM-DD')
ELSE NULL
END
FROM staging_orders;
シナリオ2:Data Pump 後の検証
-- インポート後の日付列検証
SELECT COUNT(*)
FROM imported_data
WHERE date_col IS NULL AND date_col_str IS NOT NULL;
Data Pump の詳細は Oracle Data Pump 使い方の記事を参照してください。
シナリオ3:REST API からの受信
-- ISO 8601 の可能性も含めて柔軟に
CASE
WHEN REGEXP_LIKE(input, '^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$')
THEN TO_DATE(input, 'YYYY-MM-DD"T"HH24:MI:SS')
WHEN REGEXP_LIKE(input, '^\d{4}-\d{2}-\d{2}$')
THEN TO_DATE(input, 'YYYY-MM-DD')
ELSE NULL
END
ISO 8601 の詳細は ORA-01830: date format picture ends の記事、無効月は ORA-01843: not a valid month の記事も参照してください。
シナリオ4:他 DB からの取り込み
-- Informatica 経由のセッション設定
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD';
-- または明示的
INSERT INTO t SELECT TO_DATE(date_str, 'YYYY-MM-DD') FROM ext_source;
シナリオ5:レガシー PL/SQL 修正
-- 旧コード(罠)
IF v_input = TO_DATE(SYSDATE, 'YYYY-MM-DD') THEN ...
-- 修正
IF TRUNC(v_input) = TRUNC(SYSDATE) THEN ...
シナリオ6:バッチ処理での安全な変換
DECLARE
v_date DATE;
BEGIN
FOR rec IN (SELECT id, date_str FROM staging) LOOP
BEGIN
v_date := TO_DATE(rec.date_str, 'YYYY-MM-DD');
INSERT INTO target VALUES (rec.id, v_date);
EXCEPTION
WHEN OTHERS THEN
INSERT INTO err_log VALUES (rec.id, rec.date_str, SQLERRM);
END;
END LOOP;
END;
/
シナリオ7:Docker Oracle での動作確認
docker exec -it oracle-xe sqlplus scott/tiger <<EOF
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD';
SELECT TO_DATE(SYSDATE, 'YYYY-MM-DD') FROM DUAL;
-- 上記なら OK
-- ↓ 罠を確認
ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MON-RR';
SELECT TO_DATE(SYSDATE, 'YYYY-MM-DD') FROM DUAL;
-- ORA-01858 発生
EOF
Docker 関連は docker daemon 接続エラーの記事、Docker no space left on device の記事も参照してください。
シナリオ8:Kamal デプロイ後の DB 初期化
# 環境変数で NLS 統一
env:
clear:
NLS_LANG: JAPANESE_JAPAN.AL32UTF8
NLS_DATE_FORMAT: "YYYY-MM-DD HH24:MI:SS"
Kamal 2 デプロイの詳細は Kamal 2 デプロイの記事を参照してください。
シナリオ9:Solid Queue ジョブ時刻
-- ジョブスケジュール時刻の変換
SELECT job_id,
TO_TIMESTAMP(scheduled_at_str, 'YYYY-MM-DD HH24:MI:SS.FF')
FROM job_queue;
Solid Queue の詳細は Solid Queue 使い方の記事も参照してください。
シナリオ10:全角混入の検出と修正
-- 全角数字を含む行を検出
SELECT id, date_str
FROM t
WHERE date_str <> ASCIISTR(date_str)
OR REGEXP_LIKE(date_str, '[0-9]');
-- 修正 SQL
UPDATE t
SET date_str = TRANSLATE(date_str,
'0123456789',
'0123456789')
WHERE REGEXP_LIKE(date_str, '[0-9]');
予防のベストプラクティス
1. 常に明示的なフォーマット指定
-- ❌ NLS 依存
TO_DATE(str)
-- ✅ 明示
TO_DATE(str, 'YYYY-MM-DD')
2. TO_DATE(DATE型) は使わない
-- ❌ SYSDATE は DATE 型なので無意味
TO_DATE(SYSDATE, 'YYYY-MM-DD')
-- ✅ そのまま使う
SYSDATE
TRUNC(SYSDATE)
3. アプリ側で日付型を渡す
ps.setDate(1, java.sql.Date.valueOf("2026-06-15"));
文字列化を避けるのが根本策。
4. 事前バリデーション
CASE
WHEN REGEXP_LIKE(str, '^\d{4}-\d{2}-\d{2}$')
THEN TO_DATE(str, 'YYYY-MM-DD')
ELSE NULL
END
5. 12c+ の ON CONVERSION ERROR
TO_DATE(str DEFAULT NULL ON CONVERSION ERROR, 'YYYY-MM-DD')
6. セッション設定の統一
-- 全接続で
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
7. 全角/空文字の除去
NULLIF(TRIM(str), '')
8. ISO 8601 標準採用
2026-06-15T14:30:45Z
曖昧さのない標準形式。
9. LOG ERRORS でバルク対応
INSERT ... LOG ERRORS INTO t_err REJECT LIMIT UNLIMITED;
10. テストデータの多様化
- 正常データ
- NULL / 空文字
- 全角混入
- 不正フォーマット
- 空白のみ
トラブルシューティング
一部の行だけエラー
-- 不正データを特定
SELECT id, date_str
FROM t
WHERE NOT REGEXP_LIKE(date_str, '^\d{4}-\d{2}-\d{2}$');
アプリ側は文字列だが CI で失敗
// テスト環境の NLS 設定を確認
System.getenv("NLS_DATE_FORMAT")
開発機で動くが本番で失敗
NLS_DATE_FORMAT の環境差異:
SELECT SYS_CONTEXT('USERENV', 'NLS_DATE_FORMAT') FROM DUAL;
環境で違えばセッション設定で統一。
ODBC/JDBC ドライバ経由
接続文字列に初期化スクリプト:
initSQL=ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'
レガシー PL/SQL のリファクタリング
-- 一括検索
SELECT text FROM user_source
WHERE UPPER(text) LIKE '%TO_DATE(SYSDATE%';
該当箇所を全て修正。
RAISE_APPLICATION_ERROR で明示化
BEGIN
...
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -1858 THEN
RAISE_APPLICATION_ERROR(-20001,
'Date format mismatch: ' || v_date_str);
ELSE RAISE;
END IF;
END;
/
よくある質問(FAQ)
Q1. ORA-01858 と ORA-01830 と ORA-01843 の違い
- ORA-01858: 数値位置に非数値(型ミスマッチ)
- ORA-01830: 入力が長すぎる
- ORA-01843: 月の位置に無効な値
Q2. TO_DATE(SYSDATE) の代替
-- そのまま
SYSDATE
-- 時刻切り捨て
TRUNC(SYSDATE)
-- 特定精度
TRUNC(SYSDATE, 'HH24')
Q3. 全角数字の変換
TRANSLATE(str, '0123456789', '0123456789')
Q4. Oracle は空文字を NULL 扱い
'' IS NULL -- TRUE(Oracle 特有)
他 DB と挙動が違う。
Q5. NLS_DATE_FORMAT の変更
-- セッション
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD';
-- システム
ALTER SYSTEM SET NLS_DATE_FORMAT = 'YYYY-MM-DD' SCOPE = SPFILE;
パラメータ設定の詳細は Oracle パラメータ確認(V$PARAMETER)の記事を参照してください。
Q6. Rails での対応
# ActiveRecord は自動変換
Model.create!(date: Date.parse(str))
Q7. Autonomous DB での対応
同じ挙動。プラットフォーム関係なし。
Q8. AWS RDS Oracle
同じ挙動。Parameter Group で NLS 設定可能。
Q9. 空文字を扱う設計
-- 空文字排除
NULLIF(TRIM(str), '')
Q10. 大量データの変換失敗
-- LOG ERRORS で継続
INSERT ... LOG ERRORS INTO t_err REJECT LIMIT UNLIMITED;
Q11. 数値カラムでも発生?
-- 数値変換でも似たエラー(ORA-01722)
TO_NUMBER('ABC')
-- ORA-01722: invalid number
ORA-01858 は主に日付、ORA-01722 は数値。姉妹エラー。
Q12. 12c+ の DEFAULT ON CONVERSION ERROR
TO_DATE(str DEFAULT NULL ON CONVERSION ERROR, 'YYYY-MM-DD')
-- 変換失敗時 NULL を返す
バルク処理で有用。
参考リンク
Oracle 公式
- Oracle Database Error Messages: ORA-01858
- TO_DATE Function
- Datetime Format Models
- NLS_DATE_FORMAT Parameter
まとめ
ORA-01858: a non-numeric character was found where a numeric was expected の要点を再整理します。
エラーの本質
フォーマット: YYYY (数値期待)
入力: ABCD (非数値)
↓
ORA-01858 発生
3つの日付エラー比較
| エラー | 意味 | 例 |
|---|---|---|
| ORA-01843 | 月の位置に無効な値 | 13月 |
| ORA-01830 | 入力が長すぎる | ‘YYYY-MM-DD’ に ‘2026-06-15 14:00’ |
| ORA-01858 | 数値位置に非数値 | ‘YYYY-MM-DD’ に ‘ABCD-06-15’ |
7大原因
| # | 原因 | 対処 |
|---|---|---|
| ① | 数値位置に文字 | 事前検証 |
| ② | TO_DATE(SYSDATE) | そのまま使う |
| ③ | MON vs MM 混同 | フォーマット確認 |
| ④ | 全角文字 | TRANSLATE |
| ⑤ | 空文字・空白 | NULLIF(TRIM(…), ”) |
| ⑥ | NULL 混入 | NULL チェック |
| ⑦ | NLS_DATE_FORMAT 不一致 | セッション統一 |
TO_DATE(SYSDATE) の罠
-- ❌ NG(NLS 依存)
TO_DATE(SYSDATE, 'YYYY-MM-DD')
-- ✅ そのまま
SYSDATE
-- ✅ 日付部分だけ
TRUNC(SYSDATE)
DATE 型を DATE に変換する必要はない。
事前検証パターン
CASE
WHEN str IS NULL THEN NULL
WHEN TRIM(str) IS NULL THEN NULL
WHEN REGEXP_LIKE(str, '^\d{4}-\d{2}-\d{2}$')
THEN TO_DATE(str, 'YYYY-MM-DD')
ELSE NULL
END
予防のベストプラクティス
- 明示的なフォーマット指定
- TO_DATE(DATE型) は使わない
- アプリで日付型を渡す
- 事前バリデーション
- 12c+ の ON CONVERSION ERROR
- セッション設定の統一
- 全角/空文字の除去
- ISO 8601 標準採用
- LOG ERRORS でバルク対応
事故防止
- フォーマット文字列を必ず明示
- SYSDATE は文字列変換不要
- NLS_DATE_FORMAT の環境差を意識
- 入力データを事前検証
- アプリで日付型を使う
これらの知識は、Oracle での日常開発・CSV インポート・データ移行・ETL・レガシー保守・Rails / Java / Python 開発・他 DB 連携など、あらゆる場面で活用できます。本記事をブックマークしておけば、ORA-01858 に出会っても迷わず的確に対処できるようになります。
本記事は2026年6月時点の情報をもとに、Oracle Database 19c〜23ai での動作確認・公式ドキュメントに基づき作成しています。Oracle のバージョンにより挙動が異なる場合があるため、最新の情報は Oracle 公式ドキュメント(docs.oracle.com)もあわせてご確認ください。
-
前の記事
【完全ガイド】ORA-01830: date format picture ends before converting の原因と解決方法|TO_TIMESTAMP・ISO 8601・タイムゾーンまで徹底解説 2026.07.22
-
次の記事
【完全ガイド】ORA-01031: insufficient privileges の原因と解決方法|GRANT・ROLE・PL/SQL の罠まで徹底解説 2026.07.23
コメントを書く