SQL Server DATEADD関数とは?初心者にもわかる基本の使い方ガイド


SQL Server DATEADD関数とは?初心者にもわかる基本の使い方ガイド

はじめに:日付計算の重要性とDATEADD関数

データベースを扱う上で、日付や時刻の計算は避けて通れない非常に重要な操作です。例えば、

  • 注文日から商品の発送予定日(3日後)を計算したい。
  • 契約開始日から契約満了日(1年後)を計算したい。
  • あるイベントから1ヶ月後のリマインダーを設定したい。
  • システムのログを過去24時間分だけ抽出したい。
  • ユーザーの生年月日から現在の年齢を計算したい。

このような様々なシナリオで、特定の日付や時刻に、ある一定の時間間隔(日数、月数、年数、時間、分など)を加算したり減算したりする処理が必要になります。SQL Serverにおいて、この日付/時刻の計算、特に「加算」を行うための基本的な関数が DATEADD 関数です。

DATEADD関数は非常に強力で、日付や時刻の計算を直感的かつ正確に行うことができます。SQL Serverでのデータ操作において、日付関数をマスターすることは必須と言えるでしょう。

この記事では、SQL ServerのDATEADD関数について、全くの初心者の方でも理解できるよう、その基本的な使い方から、様々な応用例、さらには知っておくべきエッジケースや注意点まで、詳細かつ網羅的に解説していきます。この記事を最後まで読むことで、あなたは自信を持ってDATEADD関数を使いこなし、様々な日付計算のタスクを効率的に処理できるようになるでしょう。

さあ、DATEADD関数の世界へ一緒に踏み出しましょう。

DATEADD関数とは?基本的な考え方

DATEADD関数は、その名前が示す通り、「日付(DATE)」に「追加(ADD)」するための関数です。具体的には、指定した日付や時刻に対して、指定した単位(年、月、日、時、分など)で、指定した数値を加算(または減算)した結果の日付/時刻を返します。

考え方としては、非常にシンプルです。例えば、「今日から10日後」を知りたい場合、基準となる日付(今日)に、単位(日)で、数値(10)を「加算」するというイメージです。DATEADD関数は、この計算をSQL Serverの中で正確に行ってくれます。

例:

今日が2023年10月27日だとします。
DATEADDを使って「今日から10日後」を計算する場合:
– 基準日: 2023年10月27日
– 単位: 日
– 数値: 10
– 結果: 2023年11月6日

このように、DATEADD関数は、指定した基準日から「未来」または「過去」の特定の日付/時刻を計算するために使用されます。過去の時刻を計算したい場合は、加算する数値を負の値として指定します。

DATEADD関数の基本的な構文

DATEADD関数の基本的な構文は以下の通りです。

sql
DATEADD ( datepart , number , date )

この構文には3つの引数があります。それぞれの引数について詳しく見ていきましょう。

  1. datepart:

    • これは、加算(または減算)する時間間隔の「単位」を指定する部分です。例えば、「年」、「月」、「日」、「時」、「分」、「秒」といった単位を指定します。
    • 文字列として指定しますが、シングルクォーテーションで囲む必要はありません(囲んでもエラーにはなりませんが、通常は囲みません)。
    • datepartには、完全な名前(例: year)と省略形(例: yy)があります。どちらを使っても同じ結果になります。一般的には省略形がよく使われますが、可読性を重視する場合は完全な名前を使うのも良いでしょう。
  2. number:

    • これは、datepartで指定した単位で加算(または減算)する「数」を指定する部分です。
    • 整数値を指定します。正の値を指定すると未来の日付/時刻が計算され、負の値を指定すると過去の日付/時刻が計算されます。
    • numberのデータ型はINTが推奨されます。他の整数型(BIGINTなど)も指定可能ですが、暗黙的な変換が行われます。非整数値を指定するとエラーになります。
    • この数値が大きい場合、計算結果がサポートされる日付/時刻の範囲を超える可能性があります。その場合、エラーが発生します。
  3. date:

    • これは、計算の「基準」となる日付または日付時刻の値を指定する部分です。
    • サポートされる日付/時刻データ型(DATE, TIME, DATETIME, DATETIME2, DATETIMEOFFSET, SMALLDATETIME)の列、変数、またはリテラル(定数)を指定できます。
    • dateNULLを指定した場合、DATEADD関数はNULLを返します。

DATEADD関数は、計算結果として新しい日付または日付時刻の値を返します。戻り値のデータ型は、date引数に指定したデータ型によって決まります。ただし、SMALLDATETIME型が指定された場合は、戻り値の精度がDATETIME型と同じになり、秒以下の精度が丸められることに注意が必要です。

datepart引数の詳細:様々な単位での計算

datepart引数には、非常に多くの種類があります。それぞれの単位によって、日付/時刻の計算結果がどのように変わるのかを見ていきましょう。ここでは主なdatepartとその例を詳しく解説します。

1. 年 (year, yy, yyyy)

年単位で加算/減算を行います。
例: 現在の日時から5年後を計算する

sql
SELECT DATEADD(year, 5, GETDATE()); -- GETDATE()は現在の日時を取得する関数

  • 例: 特定の日付から3年「前」を計算する

sql
SELECT DATEADD(year, -3, '2025-07-15');

年単位の計算はシンプルで直感的です。うるう年なども自動的に考慮されます。

2. 四半期 (quarter, qq, q)

四半期単位で加算/減算を行います。1四半期は3ヶ月です。
例: 特定の日付から2四半期後(つまり6ヶ月後)を計算する

sql
SELECT DATEADD(quarter, 2, '2023-10-27'); -- 結果は2024-04-27

  • 例: 特定の日付から1四半期前(つまり3ヶ月前)を計算する

sql
SELECT DATEADD(quarter, -1, '2023-10-27'); -- 結果は2023-07-27

quarterを使用すると、月単位で計算するよりも四半期ごとの期間計算が容易になります。

3. 月 (month, mm, m)

月単位で加算/減算を行います。
例: 現在の日時から1ヶ月後を計算する

sql
SELECT DATEADD(month, 1, GETDATE());

  • 例: 特定の日付から6ヶ月前を計算する

sql
SELECT DATEADD(month, -6, '2024-03-10'); -- 結果は2023-09-10

月単位の計算で注意が必要なのは、「月末」の扱いです。
例: 1月31日から1ヶ月後を計算する場合

sql
SELECT DATEADD(month, 1, '2023-01-31'); -- 結果は2023-02-28 (または2023-02-29 うるう年なら)

1月31日に1ヶ月加算すると、本来なら2月31日となるはずですが、2月には31日がありません。この場合、DATEADD関数は自動的にその月の最終日を返します。つまり、2月28日(またはうるう年の場合は2月29日)となります。これは、月単位の計算における重要な挙動の一つです。

  • 例: 3月31日から1ヶ月後を計算する場合

sql
SELECT DATEADD(month, 1, '2023-03-31'); -- 結果は2023-04-30

4月も31日がありませんので、結果は4月30日となります。

  • 例: 3月30日から1ヶ月後を計算する場合

sql
SELECT DATEADD(month, 1, '2023-03-30'); -- 結果は2023-04-30

この場合も4月30日となります。元の日の「日」の部分(30日)が、加算後の月の最終日よりも大きい場合、結果はその月の最終日となります。元の日の「日」の部分が、加算後の月の最終日以下である場合は、その「日」が維持されます。

  • 例: 3月29日から1ヶ月後を計算する場合

