【Oracle】TRUNC関数で日付を切り捨てる方法|月初・年初・時間切り捨て・SYSDATE活用パターン完全解説

【Oracle】TRUNC関数で日付を切り捨てる方法|月初・年初・時間切り捨て・SYSDATE活用パターン完全解説 Oracle

Oracle の TRUNC 関数は日付の時刻部分や月・年の単位で切り捨てができる強力な関数です。TRUNC(SYSDATE) で今日の 00:00:00、TRUNC(date, 'MM') で月初 1 日など、日付集計・期間絞り込みで必須のテクニックをすべて解説します。

この記事で解決できること

  • TRUNC で日付の時刻部分(時・分・秒)を切り捨てる方法
  • TRUNC(SYSDATE) で今日の 00:00:00 を取得する方法
  • 月初・年初・四半期初・週初の日付を求める方法
  • 時間単位・分単位への切り捨て
  • 月別・週別・時間帯別の集計クエリパターン
  • TIMESTAMP 型への適用と注意点
スポンサーリンク

TRUNC 関数の構文(日付版)

TRUNC(date [, fmt])
引数 説明
date DATE / TIMESTAMP 切り捨て対象の日付・タイムスタンプ
fmt VARCHAR2(省略可) 切り捨て単位を指定する書式文字列。省略時は 'DD'(日単位=時刻を 00:00:00 に)

戻り値は常に DATE 型です。TIMESTAMP を渡した場合も DATE に変換されます。

数値版 TRUNC との違い
TRUNC(1.567, 2) のように数値にも使えますが、日付版は第 2 引数が 書式文字列'MM' など)になります。構文は同じですが意味が異なるため、混在しないよう注意してください。

基本的な使い方

時刻を切り捨てる(今日の 00:00:00)

TRUNC(SYSDATE) は最もよく使われるパターンです。時・分・秒を 00:00:00 に切り捨て、純粋な「日付」を返します。

-- SYSDATE が 2024-07-17 14:35:22 の場合
SELECT SYSDATE                   FROM DUAL;  -- 2024-07-17 14:35:22
SELECT TRUNC(SYSDATE)            FROM DUAL;  -- 2024-07-17 00:00:00
SELECT TRUNC(SYSDATE, 'DD')      FROM DUAL;  -- 2024-07-17 00:00:00  ← DD は省略時と同じ

-- 任意の日付変数にも同様に使える
SELECT TRUNC(order_date)         FROM orders;  -- 時刻なしの受注日付
SELECT TRUNC(created_at)         FROM users;   -- 時刻なしの登録日付
DATE 型には常に時刻が含まれる
Oracle の DATE 型は年月日だけでなく時・分・秒も持っています。'2024-07-17' と見えても内部は 2024-07-17 00:00:00 です。order_date = SYSDATE のような比較では時刻の差で一致しないケースがあります。日付だけで比較・集計するときは必ず TRUNC を使いましょう。

月初を求める(TRUNC + MM)

-- SYSDATE が 2024-07-17 の場合
SELECT TRUNC(SYSDATE, 'MM')      FROM DUAL;  -- 2024-07-01 00:00:00
SELECT TRUNC(SYSDATE, 'MONTH')   FROM DUAL;  -- 2024-07-01 00:00:00  ← 同義

-- 先月初・翌月初も計算できる
SELECT TRUNC(ADD_MONTHS(SYSDATE, -1), 'MM')  FROM DUAL;  -- 2024-06-01(先月初)
SELECT ADD_MONTHS(TRUNC(SYSDATE, 'MM'), 1)   FROM DUAL;  -- 2024-08-01(翌月初)

年初を求める(TRUNC + YYYY)

-- SYSDATE が 2024-07-17 の場合
SELECT TRUNC(SYSDATE, 'YYYY')    FROM DUAL;  -- 2024-01-01 00:00:00
SELECT TRUNC(SYSDATE, 'YEAR')    FROM DUAL;  -- 2024-01-01 00:00:00  ← 同義
SELECT TRUNC(SYSDATE, 'YY')      FROM DUAL;  -- 2024-01-01 00:00:00  ← 同義

-- 去年初・来年初
SELECT ADD_MONTHS(TRUNC(SYSDATE, 'YYYY'), -12)  FROM DUAL;  -- 2023-01-01(去年初)
SELECT ADD_MONTHS(TRUNC(SYSDATE, 'YYYY'), 12)   FROM DUAL;  -- 2025-01-01(来年初)

四半期初を求める(TRUNC + Q)

-- SYSDATE が 2024-07-17 の場合(Q3: 7月〜9月)
SELECT TRUNC(SYSDATE, 'Q')       FROM DUAL;  -- 2024-07-01 00:00:00(Q3 の開始)

