【Oracle】TO_CHAR / TO_DATE 日付フォーマット完全ガイド|書式モデル全一覧・FM修飾子・NLS_DATE_FORMAT・ORAエラー対処まで解説

Oracle で日付の表示形式を変えるには TO_CHAR 関数、文字列を日付型に変換するには TO_DATE 関数を使います。しかし「YYYY/MM/DDYYYY-MM-DD はどう違うのか」「先頭ゼロを消すにはどうすればいいのか」「ORA-01861 が出て TO_DATE が通らない」といった疑問は尽きません。

本記事では、TO_CHAR の日付書式モデルを全要素一覧で整理したうえで、FM・TH・SP 修飾子の使い方、TO_DATE での FX 修飾子と ORA エラーの対処法、タイムスタンプ・タイムゾーンを含む TO_TIMESTAMP_TZ の書式、そして NLS_DATE_FORMAT の暗黙変換がパフォーマンスに与える影響まで体系的に解説します。

この記事でわかること
・TO_CHAR の日付書式モデル(フォーマット要素)全一覧
・FM・TH・SP 修飾子で出力を細かく制御する方法
・よく使うフォーマットパターン集(ISO・日本語・タイムスタンプ・コンパクトなど)
・TO_DATE で文字列を日付型に変換する方法と FX 修飾子の使い方
・ORA-01861 / ORA-01843 / ORA-01830 エラーの原因と対処法
・TO_TIMESTAMP / TO_TIMESTAMP_TZ でナノ秒・タイムゾーンを扱う方法
・NLS_DATE_FORMAT の設定と暗黙変換による危険性・パフォーマンスへの影響
スポンサーリンク

TO_CHAR で日付を文字列に変換する(基本)

TO_CHAR(日付値, 書式モデル [, NLSパラメータ]) は、DATE 型・TIMESTAMP 型の値を指定した書式の文字列に変換する関数です。書式モデルを省略すると、セッションの NLS_DATE_FORMAT が使われます。

SQL(TO_CHAR 基本)
-- 現在日時を各フォーマットで出力
SELECT
    TO_CHAR(SYSDATE, 'YYYY-MM-DD')          AS iso_date,
    TO_CHAR(SYSDATE, 'YYYY/MM/DD')          AS jp_date,
    TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS') AS full_ts,
    TO_CHAR(SYSDATE, 'YYYYMMDD')            AS compact,
    TO_CHAR(SYSDATE, 'DD-MON-YYYY')         AS oracle_default
FROM dual;

実行結果(イメージ / 2026年3月28日 14:35:22 の場合):

列名 出力値
ISO_DATE 2026-03-28
JP_DATE 2026/03/28
FULL_TS 2026-03-28 14:35:22
COMPACT 20260328
ORACLE_DEFAULT 28-MAR-2026
TO_CHAR の第3引数(NLS パラメータ)について
和暦表示(令和・平成)は NLS_CALENDAR='Japanese Imperial'、曜日の言語指定は NLS_DATE_LANGUAGE=JAPANESE で制御できます。詳細は「日付を和暦で取得する方法」「日付から曜日を取得する方法」を参照してください。

日付書式モデル(フォーマット要素)全一覧

TO_CHAR の書式モデルに使用できるすべての要素を分類ごとに整理しました。大文字・小文字は区別されません(YYYYyyyy は同じ動作)。

年・世紀

書式要素 説明 出力例(2026年)
YYYY 4桁年(西暦) 2026
YYY 年の下3桁 026
YY 年の下2桁 26
Y 年の下1桁 6
YEAR 英語スペルアウト TWENTY TWENTY-SIX
SYYYY BC(紀元前)対応の4桁年(BC は負値) -0044(紀元前44年)
CC 世紀数(2桁) 21
SCC BC 対応の世紀数 -01(紀元前1世紀)
IYYY ISO 8601 週基準の4桁年 2026
IYY / IY / I ISO 週基準年の下3/2/1桁 026 / 26 / 6

