HOWTO · PostgreSQL

PostgreSQL での DATEADD() の代替

この記事では、PostgreSQL の DATEADD() に代わる方法について説明します。

このページの内容

PostgreSQLでは、SQL ServerのDATEADD()構文は使いません。日数を整数で指定する場合は、manufacture_date + expires_inのように、その整数をdateに加算します。月や年などの暦単位には、たとえばdate '2022-08-24' + INTERVAL '2 months'のようにINTERVALを使います。前者の結果はdateですが、日付にintervalを加算するとタイムゾーンなしのtimestampになります。

SQLのDATEADD()関数

製品テーブルには、製造日と、期限までの日数を整数で保存することがあります。たとえば、1行にmanufacture_date = DATE '2022-08-24'とexpires_in = 10が含まれているとします。この2つの値から有効期限を計算するのが目的です。

この計算には、SQL Serverで一般的にDATEADD()が使われます。SQL Serverでの書き方はDATEADD(day, 10, '2022-08-24')です。第1引数は日付の単位、第2引数は加算する量、第3引数は開始日を指定します。これはSQL Serverから来た読者向けの比較例であり、PostgreSQLのクエリではありません。PostgreSQLでは、演算子とintervalを使って日付を計算します。

「日数を加える」ことと「暦上の1か月を加える」ことは異なるため、この区別が重要です。日数は一定の暦日数ですが、1か月の日数は月によって異なります。そのため、月を固定の日数に換算すると、暦上の日付が正しくならないことがあります。必要な単位に合ったPostgreSQLの方法を選びましょう。

PostgreSQLでDATEADD()の代わりに+演算子と-演算子を使う

日数をずらす場合、PostgreSQLでは整数をdateに直接加算できます。DATE '2022-08-24' + 10は、10日後の日付を計算します。日数を減らすにはDATE '2022-09-03' - 10とするか、DATE '2022-09-03' + (-10)のように負の整数を加算します。いずれも日付の式であり、結果はdateのままです。時刻は加わりません。

この簡潔な方法は、expires_inのような整数列を含め、日数で表される加算量に便利です。たとえば、manufacture_date + expires_inで製品の各行について有効期限を計算できます。保存された値が日数を表していることを確認してください。整数を日付に加算しても、月や年の加算にはなりません。加算量をパラメーターで渡す場合は、整数演算に対応した型を使うか、適切な場合に整数へキャストしてください。

次のPython例では、同じ製造日と保存された日数を使って有効期限を計算します。

from datetime import date, timedelta

manufacture_date = date(2022, 8, 24)
expires_in = 10
expiration_date = manufacture_date + timedelta(days=expires_in)
print(expiration_date.isoformat())
2022-09-03

このPython例は日付計算を説明するもので、PostgreSQLのSQLを実行するものではありません。PostgreSQLのクエリでは、上記の日付式を使います。

計算で暦上の月、年、週、または複数単位の組み合わせを表す場合は、intervalを使います。式に意図した単位を明示でき、すべての月が一定の日数であるかのように扱わずに済みます。

PostgreSQLでDATEADD()の代わりにINTERVALデータ型を使う

INTERVALは時間の長さを表します。暦に基づく計算では、日付にintervalを加算します。例として、DATE '2022-08-24' + INTERVAL '10 days'、DATE '2022-08-24' + INTERVAL '2 months'、DATE '2022-08-24' + INTERVAL '1 year'があります。DATE '2022-08-24' + INTERVAL '1 week'のように、週の単位も使えます。PostgreSQLのintervalリテラルでは、単位名を単数形でも複数形でも指定できます。

INTERVAL '1 week 2 days'のように、複数の単位を組み合わせることもできます。加算量が複数の暦要素から成る場合に便利です。intervalには時間、分、秒も含められます。dateにintervalを加算すると、PostgreSQLはタイムゾーンなしのtimestampを返します。そのため、通常は午前0時の時刻部分も表示されます。その後にアプリケーションで日付が必要なら、結果を明示的にdateへキャストしてください。

暦単位は、日数に固定換算するのではなく、暦に従って計算されます。月末に近い日付に1か月を加えると、翌月の最終日に当たることがあります。そのため、固定の日数を加えた場合とは結果が異なることがあります。実際のデータに近い日付で式を確認し、業務上のルールに応じて月または日を選んでください。

製品の例では、保存された日数に1日分のintervalを掛けます。manufacture_date + expires_in * INTERVAL '1 day'とすれば、行ごとに計算できます。式にintervalが含まれるため、結果はtimestampです。結果に日付型が必要なら、(manufacture_date + expires_in * INTERVAL '1 day')::dateを使います。保存値が月数なら、同じパターンでINTERVAL '1 month'を使うなど、列の意味に合った単位を指定してください。

この記事のPostgreSQL例はSQL式として示していますが、設定されている記事の実行環境ではPostgreSQLの文を実行しません。未実行のSQL例として扱い、お使いのPostgreSQLデータベースで実行してください。DATEADD()はSQL Serverでの同等機能を説明するためだけに登場します。PostgreSQLのクエリでは+、-、INTERVALを使ってください。