DATEDIFF は 2 つの日付の間の日数、月数などの単位数を返します。データベースごとに方法が異なるため、PostgreSQL、MySQL、SQLite、SQL Server の例を紹介します。
DATEDIFF の使い方
DATEDIFF は開始日から終了日までの日数、月数、年数などを数えます。以下は SQL Server 版で、一般に DATEDIFF と言えばこれを指します:
DATEDIFF(unit, start_date, end_date)
SELECT DATEDIFF(day, '2026-01-01', '2026-03-15');
-- Output: 73
SELECT DATEDIFF(month, '2026-01-01', '2026-03-15');
-- Output: 2
SELECT DATEDIFF(day, '2026-03-15', '2026-01-01');
-- Output: -73
単位には year、quarter、month、week、day、hour、minute、second またはそれより小さい単位を指定できます。終了日が開始日より前の場合、結果は負の値になります。
列を指定すると、行ごとに差分が得られます:
-- Input: orders
-- order_id | order_date | ship_date
-- ---------+------------+-----------
-- 1001 | 2026-02-02 | 2026-02-04
-- 1002 | 2026-02-03 | 2026-02-10
-- 1003 | 2026-02-27 | 2026-03-02
-- 1004 | 2026-03-05 | NULL
SELECT
order_id,
order_date,
ship_date,
DATEDIFF(day, order_date, ship_date) AS fulfillment_days
FROM orders;
-- Output:
-- order_id | order_date | ship_date | fulfillment_days
-- ---------+------------+------------+-----------------
-- 1001 | 2026-02-02 | 2026-02-04 | 2
-- 1002 | 2026-02-03 | 2026-02-10 | 7
-- 1003 | 2026-02-27 | 2026-03-02 | 3
-- 1004 | 2026-03-05 | NULL | NULL
知っておくべきルール
-
数えるのは境界であり、完全な単位ではありません。
DATEDIFF(month, ...)は 2 つの日付の間にある月の境界を数えるため、たった 1 日でも 1 か月、あるいは 1 年として数えられることがあります:
SELECT DATEDIFF(month, '2026-01-31', '2026-02-01');
-- Output: 1
SELECT DATEDIFF(year, '2025-12-31', '2026-01-01');
-- Output: 1
-
NULL を渡すと NULL が返ります。 どちらかの日付が NULL の場合、上の注文 1004 のように結果も NULL になります。デフォルト値が必要な場合は
COALESCE()で囲みます。
別の構文
MySQL の DATEDIFF は引数が 2 つで、日数のみを数え、終了日を先に指定します。その他の単位には TIMESTAMPDIFF を使います。こちらは開始日を先に指定し、完全な単位で数えます:
SELECT DATEDIFF('2026-03-15', '2026-01-01');
-- Output: 73
SELECT TIMESTAMPDIFF(MONTH, '2026-01-01', '2026-03-15');
-- Output: 2
PostgreSQL には DATEDIFF がありません。日付から日付を引くと日数が得られます。より大きな単位には、AGE()(後の日付を先に指定)が年・月・日の interval を返し、EXTRACT でそこから完全な単位を取り出せます:
SELECT DATE '2026-03-15' - DATE '2026-01-01';
-- Output: 73
SELECT AGE('2026-03-15', '2026-01-01');
-- Output: 2 mons 14 days
SELECT EXTRACT(YEAR FROM AGE('2026-03-15', '2026-01-01')) * 12
+ EXTRACT(MONTH FROM AGE('2026-03-15', '2026-01-01'));
-- Output: 2
SQLite にも DATEDIFF はありません。julianday() は日付を日数の数値に変換するので、2 つを引き算すれば差分が得られます。CAST(... AS INTEGER) で小数部分を切り捨てます。月や年の単位はないため、近似値を得るには 30 または 365 で割ります:
SELECT julianday('2026-03-15') - julianday('2026-01-01');
-- Output: 73.0
SELECT CAST((julianday('2026-03-15') - julianday('2026-01-01')) / 30 AS INTEGER);
-- Output: 2
-- The time of day counts: 8 p.m. to 8 a.m. is half a day.
-- Wrap each value in date() to count calendar days instead.
SELECT julianday('2026-03-16 08:00') - julianday('2026-03-15 20:00');
-- Output: 0.5
-- A date it can't read gives NULL, so store dates as YYYY-MM-DD
SELECT julianday('03/15/2026');
-- Output: NULL
DATEDIFF はどんなときに使う?
日付の差に関する問いの多くは、次の 4 つのいずれかに当てはまります。
かかった時間を測る
終了日から開始日を引いて期間を求めます。注文の発送にかかった日数、プロジェクトの実施週数、サポート通話の分数などです。
超過日数や残り日数を数える
期限を今日や固定のレポート日と比較して、どれだけ遅れているか、あとどれだけ残っているかを確認します。結果の符号で絞り込めば、どちらか一方だけを残せます。
経過期間を月や年で表す
アカウントの経過期間、顧客の継続期間、勤続年数などには日より大きな単位が必要です。データベースに月や年の単位があればそれを使い、SQLite では日数を割ります。
ある日付の前後の範囲にある行を探す
差分の絶対値をとると、ある日付の前後一定日数以内にある行を見つけられます。ローンチ前後のイベントや、障害発生前後のログインなどです。
DATEDIFF の実例
サポートチケットを SLA と照合する
各チケットは 3 日以内に解決する必要があります。それぞれにかかった日数を数え、違反にフラグを立てます(SQLite):
-- Input: support_tickets
-- ticket_id | created_at | resolved_at
-- ----------+------------+------------
-- 301 | 2026-03-02 | 2026-03-03
-- 302 | 2026-03-04 | 2026-03-07
-- 303 | 2026-03-05 | 2026-03-10
SELECT
ticket_id,
created_at,
resolved_at,
CAST(julianday(resolved_at) - julianday(created_at) AS INTEGER) AS resolution_days,
CASE
WHEN julianday(resolved_at) - julianday(created_at) <= 3 THEN 'Within SLA'
ELSE 'SLA Breach'
END AS sla_status
FROM support_tickets;
-- Output:
-- ticket_id | created_at | resolved_at | resolution_days | sla_status
-- ----------+------------+-------------+-----------------+-----------
-- 301 | 2026-03-02 | 2026-03-03 | 1 | Within SLA
-- 302 | 2026-03-04 | 2026-03-07 | 3 | Within SLA
-- 303 | 2026-03-05 | 2026-03-10 | 5 | SLA Breach
期限切れの請求書を一覧表示する
支払期日を過ぎた請求書を、超過日数の多い順に表示します(MySQL):
-- Input: invoices
-- invoice_id | customer | due_date
-- -----------+---------------+-----------
-- 5001 | Acme Hardware | 2026-08-15
-- 5002 | Lumen Design | 2026-09-30
-- 5003 | Acme Hardware | 2026-10-01
-- 5004 | Cedar Clinic | 2026-10-31
SELECT
invoice_id,
customer,
due_date,
DATEDIFF(CURDATE(), due_date) AS days_overdue
FROM invoices
WHERE due_date < CURDATE()
ORDER BY days_overdue DESC;
-- Output (run on 2026-10-13):
-- invoice_id | customer | due_date | days_overdue
-- -----------+---------------+------------+-------------
-- 5001 | Acme Hardware | 2026-08-15 | 59
-- 5002 | Lumen Design | 2026-09-30 | 13
-- 5003 | Acme Hardware | 2026-10-01 | 12
顧客を利用期間でグループ化する
顧客を満年数での継続期間によってランク分けします(PostgreSQL):
-- Input: customers
-- customer_name | signup_date
-- ------------------+------------
-- Northwind Traders | 2022-11-05
-- Bluebird Bakery | 2025-06-20
-- Fjord Outfitters | 2025-12-20
-- Kestrel Labs | 2026-03-02
SELECT
customer_name,
signup_date,
EXTRACT(YEAR FROM AGE(CURRENT_DATE, signup_date)) AS years_with_us,
CASE
WHEN AGE(CURRENT_DATE, signup_date) < INTERVAL '1 year' THEN 'New'
WHEN AGE(CURRENT_DATE, signup_date) < INTERVAL '2 years' THEN 'Established'
ELSE 'Loyal'
END AS customer_tier
FROM customers;
-- Output (run on 2026-10-13):
-- customer_name | signup_date | years_with_us | customer_tier
-- ------------------+-------------+---------------+--------------
-- Northwind Traders | 2022-11-05 | 3 | Loyal
-- Bluebird Bakery | 2025-06-20 | 1 | Established
-- Fjord Outfitters | 2025-12-20 | 0 | New
-- Kestrel Labs | 2026-03-02 | 0 | New
更新時期が近いサブスクリプションを探す
今後 30 日以内に終了するサブスクリプションを、残り日数とともに一覧表示します(SQL Server)。WHERE 句では subscription_end を DATEDIFF で囲まずに日付範囲と比較しているため、列のインデックスを引き続き利用できます。
-- Input: subscriptions
-- customer_name | subscription_end
-- -----------------+-----------------
-- Harbor Dental | 2026-10-09
-- Maple Yoga | 2026-10-20
-- Orbit Analytics | 2026-11-07
-- Pinecrest Realty | 2027-01-15
SELECT
customer_name,
subscription_end,
DATEDIFF(day, GETDATE(), subscription_end) AS days_remaining
FROM subscriptions
WHERE subscription_end BETWEEN CAST(GETDATE() AS date)
AND DATEADD(day, 30, CAST(GETDATE() AS date))
ORDER BY days_remaining;
-- Output (run on 2026-10-13):
-- customer_name | subscription_end | days_remaining
-- ----------------+------------------+---------------
-- Maple Yoga | 2026-10-20 | 7
-- Orbit Analytics | 2026-11-07 | 25
DATEDIFF についてさらに学ぶ
このブログの記事:
-
SQL 日付フォーマットガイド:
strftime、TO_CHAR、DATE_FORMAT、FORMATで日付をテキストに変換する - SQL 日付挿入ガイド: 日付型と日付値の挿入方法
- SQL で引き算する方法: 数値、日付、タイムスタンプの引き算
-
PostgreSQL で引き算する方法:
age()を含む日付とタイムスタンプの演算 -
SQLite の引き算操作:
julianday()による日付の差分 - PostgreSQL の interval から合計日数・時間・分を取り出す: interval を数値に変換する
- SQL COALESCE ガイド: NULL の日付にデフォルト値を設定する
公式ドキュメント:
Beekeeper Studioは無料のオープンソースのデータベースGUIです
これまで使った中で最高のSQLクエリ&エディタツールです。データベースの管理に必要なものがすべて揃っています。 - ⭐⭐⭐⭐⭐ Mit
Beekeeper Studioは高速で直感的、そして使いやすいツールです。多数のデータベースに対応し、Windows、Mac、Linuxで快適に動作します。
Beekeeper Studio 利用者の声
「Beekeeper Studio で以前の SQL ワークフローを完全に置き換えました。高速で直感的で、データベース作業がまた楽しくなりました。」
「多くのデータベース GUI を試してきましたが、Beekeeper は機能とシンプルさのバランスが完璧です。とにかくちゃんと動きます。」