sql
SELECT DATEADD(month, 1, '2023-03-29'); -- 結果は2023-04-29

4月には29日がありますので、結果は4月29日となります。

この月末処理のルールは、monthquarter, yearといった、月を跨ぐ可能性のあるdatepartで注意が必要です。特にビジネスロジックで「毎月n日」というルールがある場合、月末の挙動を理解しておくことが重要です。

4. 年の日 (dayofyear, dy, y)

年の最初の日(1月1日)からの日数で加算/減算を行います。通常、日単位の計算にはdayを使用するため、dayofyearはあまり使われません。
例: 特定の日付から10日後を、dayofyearを使って計算する

sql
SELECT DATEADD(dayofyear, 10, '2023-10-27'); -- 結果は2023-11-06

結果はdayを使った場合と同じになりますが、dayofyearという名前が少し紛らわしいかもしれません。特別な理由がない限り、日単位の計算にはdayを使用するのが一般的です。

5. 日 (day, dd, d)

日単位で加算/減算を行います。最も頻繁に使用されるdatepartの一つです。
例: 現在の日時から7日後(1週間後)を計算する

sql
SELECT DATEADD(day, 7, GETDATE());

  • 例: 特定の日付から2日前を計算する

sql
SELECT DATEADD(day, -2, '2023-10-27'); -- 結果は2023-10-25

day単位の計算はシンプルで、直感通りの結果になります。月を跨いでも、年を跨いでも、正確に日数を加算/減算します。

6. 週 (week, wk, ww)

週単位で加算/減算を行います。1週は7日として計算されます。
例: 現在の日時から2週間後を計算する

sql
SELECT DATEADD(week, 2, GETDATE()); -- 結果はGETDATE()から14日後

  • 例: 特定の日付から1週間前を計算する

sql
SELECT DATEADD(week, -1, '2023-10-27'); -- 結果は2023-10-20

weekを使用すると、DATEADD(day, 7 * number, date)と同じ結果になりますが、週単位の計算であることが明確になります。

weekや後述するweekdayを使用する際に、週の始まり(日曜日始まりか、月曜日始まりかなど)が重要になる場合があります。SQL Serverでは、SET DATEFIRSTという設定で、週の始まりの曜日を指定できます。デフォルトは環境によって異なりますが、一般的にはアメリカ英語環境では日曜日(1)、イギリス英語環境では月曜日(1)です。
SET DATEFIRSTの値は、@@DATEFIRSTというグローバル変数で確認できます。
DATEADD関数のweek``datepart自体は、SET DATEFIRSTの設定に影響されずに単純に7日として計算しますが、DATEPART(week, date)DATENAME(weekday, date)など、週や曜日の「番号」や「名前」を取得する関数はSET DATEFIRSTの影響を受けます。週の計算を行う際には、この設定にも注意を払う必要がある場合があります。

7. 曜日 (weekday, dw)

曜日単位で加算/減算を行います。これもdayを使った場合と同じ結果になります。dayofyearと同様に、あまり使われません。
例: 特定の日付から3日後を、weekdayを使って計算する

sql
SELECT DATEADD(weekday, 3, '2023-10-27'); -- 結果は2023-10-30

weekdayという名前ですが、特定の曜日(例えば次の月曜日)を計算するわけではありません。単純に指定した日数分を加算/減算するだけです。特定の曜日を計算したい場合は、DATEPART(weekday, date)などを使って曜日の番号を取得し、現在の曜日との差分を計算してDATEADD(day, difference, date)のように組み合わせて使う必要があります。

8. 時 (hour, hh)

時間単位で加算/減算を行います。
例: 現在の日時から3時間後を計算する

sql
SELECT DATEADD(hour, 3, GETDATE());

  • 例: 特定の日時より9時間前を計算する

sql
SELECT DATEADD(hour, -9, '2023-10-27 15:30:00'); -- 結果は2023-10-27 06:30:00

時間単位の計算は、日を跨ぐ場合も正しく行われます。
例: 特定の日時より10時間後を計算する(日を跨ぐ例)

sql
SELECT DATEADD(hour, 10, '2023-10-27 20:00:00'); -- 結果は2023-10-28 06:00:00

9. 分 (minute, mi, n)

分単位で加算/減算を行います。
例: 現在の日時から45分後を計算する

sql
SELECT DATEADD(minute, 45, GETDATE());

  • 例: 特定の日時より30分前を計算する

sql
SELECT DATEADD(minute, -30, '2023-10-27 10:15:00'); -- 結果は2023-10-27 09:45:00

分単位の計算も、時間や日を跨いで正しく行われます。
例: 特定の日時より90分後を計算する(時間を跨ぐ例)

sql
SELECT DATEADD(minute, 90, '2023-10-27 10:00:00'); -- 結果は2023-10-27 11:30:00

10. 秒 (second, ss, s)

秒単位で加算/減算を行います。
例: 現在の日時から30秒後を計算する

sql
SELECT DATEADD(second, 30, GETDATE());

  • 例: 特定の日時より5秒前を計算する

sql
SELECT DATEADD(second, -5, '2023-10-27 10:30:45'); -- 結果は2023-10-27 10:30:40

秒単位の計算も、分、時間、日などを跨いで正しく行われます。
例: 特定の日時より100秒後を計算する(分を跨ぐ例)

sql
SELECT DATEADD(second, 100, '2023-10-27 10:30:45'); -- 結果は2023-10-27 10:32:25 (60+40秒)

11. ミリ秒 (millisecond, ms)

ミリ秒単位で加算/減算を行います。
例: 現在の日時から500ミリ秒後を計算する

sql
SELECT DATEADD(millisecond, 500, GETDATE());

ミリ秒を使用する場合、基準となるdate引数のデータ型に注意が必要です。DATETIME型はミリ秒の精度が3ms単位で丸められます(.000, .003, .007, .010, .013, .017 … といった値しか持ちません)。DATETIME2型やDATETIMEOFFSET型はより高い精度(最大100ナノ秒)を持つため、ミリ秒以下の計算にはこれらのデータ型を使用するのが適しています。

  • 例: DATETIME型とDATETIME2型でのミリ秒計算の違い

sql
SELECT DATEADD(millisecond, 1, CAST('2023-10-27 10:30:45.000' AS DATETIME)); -- 結果例: 2023-10-27 10:30:45.003 (3ms単位に丸められる)
SELECT DATEADD(millisecond, 1, CAST('2023-10-27 10:30:45.000' AS DATETIME2)); -- 結果例: 2023-10-27 10:30:45.001 (正確に1ms加算される)

12. マイクロ秒 (microsecond, mcs)

マイクロ秒単位で加算/減算を行います。1ミリ秒 = 1000マイクロ秒です。
例: 特定の日時より100マイクロ秒後を計算する

sql
SELECT DATEADD(microsecond, 100, CAST('2023-10-27 10:30:45.123456' AS DATETIME2));

マイクロ秒、ナノ秒の精度を扱うには、date引数にDATETIME2またはDATETIMEOFFSET型を使用する必要があります。DATETIMESMALLDATETIME型では、秒以下の精度が低いため、これらのdatepartを指定しても期待通りの結果にならないか、エラーになる可能性があります(DATETIMEmicrosecondを指定するとエラーになります)。

13. ナノ秒 (nanosecond, ns)

ナノ秒単位で加算/減算を行います。1マイクロ秒 = 1000ナノ秒、1ミリ秒 = 100万ナノ秒です。DATETIME2(7)DATETIMEOFFSET(7)は、最高100ナノ秒(0.0000001秒)までの精度を扱えます。
例: 特定の日時より100ナノ秒後を計算する

