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
と2004-08-16
の間の162個の日付を生成しますが、これは私が望むものです。このコードの問題は、2つの日付が異なる年のものである場合、たとえば2007-02-01
と2008-04-01
を試す場合、正しい答えが得られないことです。
より良い解決策はありますか?
Intへ/から変換せずに実行できます(代わりにタイムスタンプへ/から変換します)
SELECT date_trunc('day', dd):: date
FROM generate_series
( '2007-02-01'::timestamp
, '2008-04-01'::timestamp
, '1 day'::interval) dd
;
一連の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
関連する:
日付を使用してシリーズを直接生成できます。 intやタイムスタンプを使用する必要はありません:
select date::date
from generate_series(
'2004-03-07'::date,
'2004-08-16'::date,
'1 day'::interval
) date;
これも使用できます。
select generate_series ( '2012-12-31'::timestamp , '2018-10-31'::timestamp , '1 day'::interval) :: date