【Oracle】ORA-01861の原因と解決方法|TO_DATE・日付フォーマット・NLS_DATE_FORMATの注意点

【Oracle】ORA-01861の原因と解決方法|TO_DATE・日付フォーマット・NLS_DATE_FORMATの注意点 Oracle

ORA-01861: literal does not match format string は、文字列の日付や時刻をDATE/TIMESTAMPへ変換するときに、値の形と指定した書式モデルが一致していない場合に発生するOracleエラーです。

日本語環境では「リテラルが書式文字列と一致しません」と表示されます。TO_DATETO_TIMESTAMP、WHERE句の日付比較、INSERT/UPDATE時の暗黙変換でよく起きます。

先に結論
ORA-01861は、日付文字列と書式モデルを完全に合わせると解消できます。固定の日付なら DATE 'YYYY-MM-DD'、文字列を変換するなら TO_DATE('2026/05/10', 'YYYY/MM/DD') のように、値と書式を同じ並びにします。NLS設定に依存する文字列比較や、DATE列へ不要な TO_DATE をかける書き方は避けます。
スポンサーリンク

ORA-01861とは

Oracle公式のエラー説明では、ORA-01861は指定したリテラルが書式文字列と一致していない場合に発生するエラーです。つまり、Oracleが「この文字列はこのフォーマットでは日付として読めない」と判断した状態です。

確認するもの 見るポイント
入力値 '2026-05-10' 区切り文字、年月日の順番、時刻の有無
書式モデル 'YYYY/MM/DD' 入力値と同じ区切り・同じ桁になっているか
列の型 DATE / VARCHAR2 DATE列に文字列変換を重ねていないか
NLS設定 NLS_DATE_FORMAT 暗黙変換が環境依存になっていないか

まず確認するSQL

原因を切り分けるときは、問題の値だけを DUAL で変換してみると早いです。実テーブルのSQLをいきなり直すより、値と書式の不一致を小さく再現できます。

check-format.sql
-- NG: 値は YYYY-MM-DD なのに、書式は YYYY/MM/DD になっている
SELECT TO_DATE('2026-05-10', 'YYYY/MM/DD')
FROM dual;

-- OK: 値と書式の区切り文字を合わせる
SELECT TO_DATE('2026-05-10', 'YYYY-MM-DD')
FROM dual;

日付の書式モデル全体は OracleのTO_CHAR / TO_DATE日付フォーマット記事 で整理しています。ORA-01861を直すときも、最終的には入力値と書式モデルを対応させる作業になります。

よくある原因

原因 失敗例 修正方針
区切り文字が違う '2026-05-10''YYYY/MM/DD' ハイフンなら書式も YYYY-MM-DD にする
年月日の順番が違う '10/05/2026''YYYY/MM/DD' 実データが日/月/年なのか年/月/日なのか確認する
時刻があるのに書式にない '2026-05-10 09:30:00''YYYY-MM-DD' HH24:MI:SS まで指定する
月名の言語が違う '10-MAY-2026' NLS_DATE_LANGUAGE を指定する
DATE列にTO_DATEをかけている TO_DATE(order_date, ...) DATE列はそのまま比較する
文字列日付を暗黙変換している order_date = '2026/05/10' DATE リテラルか TO_DATE を使う

TO_DATEの書式を合わせる

TO_DATE は文字列をDATE型へ変換する関数です。Oracle公式ドキュメントでも、NLS設定への依存を避けるため、書式モデルを明示することが推奨されています。

to-date-patterns.sql
-- YYYY-MM-DD 形式
SELECT TO_DATE('2026-05-10', 'YYYY-MM-DD') FROM dual;

-- YYYY/MM/DD 形式
SELECT TO_DATE('2026/05/10', 'YYYY/MM/DD') FROM dual;

-- 時刻まで含む形式
SELECT TO_DATE('2026-05-10 09:30:00', 'YYYY-MM-DD HH24:MI:SS') FROM dual;