sql
SELECT DATEADD(nanosecond, 100, CAST('2023-10-27 10:30:45.1234567' AS DATETIME2(7)));

nanosecondを使用する場合も、date引数には十分な精度を持つDATETIME2またはDATETIMEOFFSET型を指定する必要があります。

まとめ:datepartの選択

どのdatepartを使うかは、どのような単位で日付/時刻を計算したいかによって決まります。

  • 年、四半期、月、日、週、曜日の計算には、それぞれの名前のdatepart (year, quarter, month, day, week, weekday) を使用します。
  • 時、分、秒の計算には、それぞれの名前のdatepart (hour, minute, second) を使用します。
  • ミリ秒、マイクロ秒、ナノ秒といった高い精度の計算には、それぞれの名前のdatepart (millisecond, microsecond, nanosecond) を使用しますが、基準となる日付/時刻のデータ型(DATETIME2, DATETIMEOFFSET)に注意が必要です。

一般的に、year, month, day, hour, minute, second はよく使われます。quarter, week もビジネスレポートなどで使われることがあります。dayofyear, weekday はあまり頻繁には使われません。秒以下の精度が必要な場合は、millisecond, microsecond, nanosecond を使いますが、これはアプリケーションの要件によります。

number引数の詳細:加算/減算する数値を指定する

number引数は、datepartで指定した単位で、日付や時刻にどれだけ加算(または減算)するかを決定する数値です。

正の値 vs. 負の値

  • number正の値の場合、DATEADD関数は基準となるdateから未来の日付/時刻を計算します。

    • 例: DATEADD(day, 10, GETDATE()) -> 今日から10日後
    • 例: DATEADD(hour, 3, GETDATE()) -> 現在時刻から3時間後
  • number負の値の場合、DATEADD関数は基準となるdateから過去の日付/時刻を計算します。

    • 例: DATEADD(day, -10, GETDATE()) -> 今日から10日前
    • 例: DATEADD(month, -1, GETDATE()) -> 今月から1ヶ月前

このように、numberを正負を使い分けることで、未来と過去の両方の時刻を計算できます。

numberのデータ型と範囲

number引数には、整数型を指定する必要があります。推奨されるデータ型はINTです。INT型の範囲は -2,147,483,648 から 2,147,483,647 です。

加算/減算する値がこの範囲を超える場合や、計算結果がSQL Serverがサポートする日付/時刻の最小値または最大値を超える場合、エラーが発生します。

SQL Serverがサポートする主な日付/時刻データ型の範囲は以下の通りです(詳細な範囲はSQL Serverのバージョンやエディションによって若干異なる場合があります):
DATE: 0001-01-01 から 9999-12-31
DATETIME: 1753-01-01 から 9999-12-31 23:59:59.997
DATETIME2: 0001-01-01 00:00:00.0000000 から 9999-12-31 23:59:59.9999999
DATETIMEOFFSET: 0001-01-01 00:00:00.0000000 +14:00 から 9999-12-31 23:59:59.9999999 -14:00

  • 例: サポート範囲を超える計算

“`sql
— 9999-12-31から1日後を計算しようとするとエラー (DATETIME2の場合)
SELECT DATEADD(day, 1, CAST(‘9999-12-31’ AS DATE)); — エラー: 結果が日付範囲外
SELECT DATEADD(day, 1, CAST(‘9999-12-31’ AS DATETIME2)); — エラー: 結果が日付範囲外

— 0001-01-01から1日前を計算しようとするとエラー (DATETIME2の場合)
SELECT DATEADD(day, -1, CAST(‘0001-01-01’ AS DATE)); — エラー: 結果が日付範囲外
SELECT DATEADD(day, -1, CAST(‘0001-01-01’ AS DATETIME2)); — エラー: 結果が日付範囲外

DATETIME型の最小日付より前の日付を計算しようとするとエラー
SELECT DATEADD(year, -1, CAST(‘1753-01-01’ AS DATETIME)); — エラー: 結果が日付範囲外
“`

numberに非常に大きな値を指定すると、たとえ基準日が範囲内であっても、計算途中でINTの最大値/最小値を超えてしまう可能性があります。例えば、DATEADD(nanosecond, 3000000000, GETDATE()) のように、numberが30億の場合、これはINTの最大値を超えます。

  • 例: numberINTの範囲を超える場合

sql
-- 30億秒はINTの範囲を超えるためエラー
SELECT DATEADD(second, 3000000000, GETDATE()); -- エラー: Arithmetic overflow error converting expression to data type int.

このような場合は、計算単位を大きなものに変えるか、複数のDATEADD関数を組み合わせる必要があります。例えば、30億秒は約95年なので、DATEADD(year, 95, GETDATE()) のように計算単位を大きくして対応できる場合もあります。

date引数の詳細:基準となる日付/時刻

date引数は、DATEADD関数が計算を行う上での「出発点」となる日付または日付時刻の値です。この引数には、様々なデータ型を指定できます。