-- 月ごとの四半期開始日の例
-- 1月→1/1, 2月→1/1, 3月→1/1(Q1)
-- 4月→4/1, 5月→4/1, 6月→4/1(Q2)
-- 7月→7/1, 8月→7/1, 9月→7/1(Q3)
-- 10月→10/1, 11月→10/1, 12月→10/1(Q4)

週の開始日を求める(TRUNC + DAY / WW)

-- SYSDATE が 2024-07-17(水曜日)の場合
SELECT TRUNC(SYSDATE, 'DAY')     FROM DUAL;  -- 2024-07-14 00:00:00(直近の日曜日)
SELECT TRUNC(SYSDATE, 'DY')      FROM DUAL;  -- 2024-07-14 00:00:00  ← 同義

-- WW:年の最初の日と同じ曜日に揃える
SELECT TRUNC(SYSDATE, 'WW')      FROM DUAL;  -- 年の第 1 日(1/1)と同じ曜日の週頭

-- ※ DAY の週開始曜日は NLS_TERRITORY に依存(日本・米国はデフォルト日曜日)

時間単位・分単位に切り捨てる

-- SYSDATE が 2024-07-17 14:35:22 の場合
SELECT TRUNC(SYSDATE, 'HH')      FROM DUAL;  -- 2024-07-17 14:00:00(時間単位)
SELECT TRUNC(SYSDATE, 'HH24')    FROM DUAL;  -- 2024-07-17 14:00:00  ← 同義
SELECT TRUNC(SYSDATE, 'HH12')    FROM DUAL;  -- 2024-07-17 14:00:00  ← 同義
SELECT TRUNC(SYSDATE, 'MI')      FROM DUAL;  -- 2024-07-17 14:35:00(分単位、秒を切り捨て)

書式文字列一覧

書式文字列 切り捨て単位 例(入力: 2024-07-17 14:35:22)
YYYY / YEAR / YY 年(1月1日に) 2024-01-01 00:00:00
Q 四半期(四半期の第1日に) 2024-07-01 00:00:00
MM / MONTH / MON 月(1日に) 2024-07-01 00:00:00
WW 週(年の第1日と同じ曜日) NLS依存
W 週(月の第1日と同じ曜日) NLS依存
DAY / DY / D 週(週開始曜日に) 2024-07-14 00:00:00(日曜日)
DD / J(省略時も同じ) 日(00:00:00 に) 2024-07-17 00:00:00
HH / HH12 / HH24 時間(分・秒を 00 に) 2024-07-17 14:00:00
MI 分(秒を 00 に) 2024-07-17 14:35:00

TRUNC と ROUND の違い(日付)

日付の丸めには TRUNC(切り捨て)と ROUND(四捨五入)があります。月初を求める場合の違いを確認してください。

-- 2024-07-17 の場合
SELECT TRUNC(DATE '2024-07-17', 'MM') FROM DUAL;  -- 2024-07-01(常に当月 1 日)
SELECT ROUND(DATE '2024-07-17', 'MM') FROM DUAL;  -- 2024-07-01(16 日未満 → 当月)

-- 2024-07-16 の場合(ROUND の境界)
SELECT TRUNC(DATE '2024-07-16', 'MM') FROM DUAL;  -- 2024-07-01(TRUNC は常に当月)
SELECT ROUND(DATE '2024-07-16', 'MM') FROM DUAL;  -- 2024-08-01(16 日以降 → 翌月)

-- 集計クエリでは TRUNC が安全
-- ROUND では月初を超えて翌月に飛ぶケースがあるため、期間絞り込みには TRUNC を使う
月初・年初の取得には TRUNC を使う
ROUND(date, 'MM') は月の中間日(16 日)を境界に翌月に丸まることがあります。「当月 1 日」を確実に取得したい場合は必ず TRUNC(date, 'MM') を使ってください。

実務パターン集

今日・今月・今年のデータを集計する

-- 今日のデータ(時刻を無視して日付のみで絞り込み)
SELECT COUNT(*), SUM(amount)
FROM orders
WHERE TRUNC(order_date) = TRUNC(SYSDATE);

-- 今月のデータ(月初〜翌月初の範囲)
SELECT COUNT(*), SUM(amount)
FROM orders
WHERE order_date >= TRUNC(SYSDATE, 'MM')
  AND order_date <  ADD_MONTHS(TRUNC(SYSDATE, 'MM'), 1);

-- 今年のデータ
SELECT COUNT(*), SUM(amount)
FROM orders
WHERE order_date >= TRUNC(SYSDATE, 'YYYY')
  AND order_date <  ADD_MONTHS(TRUNC(SYSDATE, 'YYYY'), 12);
