web-dev-qa-db-ja.com

PostgreSQLでの2つの日付間の時系列の生成

2つの指定された日付の間に一連の日付をうまく生成する次のようなクエリがあります。

select date '2004-03-07' + j - i as AllDate 
from generate_series(0, extract(doy from date '2004-03-07')::int - 1) as i,
     generate_series(0, extract(doy from date '2004-08-16')::int - 1) as j

2004-03-072004-08-16の間の162個の日付を生成しますが、これは私が望むものです。このコードの問題は、2つの日付が異なる年のものである場合、たとえば2007-02-012008-04-01を試す場合、正しい答えが得られないことです。

より良い解決策はありますか?

68
f.ashouri

Intへ/から変換せずに実行できます(代わりにタイムスタンプへ/から変換します)

SELECT date_trunc('day', dd):: date
FROM generate_series
        ( '2007-02-01'::timestamp 
        , '2008-04-01'::timestamp
        , '1 day'::interval) dd
        ;
121
wildplasser

一連のdatesを生成するには、これがoptimal方法:

_SELECT t.day::date 
FROM   generate_series(timestamp '2004-03-07'
                     , timestamp '2004-08-16'
                     , interval  '1 day') AS t(day);
_
  • 追加のdate_trunc()は必要ありません。 date(_day::date_)へのキャストは暗黙的にそれを行います。

  • ただし、日付リテラルを入力パラメーターとしてdateにキャストしても意味がありません。逆に、timestampが最良の選択です。パフォーマンス上の利点はわずかですが、それを採用しない理由はありません。また、dateから_timestamp with time zone_への変換およびその逆の変換と組み合わせたDST(夏時間)ルールを不必要に使用する必要はありません。下記参照。

同等の、より明示的ではない短い構文:

_SELECT day::date 
FROM   generate_series(timestamp '2004-03-07', '2004-08-16', '1 day') day;
_

または、SELECTリストの集合を返す関数を使用して:

_SELECT generate_series(timestamp '2004-03-07', '2004-08-16', '1 day')::date AS day;
_

ASキーワードは、最後のバリアントではrequiredです。Postgresは、そうでない場合、カラムエイリアスdayを誤って解釈します。そして、私はnotPostgres 10の前にそのバリアントをアドバイスします-少なくとも同じSELECTリスト内の複数の集合を返す関数ではありません:

(それを除けば、最後のバリアントは通常わずかなマージンで最速です。)

なぜ_timestamp [without time zone]_ですか?

generate_series() のオーバーロードされたバリアントが多数あります。現在(Postgres 11):

_SELECT oid::regprocedure   AS function_signature
     , prorettype::regtype AS return_type
FROM   pg_proc
where  proname = 'generate_series';
_
 function_signature | return_type 
:------------------------------------------- ------------------------------------- | :-------------------------- 
 generate_series(integer、integer、integer)|整数
 generate_series(integer、integer)|整数
 generate_series(bigint、bigint、bigint)| bigint 
 generate_series(bigint、bigint)| bigint 
 generate_series(numeric、numeric、numeric)|数値
 generate_series(numeric、numeric)|数値
generate_series(タイムゾーンなしのタイムスタンプ、タイムゾーンなしのタイムスタンプ、間隔)|タイムゾーンなしのタイムスタンプ
 generate_series(タイムゾーン付きタイムスタンプ、タイムゾーン付きタイムスタンプ、間隔)|タイムゾーン付きのタイムスタンプ

numericバリアントはPostgres 9.5で追加されました。)関連するものは、最後の2つの太字で、timestamp/timestamptzを取得して返します。

dateを使用または返すバリアントはありません。 dateを返すには、明示的なキャストが必要です。 timestamp引数を使用した呼び出しは、関数型解決規則に陥ることなく、入力への追加キャストなしで、最適なバリアントに直接解決されます。

_timestamp '2004-03-07'_は完全に有効です。省略された時間部分は、ISO形式の_00:00_にデフォルト設定されます。

関数タイプ解決 のおかげで、dateを渡すことができます。しかし、それにはPostgresからのさらなる作業が必要です。 implicitcast from date to timestampだけでなく、dateからもあります。 timestamptzに。あいまいになりますが、timestamptzは「日付/時刻型」の中で "preferred" です。 一致はステップ4d。 で決定されます:

すべての候補を実行し、型変換が必要なほとんどの位置で(入力データ型の型カテゴリの)優先型を受け入れる候補を保持します。優先タイプを受け入れるものがない場合、すべての候補を保持します。候補が1つだけの場合は、それを使用します。それ以外の場合は、次の手順に進みます。

関数型解決での余分な作業に加えて、これによりtimestamptzに余分なキャストが追加されます。これにより、コストが増加するだけでなく、まれにDSTで問題が発生して予期しない結果が生じる可能性があります。 (DSTはモロニックな概念ですが、これは十分に強調することはできません。)関連:

より高価なクエリプランを示すデモをフィドルに追加しました。

db <> fiddle here

関連する:

48

日付を使用してシリーズを直接生成できます。 intやタイムスタンプを使用する必要はありません:

select date::date 
from generate_series(
  '2004-03-07'::date,
  '2004-08-16'::date,
  '1 day'::interval
) date;
33
fbonetti

これも使用できます。

select generate_series  ( '2012-12-31'::timestamp , '2018-10-31'::timestamp , '1 day'::interval) :: date 
0
Meyyappan