書式要素 説明 出力例(3月)
MM 月(01〜12、2桁ゼロ埋め) 03
MON 月の英語略称(3文字) MAR
MONTH 月の英語正式名(右スペース埋め) MARCH
RM ローマ数字の月(大文字) III

日・年通算日

書式要素 説明 出力例
DD 月内の日(01〜31、2桁ゼロ埋め) 28
DDD 年通算日(001〜366) 087(3月28日の場合)
J ユリウス通日(ユリウス日) 2461127 など

週・四半期

書式要素 説明 出力例
W 月内の週番号(1〜5) 4
WW 年通算週番号(01〜53) 13
IW ISO 8601 週番号(01〜53) 13
Q 四半期(1〜4) 1(1〜3月)

曜日

曜日フォーマット(D / DY / DAY)はセッションの NLS_DATE_LANGUAGE の影響を受けます。詳しくは「日付から曜日を取得する方法」を参照してください。

書式要素 説明 出力例(土曜日 / NLS_DATE_LANGUAGE=JAPANESE)
D 曜日番号(1=日曜〜7=土曜) 7
DY 曜日略称
DAY 曜日正式名(右スペース埋め) 土曜日

時刻

書式要素 説明 出力例(14:35:22)
HH / HH12 12時間制の時(01〜12) 02
HH24 24時間制の時(00〜23) 14
MI 分(00〜59) 35
SS 秒(00〜59) 22
SSSS 午前0時からの通算秒(0〜86399) 52522
FF1〜FF9 タイムスタンプの小数秒(桁数指定) FF3 → 123(ミリ秒)

AM・PM・BC・AD

書式要素 説明 出力例
AM / PM 午前・午後(ピリオドなし) AM または PM
A.M. / P.M. 午前・午後(ピリオドあり) A.M. または P.M.
BC / AD 紀元前・紀元後(ピリオドなし) AD
B.C. / A.D. 紀元前・紀元後(ピリオドあり) A.D.
注意:FF(小数秒)は TIMESTAMP 型専用
FF1〜FF9 は TIMESTAMP / TIMESTAMP WITH TIME ZONE 型のみ使用可能です。DATE 型(秒までしか持たない)に FF を使うとエラーになります。

書式修飾子:FM・TH・SP の使い方

書式要素の先頭に修飾子を付けることで、出力形式を細かく制御できます。これらは Oracle 独自の機能で、他の RDBMS には存在しないものもあります。

FM 修飾子:先頭ゼロと末尾スペースを除去する

FM(Fill Mode)を付けると、数値の先頭ゼロと、MONTH・DAY などの末尾スペース埋めが除去されます。

SQL(FM 修飾子の比較)
-- FM なし: ゼロ埋め・スペース埋めあり
SELECT TO_CHAR(DATE '2026-03-05', 'MM/DD') FROM dual;
-- 結果: 03/05

-- FM あり: 先頭ゼロを除去
SELECT TO_CHAR(DATE '2026-03-05', 'FMMM/FMDD') FROM dual;
-- 結果: 3/5

-- MONTH の末尾スペース除去
SELECT TO_CHAR(SYSDATE, 'MONTH') FROM dual;      -- 'MARCH    '(右にスペース)
SELECT TO_CHAR(SYSDATE, 'FMMONTH') FROM dual;    -- 'MARCH'(スペースなし)

-- 実用例: 'YYYY年M月D日' のように日本語で詰めて表示
SELECT TO_CHAR(DATE '2026-03-05', 'FMYYYY"年"MM"月"DD"日"') FROM dual;
-- 結果: 2026年3月5日(ゼロ埋めなし)
FM の有効範囲
FM は直後の要素だけでなく、次の FM が現れるまでの全要素に適用されます。「FMMM/FMDD」と書くと最初の FM が MM に、次の FM が DD に適用されます。「FMMM/DD」と書くと MM と DD 両方に FM が適用されます(1つの FM が残りに影響)。意図しない除去が起きないよう、必要な要素の前にだけ付けることを推奨します。