SQL Serverでサポートされている主な日付/時刻データ型は以下の通りです。DATEADD関数は、これらのデータ型をdate引数として受け付け、計算結果も同じデータ型(SMALLDATETIMEを除く)で返します。

  1. DATE:

    • 年月日のみを格納します(時刻部分は持ちません)。
    • 範囲: 0001-01-01 から 9999-12-31。
    • 精度: 日。
    • DATE型に対してhour, minute, second, millisecond, microsecond, nanosecondなどの時間単位のdatepartDATEADDを実行した場合、時刻部分は00:00:00.0000000として扱われます。加算結果が日付を跨ぐ場合は日付部分も変更されます。
    • 例: DATE型で日を加算

    sql
    SELECT DATEADD(day, 5, CAST('2023-10-27' AS DATE)); -- 結果: 2023-11-01

    例: DATE型で時間を加算(時刻部分は00:00:00として扱われる)

    sql
    SELECT DATEADD(hour, 25, CAST('2023-10-27' AS DATE)); -- 結果: 2023-10-28 01:00:00.0000000 (時刻部分が加算される)

    ただし、DATE型は本来時刻部分を持たないため、上記の結果は内部的に一時的に日付+時刻として扱われるか、結果の表示形式によって時刻部分が見える/見えないことがあります。通常、DATE型の結果は年月日のみが返されます。

    sql
    -- 結果のデータ型はDATEになる
    SELECT CAST(DATEADD(hour, 25, CAST('2023-10-27' AS DATE)) AS DATE); -- 結果: 2023-10-28

    DATE型に時間単位を加算して日付を跨ぐ計算を行うことは可能ですが、混乱を避けるためには、時刻部分も考慮する場合は最初からDATETIMEDATETIME2を使用するのがおすすめです。

  2. TIME:

    • 時分秒と小数点以下の秒(精度は宣言による)のみを格納します(日付部分は持ちません)。
    • 範囲: 00:00:00.0000000 から 23:59:59.9999999。
    • TIME型に対してyear, quarter, month, day, week, weekday, dayofyearなどの日付単位のdatepartDATEADDを実行しても意味がありません。エラーにはなりませんが、時刻部分に変化はなく、日付部分がないため計算結果として有効な日付/時刻は得られません。
    • TIME型に対してhour, minute, second, millisecond, microsecond, nanosecondなどの時間単位のdatepartDATEADDを実行すると、時刻部分の計算が行われます。時間を跨ぐ場合、時刻は循環します(例: 23:00に2時間加算すると01:00になります)。
    • 例: TIME型で分を加算

    sql
    SELECT DATEADD(minute, 30, CAST('10:15:00' AS TIME)); -- 結果: 10:45:00.0000000

    例: TIME型で時間を加算(循環する例)

    sql
    SELECT DATEADD(hour, 3, CAST('22:00:00' AS TIME)); -- 結果: 01:00:00.0000000

    TIME型は日付情報を持たないため、日付計算には適していません。時刻の計算にのみ使用します。

  3. DATETIME:

    • 年月日と時分秒、そしてミリ秒を格納します。
    • 範囲: 1753-01-01 00:00:00 から 9999-12-31 23:59:59.997。
    • 精度: 秒は常に0、3、または7で終わり、ミリ秒は3.33ms単位で丸められます。
    • 歴史的な理由から最も古くからある日付/時刻型ですが、精度の問題や古い日付範囲しかサポートしないという制限があります。
    • DATEADD関数はこの型で広く使われます。
    • 例: DATETIME型で月を加算

    sql
    SELECT DATEADD(month, 2, CAST('2023-10-27 14:00:00' AS DATETIME)); -- 結果: 2023-12-27 14:00:00.000

    例: DATETIME型でミリ秒を加算(丸め込みの例)

    sql
    SELECT DATEADD(millisecond, 1, CAST('2023-10-27 10:30:45.000' AS DATETIME)); -- 結果例: 2023-10-27 10:30:45.003

  4. DATETIME2:

    • 年月日と時分秒、そして小数点以下の秒を高い精度(宣言によって0~7桁、デフォルトは7桁)で格納します。
    • 範囲: 0001-01-01 00:00:00.0000000 から 9999-12-31 23:59:59.9999999。
    • 精度、範囲ともにDATETIMEより優れており、新しいアプリケーションでは推奨される型です。
    • DATEADD関数はこの型で、あらゆるdatepartに対して正確な計算を行います。
    • 例: DATETIME2型でマイクロ秒を加算

    sql
    SELECT DATEADD(microsecond, 100, CAST('2023-10-27 10:30:45.1234567' AS DATETIME2(7))); -- 結果: 2023-10-27 10:30:45.1235567

  5. DATETIMEOFFSET:

    • 年月日と時分秒、そして小数点以下の秒を高い精度で格納し、さらにタイムゾーンオフセット情報(UTCからの差)も持ちます。
    • 範囲: 0001-01-01 00:00:00.0000000 +14:00 から 9999-12-31 23:59:59.9999999 -14:00。
    • DATEADD関数はこの型に対しても正確な計算を行います。ただし、datepartによってはオフセット情報に影響を与える可能性があります。基本的にはローカル時刻(オフセットを含む時刻)に対して計算が行われます。
    • 例: DATETIMEOFFSET型で日を加算

    sql
    SELECT DATEADD(day, 1, CAST('2023-10-27 14:00:00 +09:00' AS DATETIMEOFFSET)); -- 結果: 2023-10-28 14:00:00.0000000 +09:00

    DATEADD関数自体は、タイムゾーンオフセットの値を変更しません。常に基準となるdateのタイムゾーンを維持したまま、そのローカル時刻に対して加算を行います。タイムゾーンを考慮して日付計算を行いたい場合は、SWITCHOFFSETAT TIME ZONEといった関数と組み合わせて使用する必要があります。

  6. SMALLDATETIME:

    • 年月日と時分を格納します(秒とミリ秒は持ちません)。
    • 範囲: 1900-01-01 00:00:00 から 2079-06-06 23:59:00。
    • 精度: 秒とミリ秒は最も近い分に丸められます。
    • DATEADD関数にSMALLDATETIMEを指定した場合、戻り値はDATETIMEになります。秒以下の加算/減算は行えず、秒以下のdatepartを指定した場合、計算結果は丸められます。
    • 例: SMALLDATETIME型で分を加算(戻り値がDATETIMEになる例)

    sql
    SELECT DATEADD(minute, 1, CAST('2023-10-27 10:30' AS SMALLDATETIME)); -- 結果: 2023-10-27 10:31:00.000 (戻り値はDATETIME)

    例: SMALLDATETIME型で秒を加算(丸め込みの例)

    sql
    SELECT DATEADD(second, 30, CAST('2023-10-27 10:30' AS SMALLDATETIME)); -- 結果: 2023-10-27 10:31:00.000 (最も近い分に丸められる)
    SELECT DATEADD(second, 29, CAST('2023-10-27 10:30' AS SMALLDATETIME)); -- 結果: 2023-10-27 10:30:00.000 (最も近い分に丸められる)

    SMALLDATETIME型は非推奨であり、新しい開発ではDATETIME2を使用することが推奨されます。

dateにNULLを指定した場合

date引数にNULLを指定した場合、DATEADD関数は計算結果としてNULLを返します。
例:

sql
SELECT DATEADD(day, 10, NULL); -- 結果: NULL

これは他の多くのSQL関数と同様の挙動です。入力がNULLであれば結果もNULLになります。

dateに無効な値を指定した場合

date引数に、指定したデータ型として有効な日付/時刻ではない文字列などを指定した場合、CASTCONVERTなどの変換関数がエラーになるか、DATEADD関数自体がエラーになる可能性があります。
例: 無効な日付文字列

sql
SELECT DATEADD(day, 1, '2023-02-30'); -- 無効な日付のためエラー

SQL Serverのバージョンや設定によっては、暗黙的な型変換でエラーにならない場合もありますが、明示的に無効な日付を指定するとエラーになるのが一般的です。日付/時刻データ型への変換は確実に行われるようにしましょう。

様々なdatepartnumberの組み合わせ例

DATEADD関数は、様々なdatepartnumberの組み合わせで、柔軟な日付計算が可能です。いくつかの実用的な例を見てみましょう。

例1:次の月の最初の日を計算する

現在の日付(または特定の日付)から、次の月の最初の日を計算したい場合。
手順としては:
1. まず、現在の日付から1ヶ月加算して、次の月の中の任意の日付を取得します。
2. 次に、その日付の「日」を「1」に変更します。

DATEADD関数とDATEFROMPARTS関数(またはEOMONTH関数とDATEADDの組み合わせ)を使って実現できます。ここではDATEADDを中心に解説します。

“`sql
— 現在の日付を取得
DECLARE @CurrentDate DATE = GETDATE(); — 例: 2023-10-27

— 1. 現在の日付から1ヶ月後の日付を取得 (例えば次の月の27日)
DECLARE @NextMonthSameDay DATE = DATEADD(month, 1, @CurrentDate); — 結果: 2023-11-27

— 2. その日付の「日」の部分を「1」に変更する
— DATEFROMPARTS関数を使う方法 (年、月、日を指定してDATE型を生成)
SELECT DATEFROMPARTS(YEAR(@NextMonthSameDay), MONTH(@NextMonthSameDay), 1) AS NextMonthFirstDay; — 結果: 2023-11-01

— DATEADDとDATEPARTを組み合わせる方法 (少し複雑)
— 現在日付の「日」の部分の数だけ過去に戻り、さらに1日加算する
— 例: 2023-10-27の場合 -> 27日分戻る -> 2023-10-00 (概念的) -> DATEADDでは月末から計算される
— 正しい方法: まず今月の初日を計算し、それに1ヶ月加算する
— 今月の初日: 現在の日付から (現在の日付の「日」- 1) 日前を計算する
DECLARE @CurrentMonthFirstDay DATE = DATEADD(day, 1 – DAY(@CurrentDate), @CurrentDate); — 例: 2023-10-27 -> 1-27 = -26日 -> 2023-10-01

— 今月の初日から1ヶ月加算する
SELECT DATEADD(month, 1, @CurrentMonthFirstDay) AS NextMonthFirstDay; — 結果: 2023-11-01
``
後者の方法 (
DATEADD(month, 1, DATEADD(day, 1 – DAY(@CurrentDate), @CurrentDate))) が、DATEADD`関数のみを組み合わせる典型的なテクニックです。まず「その月の1日」を計算し、そこに月を加算します。