BETWEEN より >= AND < を推奨
BETWEEN TRUNC(SYSDATE, 'MM') AND ADD_MONTHS(...) - 1 という書き方では月末の 23:59:59 のデータが漏れる場合があります。>= 月初 AND < 翌月初 の形式が最も安全です。

月別集計レポート(GROUP BY)

-- 月別の売上集計
SELECT
  TRUNC(order_date, 'MM')         AS month_start,
  TO_CHAR(order_date, 'YYYY-MM')  AS month_label,
  COUNT(*)                         AS order_count,
  SUM(amount)                      AS total_amount
FROM orders
WHERE order_date >= ADD_MONTHS(TRUNC(SYSDATE, 'YYYY'), -12)
GROUP BY TRUNC(order_date, 'MM'), TO_CHAR(order_date, 'YYYY-MM')
ORDER BY month_start;

-- 年別の集計
SELECT
  TO_CHAR(TRUNC(order_date, 'YYYY'), 'YYYY')  AS year_label,
  COUNT(*)                                      AS order_count,
  SUM(amount)                                   AS total_amount
FROM orders
GROUP BY TRUNC(order_date, 'YYYY')
ORDER BY TRUNC(order_date, 'YYYY');

時間帯別の集計

-- 1 時間単位で集計
SELECT
  TRUNC(order_time, 'HH')                   AS hour_block,
  TO_CHAR(TRUNC(order_time, 'HH'), 'HH24')  AS hour_label,
  COUNT(*)                                    AS order_count
FROM orders
WHERE TRUNC(order_time) = TRUNC(SYSDATE)
GROUP BY TRUNC(order_time, 'HH')
ORDER BY hour_block;

-- 30 分単位での集計(TRUNC で 30 分ブロックに丸める)
SELECT
  TRUNC(order_time, 'HH') + (FLOOR(TO_NUMBER(TO_CHAR(order_time, 'MI')) / 30) / 48)  AS block_30min,
  COUNT(*)
FROM orders
WHERE TRUNC(order_time) = TRUNC(SYSDATE)
GROUP BY TRUNC(order_time, 'HH') + (FLOOR(TO_NUMBER(TO_CHAR(order_time, 'MI')) / 30) / 48)
ORDER BY block_30min;

N 日前・N ヶ月前との比較

-- 過去 7 日間のデータ(今日を含む)
SELECT *
FROM access_log
WHERE access_time >= TRUNC(SYSDATE) - 6
  AND access_time <  TRUNC(SYSDATE) + 1;

-- 先月のデータ
SELECT *
FROM sales
WHERE sale_date >= ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -1)
  AND sale_date <  TRUNC(SYSDATE, 'MM');

-- 先月同日(月初からの経過日数を揃える)
SELECT
  TRUNC(SYSDATE) - TRUNC(SYSDATE, 'MM')                          AS days_since_month_start,
  ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -1)                           AS last_month_start,
  ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -1)
    + (TRUNC(SYSDATE) - TRUNC(SYSDATE, 'MM'))                    AS same_day_last_month
FROM DUAL;

TIMESTAMP 型への適用

TRUNC は TIMESTAMP 型にも適用できますが、戻り値は DATE 型に変換されます。ナノ秒精度が失われる点に注意してください。

-- TIMESTAMP に TRUNC を適用
SELECT TRUNC(SYSTIMESTAMP)           FROM DUAL;  -- DATE 型で今日の 00:00:00
SELECT TRUNC(SYSTIMESTAMP, 'HH')     FROM DUAL;  -- DATE 型で今時の 00 分 00 秒

-- TIMESTAMP のまま時刻を切り捨てたい場合は CAST を使う
SELECT CAST(TRUNC(SYSTIMESTAMP) AS TIMESTAMP)  FROM DUAL;  -- TIMESTAMP に再変換

-- TIMESTAMP_TZ (タイムゾーン付き) への注意
-- TRUNC(SYSTIMESTAMP AT TIME ZONE 'Asia/Tokyo', 'DD') はエラーになる場合がある
-- → CAST で DATE 経由にするかセッションの TIMEZONE を設定してから使う
TIMESTAMP を日付単位で比較するパターン
created_at が TIMESTAMP 型の場合、日付単位の絞り込みは次のように書けます。
WHERE TRUNC(created_at) = TRUNC(SYSDATE)
TRUNC の戻り値が DATE 型になるため、双方 DATE に揃えられ正しく比較できます。

よくある質問(FAQ)

Q TRUNC(SYSDATE) と TRUNC(SYSDATE, 'DD') は同じ?
A