TH 修飾子:序数(1st・2nd・3rd)を付ける

SQL(TH 修飾子)
-- DDth: 日に序数を付ける
SELECT TO_CHAR(DATE '2026-03-01', 'DDth') FROM dual;   -- 01st
SELECT TO_CHAR(DATE '2026-03-02', 'DDth') FROM dual;   -- 02nd
SELECT TO_CHAR(DATE '2026-03-03', 'DDth') FROM dual;   -- 03rd
SELECT TO_CHAR(DATE '2026-03-04', 'DDth') FROM dual;   -- 04th

-- FM + TH: ゼロ埋めなし序数
SELECT TO_CHAR(DATE '2026-03-01', 'FMDDth') FROM dual; -- 1st

SP 修飾子:数値をスペルアウト(英語)する

SQL(SP 修飾子)
-- DDSP: 日を英語スペルアウト
SELECT TO_CHAR(DATE '2026-03-28', 'DDSP') FROM dual;   -- TWENTY-EIGHT

-- DDSPTH: スペルアウト + 序数
SELECT TO_CHAR(DATE '2026-03-28', 'DDSPTH') FROM dual; -- TWENTY-EIGHTH

-- YEARSP: 年をスペルアウト
SELECT TO_CHAR(SYSDATE, 'YEARSP') FROM dual;           -- TWENTY TWENTY-SIX
SP / TH は英語出力専用
SP(スペルアウト)と TH(序数)は英語のみ対応しています。日本語環境(NLS_DATE_LANGUAGE=JAPANESE)では SP の出力は英語のままとなり、日本語の序数(「第28日」など)を生成する書式はありません。そのような場合は CASE 文 や PL/SQL で対応します。

よく使うフォーマットパターン集

実務で頻繁に使うパターンをまとめました。コピーして使えます。

用途 書式モデル 出力例
ISO 8601 日付 ‘YYYY-MM-DD’ 2026-03-28
日本語スラッシュ区切り ‘YYYY/MM/DD’ 2026/03/28
日付 + 時刻(24時間制) ‘YYYY-MM-DD HH24:MI:SS’ 2026-03-28 14:35:22
日本語 年月日 ‘YYYY”年”MM”月”DD”日”‘ 2026年03月28日
日本語 年月日(ゼロ埋めなし) ‘FMYYYY”年”MM”月”DD”日”‘ 2026年3月28日
コンパクト(ファイル名など) ‘YYYYMMDD’ 20260328
コンパクト タイムスタンプ ‘YYYYMMDD_HH24MISS’ 20260328_143522
Oracle デフォルト形式 ‘DD-MON-YYYY’ 28-MAR-2026
米国形式(MM/DD/YYYY) ‘MM/DD/YYYY’ 03/28/2026
年月のみ ‘YYYY-MM’ 2026-03
年週(ISO 週番号) ‘IYYY-IW”W”‘ 2026-13W
四半期 ‘YYYY”年”Q”四半期”‘ 2026年1四半期
12時間制 AM/PM ‘YYYY-MM-DD HH12:MI:SS AM’ 2026-03-28 02:35:22 PM
ミリ秒まで(TIMESTAMP専用) ‘YYYY-MM-DD HH24:MI:SS.FF3’ 2026-03-28 14:35:22.123
SQL(リテラル文字の埋め込み)
-- 書式モデル内に文字列リテラルを埋め込む(ダブルクォートで囲む)
SELECT TO_CHAR(SYSDATE, '"本日は"YYYY"年"FMMM"月"FMDD"日"です。"') FROM dual;
-- 結果: 本日は2026年3月28日です。

