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


92

このようなクエリがあり、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-07で162の日付が生成され2004-08-16、これが必要です。このコードの問題は、二つの日付が異なる年からあるとき、私はしようとすると、それは例えば、正しい答えを与えていないだろうということです2007-02-012008-04-01

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


回答:


175

intとの間で変換せずに実行できます(ただし、代わりにタイムスタンプとの間で変換します)

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

3
なぜdate_trunc必要なのですか?
Idefixx 2017

2
ただのプレゼンテーションです。これにより、この場合は常にゼロであるタイムスタンプの時間部分の出力が排除されます。
beemtee 2018

73

一連の日付を生成するには、これが最適な方法です。

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

  • ただし、日付リテラルをdate入力パラメーターとしてキャストしても意味がありません。Au contrairetimestampは、最良の選択です。パフォーマンスの利点は小さいですが、それを採用しない理由はありません。そして、あなたは不からの変換と結合されたDST(サマータイム)のルール伴わないdatetimestamp with time zoneして背中を。下記参照。

同等の、あまり明確ではない短い構文:

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

または、SELECTリスト内のset-returning関数を使用します。

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

ASキーワードがされて必要な最後の変形では、Postgresは列の別名を誤って解釈するだろうdayそう。そして、Postgres 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で追加されました。)関連するものは、太字のテイクとリターンtimestamp/の最後の2つtimestamptzです。

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

timestamp '2004-03-07'ところで、完全に有効です。省略された時間部分は、デフォルト00:00でISO形式になります。

関数型の解決のおかげで、まだ渡すことができますdate。しかし、それにはPostgresからのより多くの作業が必要です。ある暗黙のキャストからdatetimestampなどから1dateまではtimestamptz。あいまいになるが、timestamptzされる「好ましい」、「日付/時刻型」の中。したがって、一致はステップ4dで決定されます。

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

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

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

db <>フィドルはこちら

関連:


7
さらに短いバージョン:SELECT generate_series(timestamp '2004-03-07', '2004-08-16', '1 day') :: DATE AS day;
ヴァーツラフKužel

t(day)構文は何を意味しますか?
ルンダン

@rendang:AS t(day)SELECT * FROM func() AS t(day)のテーブルと列の別名です。ASこのコンテキストでは、キーワードはオプションのノイズです。参照:stackoverflow.com/a/20230716/939860
Erwin Brandstetter

35

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

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

タイムゾーンによっては、予期しない結果が返される場合があります。私はこの問題を抱えていました。代わりにタイムスタンプを使用してください。SETセッションタイムゾーン 'America / Sao_Paulo' SELECT d :: date FROM generate_series( '2019-11-01' :: date、 '2019-11-03' :: date、 '1 day')d SELECT d :: date generate_series( '2019年11月1日' ::日、 '2019年11月4日' ::日、 '1日')Dからの
パルハレス

1

これも使えます。

select generate_series  ( '2012-12-31'::timestamp , '2018-10-31'::timestamp , '1 day'::interval) :: date 
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.