はい、全く同じです。TRUNC(date)fmt を省略した場合のデフォルトは 'DD'(日単位)です。どちらも時・分・秒を 00:00:00 に切り捨て、日付部分だけを返します。

可読性のために TRUNC(SYSDATE)(時刻カット)と覚えておくのがシンプルです。

Q WHERE 句で TRUNC を使うとインデックスが使われない?
A

WHERE TRUNC(order_date) = TRUNC(SYSDATE) のように 列側に TRUNC を使うと通常のインデックスは使われません(列値ではなく関数結果での評価になるため)。

パフォーマンスが重要な場合は次のいずれかを検討してください。

-- 方法 1: 範囲条件に書き換える(インデックスが使われやすい)
WHERE order_date >= TRUNC(SYSDATE)
  AND order_date <  TRUNC(SYSDATE) + 1

-- 方法 2: ファンクションベースインデックスを作成する
CREATE INDEX idx_order_date_trunc ON orders (TRUNC(order_date));
-- この後 WHERE TRUNC(order_date) = TRUNC(SYSDATE) でインデックスが使われる
Q 月末の日付を求めるには?
A

TRUNC 単体では月末を直接求められません。LAST_DAY 関数か、「翌月 1 日の 1 秒前」で計算します。

-- 月末の日付(時刻なし)
SELECT LAST_DAY(TRUNC(SYSDATE, 'MM'))               FROM DUAL;  -- 2024-07-31 00:00:00

-- 月末の 23:59:59(その月の最後の秒)
SELECT ADD_MONTHS(TRUNC(SYSDATE, 'MM'), 1) - (1/86400)  FROM DUAL;  -- 2024-07-31 23:59:59
Q TRUNC で週の月曜日を求めたい(日曜日ではなく)
A

Oracle の TRUNC(date, 'DAY') はデフォルトで日曜日を週の開始日とします。月曜日を週頭にしたい場合は手動で計算します。

-- 月曜日始まりの週頭を求める(日曜日以外の曜日では機能する)
SELECT TRUNC(SYSDATE, 'DAY') + 1  FROM DUAL;  -- 直前の日曜日 + 1 日 = 月曜日

-- 注意: SYSDATE が日曜日の場合は TRUNC = 当日(日曜日)なので +1 は翌週月曜日になる

-- より厳密な計算(何曜日でも当週月曜日を返す)
SELECT TRUNC(SYSDATE) - MOD(TRUNC(SYSDATE) - TO_DATE('1970-01-05', 'YYYY-MM-DD'), 7)  FROM DUAL;
-- 1970-01-05 は月曜日 → この基準日からの日数の余りで補正する
Q TRUNC で時刻を切り捨てた日付同士の差分(日数)を計算したい
A

Oracle の DATE 型同士の減算は差分を日数(数値)で返します。TRUNC で時刻を統一してから引くと、ちょうどの日数差が得られます。

-- 今日から指定日までの日数
SELECT TRUNC(SYSDATE) - TRUNC(TO_DATE('2024-01-01', 'YYYY-MM-DD'))  FROM DUAL;  -- 198(日)

-- TRUNC しないと時刻の差で小数が出ることがある
SELECT SYSDATE - TO_DATE('2024-01-01', 'YYYY-MM-DD')  FROM DUAL;  -- 198.6... のような値

-- 整数で日数差を確実に求めるには TRUNC を使う
SELECT TRUNC(SYSDATE) - TRUNC(start_date)  AS days_elapsed  FROM tasks;

まとめ

やりたいこと 構文 結果例(2024-07-17 14:35:22)
時刻を切り捨て(今日 00:00:00) TRUNC(SYSDATE) 2024-07-17 00:00:00
月初(当月 1 日) TRUNC(date, 'MM') 2024-07-01 00:00:00
年初(当年 1/1) TRUNC(date, 'YYYY') 2024-01-01 00:00:00
四半期初 TRUNC(date, 'Q') 2024-07-01 00:00:00
週の開始日(日曜日) TRUNC(date, 'DAY') 2024-07-14 00:00:00
時間単位(分・秒をカット) TRUNC(date, 'HH') 2024-07-17 14:00:00
分単位(秒をカット) TRUNC(date, 'MI') 2024-07-17 14:35:00
今月のデータを絞り込む >= TRUNC(SYSDATE,'MM') AND < ADD_MONTHS(...,1)

Oracle の TRUNC 関数は日付の切り捨てに欠かせないツールです。TRUNC(SYSDATE) で時刻カットTRUNC(date, 'MM') で月初取得の 2 つを押さえるだけで、日常的な集計クエリの大半に対応できます。期間絞り込みは BETWEEN より >= AND < 形式を使い、インデックス効率を意識した書き方を心がけてください。