例2:3ヶ月前の月末を計算する

現在の日付(または特定の日付)から、3ヶ月前の月末を計算したい場合。
手順としては:
1. 現在の日付から3ヶ月「前」に移動します。
2. その結果得られた月の月末を取得します。

月末を取得するには、EOMONTH関数が便利です。EOMONTH関数は、指定した日付の月の最終日を返します。

“`sql
— 現在の日付を取得
DECLARE @CurrentDate DATE = GETDATE(); — 例: 2023-10-27

— 1. 現在の日付から3ヶ月前に移動する
DECLARE @ThreeMonthsAgo DATE = DATEADD(month, -3, @CurrentDate); — 結果: 2023-07-27

— 2. その日付の月末を取得する
SELECT EOMONTH(@ThreeMonthsAgo) AS EndOfThreeMonthsAgo; — 結果: 2023-07-31
``EOMONTH関数は非常に直感的ですが、SQL Server 2012以降で使用可能です。それ以前のバージョンでは、DATEADDDAY`を組み合わせて月末を計算する複雑な方法が必要でした。

“`sql
— EOMONTH関数がない場合の3ヶ月前の月末計算 (SQL Server 2008 R2以前など)
— 1. 現在の日付から3ヶ月前の月の「次の月」の1日を計算する
— 例: 2023-10-27 -> 3ヶ月前は2023-07-27
— その月の次の月は2023年8月なので、2023年8月1日を計算する
DECLARE @NextMonthOfThreeMonthsAgoFirstDay DATE = DATEADD(month, -2, DATEADD(day, 1 – DAY(@CurrentDate), @CurrentDate)); — 2023-10-01から-2ヶ月 -> 2023-08-01

— 2. その日付(2023年8月1日)から1日前を計算する
SELECT DATEADD(day, -1, @NextMonthOfThreeMonthsAgoFirstDay) AS EndOfThreeMonthsAgo_Legacy; — 結果: 2023-07-31
``
この例は、
DATEADDを複数回、異なるdatepartnumber`で適用することで、複雑な日付計算を実現できることを示しています。

例3:特定の期間(例:先月)の範囲を取得する

レポートなどで、先月のデータだけを抽出したい場合、抽出条件として「先月の最初の日から先月の最後の日まで」という範囲が必要です。

“`sql
— 現在の日付を取得
DECLARE @CurrentDate DATE = GETDATE(); — 例: 2023-10-27

— 今月の初日を計算
DECLARE @CurrentMonthFirstDay DATE = DATEADD(day, 1 – DAY(@CurrentDate), @CurrentDate); — 結果: 2023-10-01

— 先月の初日を計算 (今月の初日から1ヶ月前)
DECLARE @LastMonthFirstDay DATE = DATEADD(month, -1, @CurrentMonthFirstDay); — 結果: 2023-09-01

— 今月の初日から1日前を計算 (先月の最終日)
DECLARE @LastMonthLastDay DATE = DATEADD(day, -1, @CurrentMonthFirstDay); — 結果: 2023-09-30

— 抽出条件の例
— SELECT * FROM YourTable WHERE YourDateColumn BETWEEN @LastMonthFirstDay AND @LastMonthLastDay;
— または (推奨される方法 – 終端を含まない条件)
— SELECT * FROM YourTable WHERE YourDateColumn >= @LastMonthFirstDay AND YourDateColumn < @CurrentMonthFirstDay;
``
この例も、
DATEADD関数とDAY関数(日付の「日」の部分を取得)を組み合わせています。日付の範囲を指定する際は、BETWEENを使うか、>=<を組み合わせるか、データの型や要件によって選択します。日付時刻型の列を扱う場合は、後者の>=<の組み合わせ(例:>= ‘2023-09-01 00:00:00’ AND < ‘2023-10-01 00:00:00’`) が、最後の日の時刻部分の問題を回避できるため推奨されることが多いです。

エッジケースと注意点

DATEADD関数を使用する上で、知っておくべき特定のエッジケースや注意点があります。

1. うるう年

DATEADD関数はうるう年(2月29日がある年)を正しく考慮して計算を行います。
例: うるう年の2月29日から1年後を計算

sql
SELECT DATEADD(year, 1, '2024-02-29'); -- 2024年はうるう年
-- 結果: 2025-02-28

2025年はうるう年ではないため、結果は2月28日となります。これは月単位の計算における月末処理と同じルールに従います。加算後の年に2月29日が存在しないため、その月の最終日である2月28日が返されます。

  • 例: うるう年ではない年の2月28日から1年後を計算

sql
SELECT DATEADD(year, 1, '2023-02-28'); -- 2023年はうるう年ではない
-- 結果: 2024-02-28

2024年はうるう年ですが、元の日の「日」の部分である28日は2024年2月にも存在するため、結果は2月28日となります。

2. 月末処理

前述のmonth``datepartの説明でも触れましたが、月単位(または四半期、年単位)で計算を行う際、元の日の「日」の部分が加算後の月の最終日よりも大きい場合、結果はその月の最終日となります。
例: 5月31日から3ヶ月後

sql
SELECT DATEADD(month, 3, '2023-05-31');
-- 結果: 2023-08-31 (8月は31日まであるので、31日が維持される)

例: 5月31日から4ヶ月後

sql
SELECT DATEADD(month, 4, '2023-05-31');
-- 結果: 2023-09-30 (9月は30日までしかないので、30日になる)

この挙動は仕様であり、意図したものでない場合は、例えば常に月の最初の日に対して計算を行うなどの工夫が必要になります(例1参照)。

3. 時刻の繰り上がり/繰り下がり

hour, minute, secondなどの時間単位で大きな値を加算/減算すると、日付や時間帯が繰り上がったり繰り下がったりします。これは期待通りの自然な挙動です。
例: 23時30分に1時間加算

sql
SELECT DATEADD(hour, 1, '2023-10-27 23:30:00'); -- 結果: 2023-10-28 00:30:00

  • 例: 0時15分から30分減算

sql
SELECT DATEADD(minute, -30, '2023-10-27 00:15:00'); -- 結果: 2023-10-26 23:45:00

これらの計算は、日を跨いでも正しく行われます。

4. 精度

millisecond, microsecond, nanosecondなどの細かい単位で計算を行う場合、基準となるdate引数のデータ型がその精度をサポートしているか確認が必要です。
DATETIME型はミリ秒が3.33ms単位で丸められるため、正確なミリ秒計算には不向きです。
SMALLDATETIME型は秒以下の情報を持ちません。DATEADDに渡すと戻り値はDATETIMEになり、秒以下のdatepartを指定しても丸められます。
DATETIME2型やDATETIMEOFFSET型は高い精度をサポートしており、細かい単位での計算に適しています。

必要な精度に合わせて適切なデータ型を使用することが重要です。

5. SET DATEFIRSTの影響 (week, weekday)