-- 英語月名を含む形式
SELECT TO_CHAR(SYSDATE, 'DD MONTH YYYY') FROM dual;  -- 28 MARCH     2026
SELECT TO_CHAR(SYSDATE, 'FMDDth FMMONTH YYYY') FROM dual; -- 28th March 2026

TO_DATE で文字列を日付型に変換する

TO_DATE(文字列, 書式モデル [, NLSパラメータ]) は、文字列を DATE 型に変換する関数です。書式モデルを省略すると NLS_DATE_FORMAT が使われますが、NLS 設定に依存したコードはバグの温床になるため、常に書式モデルを明示してください。

SQL(TO_DATE 基本)
-- 文字列を DATE 型に変換
SELECT TO_DATE('2026-03-28', 'YYYY-MM-DD')           AS d1 FROM dual;
SELECT TO_DATE('28/03/2026', 'DD/MM/YYYY')           AS d2 FROM dual;
SELECT TO_DATE('2026年3月28日', 'YYYY"年"MM"月"DD"日"') AS d3 FROM dual;

-- 時刻を含む変換
SELECT TO_DATE('2026-03-28 14:35:22', 'YYYY-MM-DD HH24:MI:SS') AS dt FROM dual;

-- WHERE 句での使用(最重要パターン)
SELECT * FROM orders
WHERE order_date >= TO_DATE('2026-01-01', 'YYYY-MM-DD')
  AND order_date <  TO_DATE('2026-04-01', 'YYYY-MM-DD');

FX 修飾子:フォーマットの厳密一致を強制する

通常の TO_DATE はやや寛容で、「2026-3-28」のようにゼロ埋めがない入力も受け付けます。FX(Format eXact)修飾子を付けると、入力文字列と書式モデルを厳密に一致させます。データバリデーションに有用です。

SQL(FX 修飾子の動作)
-- FX なし: ゼロ埋めなしも許容する
SELECT TO_DATE('2026-3-5', 'YYYY-MM-DD') FROM dual;      -- OK('2026-03-05'と解釈)

-- FX あり: 書式と文字数が完全一致しないとエラー
SELECT TO_DATE('2026-3-5', 'FXYYYY-MM-DD') FROM dual;
-- → ORA-01861: リテラルが書式文字列と一致しません

-- FX + ゼロ埋めあり: OK
SELECT TO_DATE('2026-03-05', 'FXYYYY-MM-DD') FROM dual;  -- OK

-- FX の用途: 入力バリデーション(アプリ側でゼロ埋めを保証したうえで使う)

ORA エラーの原因と対処法

エラーコード 主な原因 対処法
ORA-01861 リテラルが書式文字列と一致しない
例: ‘2026/03/28’ に ‘YYYY-MM-DD’ を指定
書式モデルを入力文字列の区切り文字に合わせる
(例: ‘YYYY/MM/DD’ に変更)
ORA-01843 無効な月名
例: ‘MON’ 書式に ‘MAR’ 以外の略称(言語不一致)
NLS_DATE_LANGUAGE を入力言語に合わせる
(例: 日本語月名なら JAPANESE を指定)
ORA-01830 書式モデルの末尾に余分な文字がある
例: ‘YYYY-MM-DD’ に ‘2026-03-28 14:35’ を渡す
書式モデルに HH24:MI を追加するか、時刻部分を除いた文字列を渡す
ORA-01847 月の日が範囲外(例: 2月30日) 入力データの日付値を確認する
ORA-01840 入力値が日付として短すぎる 入力文字列がゼロ埋めされていることを確認する。または FX を除去する
SQL(ORA-01861 の対処パターン)
-- NG: 書式と入力が不一致
SELECT TO_DATE('2026/03/28', 'YYYY-MM-DD') FROM dual;
-- ORA-01861: リテラルが書式文字列と一致しません

-- OK: 書式を入力に合わせる
SELECT TO_DATE('2026/03/28', 'YYYY/MM/DD') FROM dual;