-- 日本語や英語の月名を扱う場合はNLS_DATE_LANGUAGEも検討する
SELECT TO_DATE('10-MAY-2026', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE=American') FROM dual;

ポイントは、文字列の見た目を先に確認し、その見た目に合わせて書式モデルを書くことです。MMMI の取り違え、YYYYYY の混在、時刻の有無は特に見落としやすいです。

FX修飾子を使って厳密に判定する

Oracleの書式モデルには FX 修飾子があります。通常の変換では空白や一部の表記ゆれが許容されることがありますが、FX を付けると入力値と書式をより厳密に一致させます。CSVや外部連携データの品質を確認したいときに役立ちます。

fx-format.sql
-- 厳密に YYYY-MM-DD だけを許可したい場合
SELECT TO_DATE('2026-05-10', 'FXYYYY-MM-DD')
FROM dual;

-- 形式がずれるとORA-01861などの変換エラーで検知しやすい
SELECT TO_DATE('2026/05/10', 'FXYYYY-MM-DD')
FROM dual;

FX は、ゆるく変換してほしい場面では使いにくい一方、入力形式を固定したいバッチやデータ移行では原因の切り分けに便利です。

DATE列を文字列と比較しない

DATE列を文字列リテラルと比較すると、OracleがセッションのNLS設定に従って暗黙変換します。環境によって通ったり落ちたりするため、実務では避けたほうが安全です。

date-column-compare.sql
-- NG: 文字列がNLS_DATE_FORMATに依存する
SELECT *
FROM orders
WHERE order_date = '2026/05/10';

-- OK: 日付だけを指定するならDATEリテラルを使う
SELECT *
FROM orders
WHERE order_date = DATE '2026-05-10';

-- OK: 文字列を受け取るならTO_DATEで明示する
SELECT *
FROM orders
WHERE order_date = TO_DATE('2026/05/10', 'YYYY/MM/DD');

DATE 'YYYY-MM-DD' はANSI日付リテラルです。時刻を含まない固定日付なら、TO_DATE より短く、NLS設定にも依存しません。

DATE列にTO_DATEをかけない

ORA-01861でよくあるのが、すでにDATE型の列に TO_DATE をかけてしまうパターンです。TO_DATE は文字列をDATEへ変換する関数なので、DATE列に使うと暗黙的に文字列化され、NLS設定に巻き込まれます。

do-not-to-date-date-column.sql
-- NG: order_date がDATE型ならTO_DATEをかけない
SELECT *
FROM orders
WHERE TO_DATE(order_date, 'YYYY-MM-DD') >= TO_DATE('2026-05-01', 'YYYY-MM-DD');

-- OK: DATE列はDATE値と比較する
SELECT *
FROM orders
WHERE order_date >= DATE '2026-05-01';

列の型が分からない場合は、テーブル定義を確認します。DATE型なら変換する対象は列ではなく、外から渡ってくる文字列です。

時刻を含むDATE列の検索

OracleのDATE型は、日付だけでなく時分秒も持ちます。WHERE order_date = DATE '2026-05-10' は、2026年5月10日 00:00:00 の行だけに一致します。その日の全データを取りたい場合は範囲条件にします。

date-range.sql
-- 2026-05-10 の全データを取得する
SELECT *
FROM orders
WHERE order_date >= DATE '2026-05-10'
  AND order_date <  DATE '2026-05-11';

TRUNC(order_date) = DATE '2026-05-10' と書く方法もありますが、列に関数をかけると通常のインデックスを使いにくくなることがあります。まずは上限未満の範囲条件を基本にすると扱いやすいです。

TIMESTAMP型ならTO_TIMESTAMPを使う

列が TIMESTAMP 型の場合は、DATEではなくTIMESTAMPとして比較します。小数秒まで含む文字列を TO_DATE で処理しようとすると、書式不足や型の不一致でつまずきやすくなります。

timestamp-format.sql
-- TIMESTAMP列を小数秒付きの文字列と比較する例
SELECT *
FROM access_logs
WHERE created_at >= TO_TIMESTAMP('2026-05-10 09:30:00.123', 'YYYY-MM-DD HH24:MI:SS.FF3');

-- 固定値ならTIMESTAMPリテラルも使える
SELECT *
FROM access_logs
WHERE created_at >= TIMESTAMP '2026-05-10 09:30:00.123';

INSERTやUPDATEで発生する場合

INSERTやUPDATEでDATE列へ文字列を入れるときも、暗黙変換に頼るとORA-01861の原因になります。SQLに日付文字列を書くなら、型が分かる形で渡します。

insert-update-date.sql
-- NG: セッションのNLS_DATE_FORMATに依存する
INSERT INTO orders (order_id, order_date)
VALUES (1001, '2026/05/10');

-- OK: 固定日付ならDATEリテラル
INSERT INTO orders (order_id, order_date)
VALUES (1001, DATE '2026-05-10');

-- OK: 文字列の形式が決まっているならTO_DATE
UPDATE orders
SET order_date = TO_DATE('2026/05/10 09:30:00', 'YYYY/MM/DD HH24:MI:SS')
WHERE order_id = 1001;

NLS_DATE_FORMATを確認する

書式を省略した TO_DATE('2026/05/10') や、DATE列と文字列の比較は、NLS_DATE_FORMAT の影響を受けます。環境差でエラーが出る場合は、まずセッション設定を確認します。

check-nls-date-format.sql
SELECT *
FROM nls_session_parameters
WHERE parameter IN ('NLS_DATE_FORMAT', 'NLS_DATE_LANGUAGE');

-- 検証用にセッションだけ変更する例
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';

NLS設定の全体像は OracleのNLS設定記事 も参考になります。ただし、アプリやバッチのSQLでは、NLS設定を変えるよりもSQL側で書式を明示するほうが事故を減らせます。

バインド変数を使う場合

アプリケーションから日付条件を渡す場合は、文字列連結でSQLを作るより、バインド変数を使います。可能ならアプリ側の日時型をDATE/TIMESTAMPとして渡すのが安全です。文字列として渡す場合でも、書式をSQL側で固定します。

bind-variable.sql
-- 文字列パラメータを受ける場合は、SQL側で書式を固定する
SELECT *
FROM orders
WHERE order_date >= TO_DATE(:from_date, 'YYYY-MM-DD')
  AND order_date <  TO_DATE(:to_date,   'YYYY-MM-DD');

文字列連結はSQLインジェクションの問題だけでなく、日付フォーマットの揺れも持ち込みます。画面入力値、CSV、外部APIなどから来る日付は、入力形式を決めてから変換しましょう。

変換できない値をNULLや既定値に逃がす場合

データ移行やCSV取込では、変換できない値が混ざることがあります。Oracle 12.2以降では、TO_DATEDEFAULT ... ON CONVERSION ERROR を使い、変換エラー時の戻り値を指定できます。

default-on-conversion-error.sql
-- 変換できない場合はNULLを返す
SELECT TO_DATE(
    raw_order_date DEFAULT NULL ON CONVERSION ERROR,
    'YYYY-MM-DD'
) AS order_date
FROM import_orders;

-- 既定日を返す例。安易に使うと不正データを隠すため注意
SELECT TO_DATE(
    raw_order_date DEFAULT '1900-01-01' ON CONVERSION ERROR,
    'YYYY-MM-DD'
) AS order_date
FROM import_orders;

この方法は取込を止めたくない場面では便利ですが、不正な日付を見逃す危険もあります。実務では、変換できなかった行を別途抽出して確認できる設計にしておくと安心です。

ORA-01722との違い

ORA-01861は主に日付・時刻の書式不一致で発生します。一方、数値変換の失敗では ORA-01722: 数値が無効です がよく発生します。どちらも暗黙変換が原因になりやすい点は同じです。

数値変換側のエラーは ORA-01722の原因と解決方法 で整理しています。日付は日付として、数値は数値として、列の型に合わせて比較するのが基本です。

修正チェックリスト

チェック項目 見る場所 修正例
入力値と書式の区切りが一致しているか SQL中の文字列と第2引数 '2026-05-10' なら 'YYYY-MM-DD'
時刻があるのに書式が日付だけになっていないか ログ、CSV、画面入力値 HH24:MI:SS を追加する
DATE列にTO_DATEをかけていないか WHERE句、JOIN条件 DATE列はそのままDATE値と比較する
文字列比較で暗黙変換していないか WHERE句、INSERT、UPDATE DATE リテラルか TO_DATE を使う
NLS設定に依存していないか 環境差、本番だけ失敗 書式モデルを明示する

よくある質問

ORA-01861はどんなときに発生しますか?

日付や時刻の文字列を変換するときに、値と書式モデルが一致していないと発生します。TO_DATE の第1引数と第2引数、DATE列と文字列の比較、INSERT/UPDATEの値を確認します。

DATE ‘2026-05-10’ と TO_DATE はどちらを使うべきですか?

固定の日付だけなら DATE 'YYYY-MM-DD' が簡潔です。文字列の形式が YYYY/MM/DD や時刻付きなどの場合は、TO_DATE で書式を明示します。

NLS_DATE_FORMATを変更すれば解決しますか?

一時的には解決することがありますが、環境依存が残ります。アプリやバッチでは、SQL側で DATE リテラルや TO_DATE の書式モデルを明示するほうが安全です。

TO_TIMESTAMPでもORA-01861は発生しますか?

発生します。TIMESTAMP文字列でも、値と書式モデルが一致していなければ変換エラーになります。小数秒がある場合は FF、時刻がある場合は HH24:MI:SS まで指定します。

DATE列の検索で日付だけ一致させたい場合は?

時刻を含む可能性があるため、order_date >= DATE '2026-05-10' AND order_date < DATE '2026-05-11' のような範囲条件が扱いやすいです。

まとめ

ORA-01861は、文字列の日付と書式モデルが一致していないことを示すエラーです。まず入力値の形を確認し、YYYY-MM-DDYYYY/MM/DD、時刻の有無、月名の言語を正しく合わせます。

DATE列は文字列ではなくDATE値と比較します。固定日付なら DATE 'YYYY-MM-DD'、文字列を受け取るなら TO_DATE に明示的な書式モデルを指定しましょう。NLS設定や暗黙変換に任せないことが、ORA-01861を防ぐ一番確実な対策です。

参考

ORA-01861 – Oracle Database Error Help

TO_DATE – Oracle SQL Language Reference

Literals – Oracle SQL Language Reference