weekweekdaydatepartを使用する場合、SET DATEFIRSTの設定が結果に影響を与える可能性がある(または与えない)ことに注意が必要です。
DATEADD(week, number, date): この計算自体は、SET DATEFIRSTの影響を受けません。単にdatenumber * 7日を加算/減算するだけです。
DATEADD(weekday, number, date): この計算も、SET DATEFIRSTの影響を受けません。単にdatenumber日を加算/減算するだけです。

ただし、DATEPART(week, date)DATENAME(weekday, date)など、日付から週番号や曜日名を取得する関数はSET DATEFIRSTの影響を受けます。例えば、「来週の月曜日」を計算する際に、まず基準日の曜日番号を取得し、それに基づいて日数を計算する場合、その曜日番号の取得部分がSET DATEFIRSTに依存するため、間接的にDATEADDを使った計算結果もSET DATEFIRSTに依存することになります。

もし週の始まりを特定の曜日に固定して計算を行いたい場合は、SET DATEFIRSTを明示的に設定するか、曜日番号を取得するロジックをSET DATEFIRSTに依存しない形(例: 固定の基準日から計算するなど)で記述する必要があります。

6. タイムゾーン (DATETIMEOFFSET)

DATETIMEOFFSET型に対してDATEADDを実行しても、タイムゾーンオフセット自体は変化しません。加算/減算はあくまでそのオフセットでのローカル時刻に対して行われます。タイムゾーンを考慮して、異なるタイムゾーンでの同時刻を計算したり、夏時間などを考慮した正確な時間を計算したい場合は、SWITCHOFFSETAT TIME ZONEといった関数をDATEADDと組み合わせて使用する必要があります。

  • 例: DATETIMEOFFSETに時間を加算

sql
DECLARE @dt DATETIMEOFFSET = '2023-10-27 10:00:00 +09:00';
SELECT DATEADD(hour, 5, @dt); -- 結果: 2023-10-27 15:00:00.0000000 +09:00 (オフセットは変わらない)

DATEADD関数と関連関数

SQL Serverには、DATEADD以外にも様々な日付/時刻関連関数があります。これらの関数をDATEADDと組み合わせて使うことで、より複雑な日付計算や操作が可能になります。

  1. DATEDIFF ( datepart , startdate , enddate ):

    • DATEADDとは逆の操作を行います。指定した2つの日付/時刻(startdateenddate)の間の期間を、指定したdatepart単位で返します。
    • 例: 2つの日付の間の日数を取得

    sql
    SELECT DATEDIFF(day, '2023-10-01', '2023-10-27'); -- 結果: 26

    DATEADDDATEDIFFは互いに補完する関係にあります。ある日から特定期間後の日付を知りたい場合はDATEADD、2つの日付の間にどれくらいの期間があるかを知りたい場合はDATEDIFFを使用します。

  2. GETDATE(), SYSDATETIME(), GETUTCDATE(), SYSUTCDATETIME(), SYSDATETIMEOFFSET():

    • これらは現在のシステム日時を取得する関数です。GETDATE()GETUTCDATE()DATETIME型を返しますが、精度が低い(3.33ms)などの制約があります。
    • SYSDATETIME(), SYSUTCDATETIME(), SYSDATETIMEOFFSET()はSQL Server 2008で導入された新しい関数で、より高い精度(DATETIME2またはDATETIMEOFFSET型)でシステム日時を取得できます。
    • DATEADD関数のdate引数にこれらの関数をよく使用します。
    • 例: 現在から1週間後の日付を取得

    sql
    SELECT DATEADD(week, 1, GETDATE());

    例: 現在時刻から1時間後の正確な時刻を取得

    sql
    SELECT DATEADD(hour, 1, SYSDATETIME()); -- DATETIME2型で高精度な現在時刻を取得

  3. EOMONTH ( start_date [, month_to_add ] ):

    • 指定した日付の月の最終日を返します。オプションで、指定した月数を加算/減算した月の最終日を計算することもできます。SQL Server 2012以降で使用可能です。
    • DATEADDと組み合わせて、月末基準の計算を簡単に行うことができます。
    • 例: 現在の月の最終日を取得

    sql
    SELECT EOMONTH(GETDATE());

    例: 2ヶ月後の月の最終日を取得

    sql
    SELECT EOMONTH(GETDATE(), 2); -- EOMONTHの第2引数を使う方法
    SELECT EOMONTH(DATEADD(month, 2, GETDATE())); -- DATEADDを組み合わせて使う方法

  4. DATEPART ( datepart , date ):

    • 指定した日付/時刻から、指定したdatepart(年、月、日、時など)の値(数値)を抽出します。
    • 例: 現在の年を取得

    sql
    SELECT DATEPART(year, GETDATE());

    DATEPARTは、DATEADDと組み合わせて、特定の日付部分に基づいた計算を行う際によく使用されます(例1や例3のように、日付の「日」を取得して月の初日を計算する場合など)。

  5. DATENAME ( datepart , date ):

    • DATEPARTと似ていますが、datepartの「名前」(文字列)を返します。例えば、曜日の名前(’Monday’, ‘火曜日’など)や月の名前(’January’, ’10月’など)を取得できます。
    • 例: 現在の曜日の名前を取得

    sql
    SELECT DATENAME(weekday, GETDATE()); -- 結果例: 'Friday' (英語環境の場合)

    DATENAMEDATEADDと直接組み合わせて計算を行うことは少ないですが、計算結果を表示する際に役立ちます。

これらの関連関数とDATEADDを組み合わせることで、より複雑で実用的な日付計算のロジックを構築できます。

パフォーマンスに関する考慮事項

DATEADD関数自体は非常に高速な関数ですが、クエリの中でどのように使用するかによって、全体のパフォーマンスに影響を与える可能性があります。特に、大量のデータを扱うテーブルのWHERE句やJOIN句で使用する場合に考慮が必要です。

  1. 計算列での利用:

    • テーブルに計算列としてDATEADDの結果を追加することができます。例えば、OrderDateからDueDate (OrderDateの3日後) を計算列として持つ場合などです。
    • 持続的計算列 (PERSISTED) にすると、計算結果が物理的に保存され、その列にインデックスを作成することも可能です。これにより、計算列を条件とするクエリのパフォーマンスが向上する場合があります。ただし、データの更新時には計算コストがかかります。
  2. WHERE句での利用:

    • フィルタリング条件としてDATEADDを使用する場合、書き方によってはインデックスが効かなくなることがあります。
    • 例:
      “`sql
      — インデックスが効きにくい可能性がある書き方
      SELECT * FROM Orders WHERE DATEADD(day, 3, OrderDate) >= ‘2023-11-01’;

    — インデックスが効きやすい書き方 (SARGableな条件)
    SELECT * FROM Orders WHERE OrderDate >= DATEADD(day, -3, ‘2023-11-01’);
    ``
    - 後者の書き方 (
    OrderDate >= DATEADD(day, -3, ‘2023-11-01’)) では、インデックス付きのOrderDate`列自体が計算の対象になっていないため、SQL Serverがインデックスを効率的に利用できる可能性が高まります。これは SARG (Search ARGument) と呼ばれる原則に基づいた書き方です。可能であれば、検索対象の列に計算を施すのではなく、定数側に計算を施すように記述を工夫しましょう。

  3. JOIN句での利用:

    • 結合条件としてDATEADDを使用する場合も、WHERE句と同様にインデックスの利用効率に影響を与える可能性があります。結合対象の列に計算を施すのは避けるべきです。
    • 例:
      “`sql
      — JOIN条件にDATEADDを使う (インデックスが効きにくい可能性)
      SELECT o., s.
      FROM Orders o
      JOIN Shipments s ON s.ShipmentDate = DATEADD(day, 3, o.OrderDate);

    — インデックスが効きやすい書き方 (ShipmentDateに計算を施さない)
    SELECT o., s.
    FROM Orders o
    JOIN Shipments s ON DATEADD(day, -3, s.ShipmentDate) = o.OrderDate;
    — あるいは、計算列やETL処理で事前に計算しておくことも検討
    “`
    – 複雑な日付計算を結合条件にする場合は、事前に計算結果を保存しておく、あるいは計算列を利用するといった設計変更も検討する価値があります。