-- NG: 時刻付きの文字列に日付書式のみ指定
SELECT TO_DATE('2026-03-28 14:35:22', 'YYYY-MM-DD') FROM dual;
-- ORA-01830: 書式文字列の変換前に入力文字列が終了しました

-- OK: 時刻書式を追加
SELECT TO_DATE('2026-03-28 14:35:22', 'YYYY-MM-DD HH24:MI:SS') FROM dual;

TO_TIMESTAMP / TO_TIMESTAMP_TZ でナノ秒・タイムゾーンを扱う

DATE 型は秒精度までですが、TIMESTAMP 型はナノ秒(小数秒)を保持でき、TIMESTAMP WITH TIME ZONE 型はタイムゾーン情報も持ちます。それぞれの変換には TO_TIMESTAMPTO_TIMESTAMP_TZ を使います。

SQL(TO_TIMESTAMP 基本)
-- ミリ秒(FF3)まで変換
SELECT TO_TIMESTAMP('2026-03-28 14:35:22.123', 'YYYY-MM-DD HH24:MI:SS.FF3')
    AS ts_val FROM dual;

-- マイクロ秒(FF6)まで変換
SELECT TO_TIMESTAMP('2026-03-28 14:35:22.123456', 'YYYY-MM-DD HH24:MI:SS.FF6')
    AS ts_val FROM dual;

-- SYSTIMESTAMP を TO_CHAR で書式化(FF3 でミリ秒表示)
SELECT TO_CHAR(SYSTIMESTAMP, 'YYYY-MM-DD HH24:MI:SS.FF3') AS formatted_ts
FROM dual;
SQL(TO_TIMESTAMP_TZ / タイムゾーン書式)
-- TZH:TZM でタイムゾーンオフセットを指定
SELECT TO_TIMESTAMP_TZ(
    '2026-03-28 14:35:22 +09:00',
    'YYYY-MM-DD HH24:MI:SS TZH:TZM'
) AS ts_tz FROM dual;

-- TZR でタイムゾーンリージョン名を指定(例: Asia/Tokyo)
SELECT TO_TIMESTAMP_TZ(
    '2026-03-28 14:35:22 Asia/Tokyo',
    'YYYY-MM-DD HH24:MI:SS TZR'
) AS ts_tz FROM dual;

-- タイムゾーン付き TIMESTAMP を TO_CHAR で出力
SELECT TO_CHAR(SYSTIMESTAMP, 'YYYY-MM-DD HH24:MI:SS.FF3 TZH:TZM') AS ts_with_tz
FROM dual;
-- 結果例: 2026-03-28 14:35:22.123 +09:00
タイムゾーン書式要素 説明 出力例
TZH タイムゾーンの時間オフセット(+/-HH) +09
TZM タイムゾーンの分オフセット(MM) 00
TZR タイムゾーンリージョン名 Asia/Tokyo
TZD タイムゾーン略称(夏時間対応) JST
DATE 型 vs TIMESTAMP 型の使い分け
・秒精度で十分な場合 → DATE 型(TO_DATE で変換)
・ミリ秒・マイクロ秒が必要な場合 → TIMESTAMP 型(TO_TIMESTAMP で変換)
・複数タイムゾーンを扱う場合 → TIMESTAMP WITH TIME ZONE 型(TO_TIMESTAMP_TZ で変換)
システム日付の取得については「SYSDATE・SYSTIMESTAMP の違いと使い方」も参照してください。

NLS_DATE_FORMAT の設定と注意点

NLS_DATE_FORMAT は DATE 型を文字列として表示するときのデフォルト書式を定義するパラメータです。TO_CHAR に書式を指定しない場合や、DATE 型を直接 SELECT した場合にこの書式が適用されます。

SQL(NLS_DATE_FORMAT の確認と設定)
-- 現在のセッション NLS 設定を確認
SELECT parameter, value FROM NLS_SESSION_PARAMETERS
WHERE parameter IN ('NLS_DATE_FORMAT', 'NLS_TIMESTAMP_FORMAT',
                    'NLS_TIMESTAMP_TZ_FORMAT', 'NLS_CALENDAR',
                    'NLS_DATE_LANGUAGE');

-- セッション単位で設定を変更
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';

-- 設定後: TO_CHAR なしで日付を SELECT しても意図通りに出力される
SELECT SYSDATE FROM dual;  -- 2026-03-28 14:35:22

-- データベース全体のデフォルト確認(インスタンス設定)
SELECT parameter, value FROM NLS_DATABASE_PARAMETERS
WHERE parameter = 'NLS_DATE_FORMAT';

暗黙変換の危険性

Oracle では、DATE 型と文字列を比較する際に暗黙的な型変換が行われます。この挙動は NLS_DATE_FORMAT の設定に依存するため、環境が変わると突然エラーが発生したり、想定外のデータが返ったりします。

SQL(暗黙変換の危険な例)
-- 【危険】暗黙変換に依存したコード
-- NLS_DATE_FORMAT が 'YYYY-MM-DD' のときは動くが、
-- 'DD-MON-YYYY'(Oracle デフォルト)だと ORA-01861 が発生する
SELECT * FROM orders WHERE order_date = '2026-03-28';  -- NG

-- 【推奨】TO_DATE で書式を明示
SELECT * FROM orders
WHERE order_date = TO_DATE('2026-03-28', 'YYYY-MM-DD');  -- OK(環境依存なし)

-- 【推奨】DATE リテラルを使う(ISO 8601形式のみ対応)
SELECT * FROM orders
WHERE order_date = DATE '2026-03-28';  -- OK(時刻は 00:00:00 になる)

暗黙変換とインデックスの関係

DATE 型の列に対して文字列で比較すると、暗黙変換によってインデックスが使われなくなる場合があります。

SQL(インデックスが使われないケース)
-- order_date が DATE 型の場合
-- NG: 暗黙変換でインデックスが使われない可能性がある
SELECT * FROM orders WHERE TO_CHAR(order_date, 'YYYY-MM-DD') = '2026-03-28';
-- → order_date 列に関数が適用されるため通常インデックスが使えない

-- OK: 右辺を変換してインデックスを活かす
SELECT * FROM orders
WHERE order_date >= DATE '2026-03-28'
  AND order_date <  DATE '2026-03-29';

-- OK: 関数ベースインデックスがある場合は TO_CHAR 側でも使える
-- CREATE INDEX idx_order_date_fmt ON orders (TO_CHAR(order_date, 'YYYY-MM-DD'));
SELECT * FROM orders WHERE TO_CHAR(order_date, 'YYYY-MM-DD') = '2026-03-28';
NLS_DATE_FORMAT に依存したコードを書かない
「このサーバーは NLS_DATE_FORMAT = 'YYYY/MM/DD' だから文字列比較で大丈夫」という前提は危険です。DB 移行・サーバー再構築・クライアントツールの違いで NLS 設定が変わり、意図しない ORA エラーや誤ったデータ取得が起こります。日付操作は必ず TO_DATEDATE リテラルTO_CHAR で書式を明示するのが鉄則です。

実践パターン集

パターン①:INSERT 文での日付挿入

SQL(INSERT での日付値挿入)
-- TO_DATE で明示的に変換して INSERT(推奨)
INSERT INTO orders (order_id, order_date, amount)
VALUES (1001, TO_DATE('2026-03-28 14:35:22', 'YYYY-MM-DD HH24:MI:SS'), 5000);

-- DATE リテラルを使う方法(時刻は 00:00:00 固定)
INSERT INTO orders (order_id, order_date, amount)
VALUES (1002, DATE '2026-03-28', 5000);

-- TIMESTAMP リテラル(秒精度まで)
INSERT INTO logs (log_id, log_time, message)
VALUES (1, TIMESTAMP '2026-03-28 14:35:22.123456', 'start');