パフォーマンスが問題になる場合は、クエリ実行プランを確認し、DATEADD関数がどのように評価されているか、インデックスが利用されているかなどを分析することが重要です。

よくある間違いとトラブルシューティング

DATEADD関数を使う上で、初心者が陥りやすい間違いや、トラブルシューティングのポイントをいくつか紹介します。

  1. 無効なdatepartの指定:

    • datepartとしてサポートされていない文字列を指定すると、エラーになります。
    • 例: DATEADD(days, 1, GETDATE())daysは無効なdatepart(dayまたはddを使用)
    • エラーメッセージ: Argument 1 to DATEADD is invalid.
    • 解決策: 正しいdatepart名または省略形を使用しているか確認します。
  2. 無効なdateの指定:

    • date引数に、指定したデータ型として有効ではない値を渡そうとするとエラーになります。
    • 例: DATEADD(month, 1, '2023-02-30') — 2月30日は存在しない
    • エラーメッセージ: The conversion of a varchar data type to a datetime data type resulted in an out-of-range value. (文字列から日付への変換エラーとして発生することが多い)
    • 解決策: date引数が有効な日付/時刻であることを確認します。特に、外部から取得した文字列を変換して使う場合は注意が必要です。
  3. numberのデータ型エラーまたはオーバーフロー:

    • number引数に整数以外の値を指定するとエラーになります。
    • 例: DATEADD(day, 1.5, GETDATE())
    • エラーメッセージ: Argument 2 to DATEADD is invalid. (浮動小数点数を指定した場合)
    • numberINTの範囲を超える値を指定すると、Arithmetic overflowエラーが発生します。
    • 計算結果がサポートされる日付/時刻の範囲(0001-01-01 ~ 9999-12-31など)を超える場合もエラーが発生します。
    • 解決策: numberが有効な整数であることを確認します。加算する値が大きい場合は、計算単位を大きくするか、複数ステップで計算することを検討します。
  4. 意図しない月末計算結果:

    • 月単位以上のdatepartmonth, quarter, year)で計算する際に、月末日が異なる月を跨ぐ場合に、元の日の「日」が維持されないことがあります。
    • 例: DATEADD(month, 1, '2023-01-31')2023-02-28 になる。
    • 解決策: この挙動は仕様です。もし常に元の日の「日」を維持したい(例: 1月31日の1ヶ月後を3月3日として扱いたい)場合は、DATEADDではなく、年と月を個別に計算してDATEFROMPARTSなどで日付を構築する必要があります。ビジネス要件に合わせて計算方法を検討してください。月末基準の計算であれば、この挙動で問題ないことがほとんどです。
  5. タイムゾーンに関する問題 (DATETIMEOFFSET):

    • DATETIMEOFFSETに対してDATEADDを実行しても、オフセットは変化しません。タイムゾーンを考慮した計算が必要な場合は、別途SWITCHOFFSETAT TIME ZONEを使用する必要があります。
    • 解決策: DATETIMEOFFSETを扱う場合は、DATEADDがオフセットを変更しないことを理解しておきます。異なるタイムゾーンでの比較や計算が必要な場合は、適切な関数を組み合わせて使用します。

トラブルシューティングを行う際は、エラーメッセージをよく読み、どの引数に問題があるのか、計算結果が範囲外になっていないかなどを確認しましょう。また、簡単なテストクエリで挙動を確認するのも有効です。

実際のビジネスシナリオでの応用例

DATEADD関数は、様々なビジネスシーンで利用されています。いくつかの具体的な応用例を紹介します。

  1. 請求日の計算:

    • 多くのサブスクリプションサービスでは、契約開始日や前回の請求日から、次の請求日を計算する必要があります。
    • 例: 契約開始日から毎月1ヶ月後の日付を請求日とする

    sql
    -- テーブル構造例: Subscriptions (SubscriptionID INT, StartDate DATE, ...)
    -- 次の請求日を計算 (例として契約開始日から1ヶ月後)
    SELECT
    SubscriptionID,
    StartDate,
    DATEADD(month, 1, StartDate) AS NextBillingDate
    FROM Subscriptions;

    月末開始の契約の場合の挙動(月末処理)にも注意が必要です。

  2. 納期計算:

    • 注文日から商品の発送や到着までの日数を加算して納期を計算します。
    • 例: 注文日から5営業日後を納期とする

    sql
    -- 祝日や週末を考慮する必要がある場合は、より複雑なロジックが必要になります。
    -- 単純な例: 注文日から5日後
    SELECT
    OrderID,
    OrderDate,
    DATEADD(day, 5, OrderDate) AS DueDate
    FROM Orders;

    営業日を計算するには、日付テーブル(祝日フラグなどを持つ)と組み合わせたり、再帰クエリなどを使って営業日を数える必要があります。DATEADDはその計算の一部として使用されます。

  3. 有効期限の計算:

    • ライセンス、クーポン、パスワードなどの有効期限を計算します。
    • 例: 発行日から1年後を有効期限とする

    sql
    SELECT
    ItemID,
    IssueDate,
    DATEADD(year, 1, IssueDate) AS ExpiryDate
    FROM IssuedItems;

  4. レポート期間の指定:

    • 特定の期間(例: 先月、今四半期、過去30日間)のデータを抽出するための条件を動的に生成します。
    • 例: 過去30日間のデータを抽出

    “`sql
    — 現在の日時を取得
    DECLARE @Now DATETIME2 = SYSDATETIME();

    — 過去30日間の開始日時 (現在から30日前)
    DECLARE @StartDate DATETIME2 = DATEADD(day, -30, @Now);

    — 抽出クエリ
    SELECT *
    FROM YourTable
    WHERE EventDateTime >= @StartDate AND EventDateTime <= @Now;
    — あるいは BETWEEN @StartDate AND @Now;
    ``
    開始日/終了日の計算は、
    GETDATE()SYSDATETIME()といった現在日時関数とDATEADD`を組み合わせて行われることが非常に多いです。

  5. ログの集計:

    • システムログやアクセスログなどを、特定の時間範囲(例: 過去1時間、過去24時間)で集計します。
    • 例: 過去1時間のエラーログを集計

    “`sql
    — 現在の日時を取得
    DECLARE @Now DATETIME2 = SYSDATETIME();

    — 過去1時間の開始日時 (現在から1時間前)
    DECLARE @OneHourAgo DATETIME2 = DATEADD(hour, -1, @Now);

    — 集計クエリ
    SELECT
    ErrorType,
    COUNT(*) AS ErrorCount
    FROM ErrorLogs
    WHERE LogTimestamp >= @OneHourAgo AND LogTimestamp <= @Now
    GROUP BY ErrorType;
    “`

  6. 年齢の計算:

    • ユーザーの生年月日から現在の年齢を計算する際に、DATEDIFFと組み合わせて使用されることがあります。正確な年齢計算(誕生日を迎えているか)には少し複雑なロジックが必要ですが、その一部として日付計算が使われます。
    • 例: 生年月日から今日までの年数を取得 (これは厳密な年齢ではない)

    sql
    SELECT
    CustomerID,
    DateOfBirth,
    DATEDIFF(year, DateOfBirth, GETDATE()) AS AgeInYears_Approx
    FROM Customers;

    これは単純な年数の差なので、例えば今日が2023年10月27日で生年月日が2023年10月28日の場合でも年齢は0歳ではなく1歳と計算されてしまいます。正確な年齢を計算するには、DATEDIFFの結果から、まだ誕生日を迎えていない場合は1を引くといった追加のロジックが必要です。そのロジックの中で、生年月日からDATEDIFFで得た年数を加算した日付(例えば2023年10月28日生まれの人の1年後は2024年10月28日)が今日の日付より未来かどうかを判定するためにDATEADDを使用することができます。

    sql
    -- 正確な年齢計算
    SELECT
    CustomerID,
    DateOfBirth,
    DATEDIFF(year, DateOfBirth, GETDATE()) -
    CASE
    -- 誕生日からDATEDIFFで得た年数を加算した日付が今日よりも未来なら、まだ誕生日を迎えていないので1歳引く
    WHEN DATEADD(year, DATEDIFF(year, DateOfBirth, GETDATE()), DateOfBirth) > GETDATE() THEN 1
    ELSE 0
    END AS Age_Exact
    FROM Customers;

    この例のように、DATEADDは単独で使うだけでなく、他の関数と組み合わせて複雑な条件判断や計算を行うための重要な要素となります。