パターン②:月単位・週単位での集計表示

SQL(月・週・四半期ごとの集計)
-- 月ごとの売上集計(月を YYYY-MM 形式で表示)
SELECT TO_CHAR(order_date, 'YYYY-MM')    AS month,
       COUNT(*)                           AS order_count,
       SUM(amount)                        AS total_amount
FROM orders
GROUP BY TO_CHAR(order_date, 'YYYY-MM')
ORDER BY TO_CHAR(order_date, 'YYYY-MM');

-- 四半期ごとの集計
SELECT TO_CHAR(order_date, 'YYYY')       AS year,
       TO_CHAR(order_date, 'Q')           AS quarter,
       SUM(amount)                        AS total_amount
FROM orders
GROUP BY TO_CHAR(order_date, 'YYYY'), TO_CHAR(order_date, 'Q')
ORDER BY 1, 2;

-- ISO 週番号での集計
SELECT TO_CHAR(order_date, 'IYYY-IW')    AS iso_week,
       COUNT(*)                           AS order_count
FROM orders
GROUP BY TO_CHAR(order_date, 'IYYY-IW')
ORDER BY 1;

パターン③:帳票出力・ログ用フォーマット

SQL(帳票・ログ向けフォーマット)
-- 帳票用: 日付を日本語で表示(年月日付き)
SELECT TO_CHAR(SYSDATE, 'FMYYYY"年"MM"月"DD"日"') AS print_date FROM dual;
-- → 2026年3月28日

-- ログ用: ミリ秒付きタイムスタンプ
SELECT TO_CHAR(SYSTIMESTAMP, 'YYYY-MM-DD HH24:MI:SS.FF3') AS log_ts FROM dual;
-- → 2026-03-28 14:35:22.123

-- ファイル名用: スペース・コロンなし形式
SELECT TO_CHAR(SYSDATE, 'YYYYMMDD_HH24MISS') AS file_ts FROM dual;
-- → 20260328_143522

-- 会計年度表示(4月始まり: 4月〜翌3月を同一年度として表示)
SELECT TO_CHAR(
           ADD_MONTHS(order_date, -3),
           'YYYY'
       ) AS fiscal_year,
       SUM(amount) AS total
FROM orders
GROUP BY TO_CHAR(ADD_MONTHS(order_date, -3), 'YYYY')
ORDER BY 1;

パフォーマンスの注意点

注意点 詳細 対処法
GROUP BY に TO_CHAR を使う TO_CHAR は文字列比較になるため、YYYY-13 のようなソートが辞書順になる 集計クエリでは TO_CHAR の ORDER BY も同じ書式モデルで統一するか、TRUNC(date, ‘MM’) で DATE のまま GROUP BY する
WHERE 句で DATE 型列に TO_CHAR を適用 関数インデックスがなければフルスキャンになる 右辺を TO_DATE / DATE リテラルに変換して列はそのまま使う
暗黙変換でインデックスが無効化 文字列 → DATE の暗黙変換が WHERE 句で発生するとオプティマイザがインデックスを使わない場合がある TO_DATE を明示して暗黙変換を排除する
TRUNC(date, ‘MM’) で月初集計 TRUNC は DATE 型のまま返すのでインデックスが使いやすい 月単位集計は TO_CHAR より TRUNC の方がパフォーマンス上有利なことが多い。詳細は「TRUNC関数完全ガイド」を参照

よくある質問

QTO_CHAR で先頭ゼロを消すにはどうすればいいですか?
AFM(Fill Mode)修飾子を使います。TO_CHAR(date, 'FMMM/FMDD') とすると、「03/05」が「3/5」になります。注意点として、FM は次の FM が現れるまで全要素に影響するため、意図しない要素まで変わらないよう位置に気をつけてください。
QMONTH を TO_CHAR で出力すると末尾にスペースが付きます
AMONTHDAY は固定長で右スペースが埋められます。FM 修飾子を付けて 'FMMONTH' とするとスペースが除去されます。文字列連結や画面表示で余分なスペースが出る場合は FM を活用してください。
QTO_DATE に書式モデルを指定しないとどうなりますか?
Aセッションの NLS_DATE_FORMAT が使われます。Oracle インストール時のデフォルトは DD-MON-YYYY(例: 28-MAR-2026)ですが、サーバー設定によって異なります。異なる環境では ORA-01861 が発生するため、書式モデルは常に明示してください
QORA-01861 が出ます。原因は何ですか?
A入力文字列と書式モデルの区切り文字や桁数が一致していません。たとえば '2026/03/28''YYYY-MM-DD'(ハイフン区切り)で変換しようとすると ORA-01861 になります。書式モデルの区切り文字を入力文字列に合わせてください(ここでは 'YYYY/MM/DD' に変更)。FX 修飾子がある場合はゼロ埋めの有無まで厳密に一致させる必要があります。
QDATE 型の列を TO_CHAR でフォーマットすると日付が 1 日ずれます
A時刻成分を含む DATE 型の値を TRUNC せずに比較・グループ化すると、時刻の違いで同じ日付なのに別の行として扱われることがあります。また、タイムゾーンの違いで見かけ上 1 日ずれるケースもあります。TRUNC(date_col) で時刻を 00:00:00 に揃えるか、タイムゾーン設定を確認してください。日付の切り捨て方法は「TRUNC関数完全ガイド」を参照してください。
QDATE リテラル(DATE ‘2026-03-28’)と TO_DATE の違いは何ですか?
ADATE 'YYYY-MM-DD' リテラルは ISO 8601 形式のみ受け付け、時刻は 00:00:00 になります。NLS 設定に依存せず常に動作するため、日付部分のみの指定には DATE リテラルが最もシンプルです。時刻も含めて指定したい場合や、ISO 形式以外の文字列を扱う場合はTO_DATE('...', 'YYYY-MM-DD HH24:MI:SS') を使います。

まとめ

Oracle の日付フォーマット操作の要点をまとめます。

やりたいこと 使う関数・構文
DATE 型を任意の形式の文字列で表示 TO_CHAR(date_col, ‘YYYY-MM-DD HH24:MI:SS’)
先頭ゼロを除去して表示 TO_CHAR(date_col, ‘FMYYYY”年”MM”月”DD”日”‘)
英語スペルアウト(1st・2nd・3rd) TO_CHAR(date_col, ‘FMDDth FMMONTH YYYY’)
文字列を DATE 型に変換 TO_DATE(‘2026-03-28’, ‘YYYY-MM-DD’)
厳密フォーマット一致を強制 TO_DATE(‘2026-03-28’, ‘FXYYYY-MM-DD’)
ミリ秒・マイクロ秒を含む変換 TO_TIMESTAMP(‘2026-03-28 14:35:22.123’, ‘YYYY-MM-DD HH24:MI:SS.FF3’)
タイムゾーン付き変換 TO_TIMESTAMP_TZ(‘2026-03-28 14:35:22 +09:00’, ‘YYYY-MM-DD HH24:MI:SS TZH:TZM’)
和暦(令和・平成)で表示 TO_CHAR(date_col, ‘EEYY”年”MM”月”DD”日”‘, ‘NLS_CALENDAR=Japanese Imperial’)
セッションのデフォルト書式を変更 ALTER SESSION SET NLS_DATE_FORMAT = ‘YYYY-MM-DD HH24:MI:SS’
暗黙変換を避けるリテラル指定 WHERE order_date = DATE ‘2026-03-28’

和暦変換については「日付を和暦で取得する方法」、曜日の取得については「日付から曜日を取得する方法」、TO_CHAR の数値書式モデルについては「小数点以下の0が消える原因と表示する方法」、日付の切り捨てについては「TRUNC関数で日付を切り捨てる方法」も併せて参照してください。