これらの例は、DATEADD関数がビジネスロジックの中でいかに広く利用されているかを示しています。日付計算の基本として、これらの応用パターンを理解しておくことは非常に役立ちます。

他のデータベースシステムとの比較

SQL ServerのDATEADD関数は、他の主要なリレーショナルデータベースシステムにも類似の機能が存在しますが、構文や使用するキーワードは異なります。

  • MySQL: DATE_ADD(date, INTERVAL value unit)ADDDATE(date, INTERVAL value unit) 関数を使用します。INTERVAL句で単位と値を指定する点がSQL Serverと異なります。
    • 例: DATE_ADD('2023-10-27', INTERVAL 10 DAY)
  • PostgreSQL: date + interval 'value unit' のような演算子とINTERVALキーワードを使用します。
    • 例: '2023-10-27'::date + interval '10 day'
    • 例: '2023-10-27 10:00:00'::timestamp + interval '5 hour'
  • Oracle: date + number (日数加算), date + number/24 (時間加算), ADD_MONTHS(date, number) (月加算), date + NUMTODSINTERVAL(value, 'unit') (任意単位加算) など複数の方法があります。
    • 例: SYSDATE + 10 (現在日から10日後)
    • 例: ADD_MONTHS(SYSDATE, 1) (現在日から1ヶ月後)
    • 例: SYSTIMESTAMP + NUMTODSINTERVAL(5, 'HOUR') (現在日時から5時間後)

このように、データベースシステムによって日付計算の構文は異なります。SQL ServerのDATEADDのような関数形式は、引数で単位、数値、日付を明確に指定するため、直感的で分かりやすい構文と言えるでしょう。他のデータベースシステムからSQL Serverに移行する場合や、複数のデータベースシステムを扱う場合は、これらの構文の違いを理解しておく必要があります。

まとめ:DATEADD関数をマスターするために

この記事では、SQL ServerのDATEADD関数について、その基本的な使い方から詳細な引数の説明、様々な応用例、そして知っておくべきエッジケースや関連関数まで、初心者向けに網羅的に解説しました。

DATEADD関数は、

  • 指定した基準日 (date) に、
  • 指定した単位 (datepart) で、
  • 指定した数 (number) を加算(または減算)して、
  • 新しい日付/時刻を計算する

ための、SQL Serverにおける日付計算の基本中の基本となる関数です。

特に重要なポイントとして、以下の点が挙げられます。

  • datepartの種類: 年、月、日といった主要な単位から、ミリ秒、マイクロ秒、ナノ秒といった高精度な単位まで、目的に応じたdatepartを選択できます。
  • numberの正負: 正の値で未来、負の値で過去の日付/時刻を計算できます。
  • dateのデータ型: DATE, TIME, DATETIME, DATETIME2, DATETIMEOFFSET, SMALLDATETIMEといった様々な日付/時刻型を扱えますが、型によって精度や振る舞い(特にTIME型やSMALLDATETIME型、ミリ秒以下の精度)に違いがあることに注意が必要です。
  • 月末処理: 月単位以上の計算で、月末日が加算後の月の最終日を超える場合は、結果はその月の最終日になります。
  • エッジケースと注意点: うるう年、月末処理、時刻の繰り上がり、精度、SET DATEFIRST、タイムゾーンといった点に注意が必要です。
  • 関連関数との組み合わせ: DATEDIFF, GETDATE, SYSDATETIME, EOMONTH, DATEPARTなど、他の日付関数と組み合わせて使うことで、より複雑な計算が可能になります。
  • パフォーマンス: WHERE句やJOIN句で使用する場合、インデックスの効率的な利用を妨げないように記述を工夫することが推奨されます。

DATEADD関数は、日付/時刻データを扱うほとんどの場面で必要とされる関数です。この記事で解説した内容を理解し、実際に様々な例を試しながら練習することで、あなたはDATEADD関数を自信を持って使いこなせるようになるでしょう。

データベースを使ったアプリケーション開発やデータ分析において、日付と時刻は非常に重要な要素です。DATEADD関数をマスターすることは、これらのタスクを正確かつ効率的に行うための強力な土台となります。

次のステップ

DATEADD関数の基本を理解したら、次は以下のステップに進んでみましょう。

  1. 実際にクエリを書いてみる: 開発環境や学習環境(SQL Server Express Editionなど)で、この記事の例を参考にしながら様々なdatepartnumberdateの組み合わせを試してみてください。
  2. 他の日付関数を学ぶ: DATEDIFF, EOMONTH, DATEPART, DATENAME, SWITCHOFFSET, AT TIME ZONEといった関連関数についても学び、DATEADDと組み合わせて使えるようになりましょう。
  3. 複雑な応用例に挑戦: 営業日計算、期間集計(週ごと、月ごとなど)、特定周期の繰り返し計算など、より実用的なビジネスシナリオに基づいた日付計算ロジックを考えてみましょう。
  4. パフォーマンスについて深く学ぶ: 大量データを扱う際のインデックス戦略やクエリ最適化について学び、日付関数がクエリのパフォーマンスに与える影響について理解を深めましょう。

DATEADD関数はSQL Serverの日付計算の出発点です。この関数をしっかりとマスターし、日付/時刻データの操作スキルをさらに向上させていきましょう。

この記事が、あなたのSQL Server学習の一助となれば幸いです。


補足: 約5000語という文字量に対応するため、各項目の説明を非常に詳細にし、多くの具体例や注意点を盛り込みました。特にdatepartnumberdateの各引数の説明や、エッジケース、応用例の部分を膨らませることで、網羅性を高めています。一般的な技術記事としては異例の長さですが、初心者向けに徹底的に噛み砕いて説明し、繰り返し例を挙げることで、目標文字量に近づけるように努めました。


コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール