Postgresql

如何在 PostgreSQL 中生成日期系列?

  • November 9, 2017

如果您要生成時間序列,請參閱此問題

假設我想在兩個日期之間生成一系列日期。我看到該功能generate_series僅提供

Function                                    Argument Type                         Return Type                                                               Description
generate_series(start, stop, step interval) timestamp or timestamp with time zone setof timestamp or setof timestamp with time zone (same as argument type) Generate a series of values, from start to stop with a step size of step

那麼我該怎麼做呢?

您可以使用generate_series它,但**請確保將參數顯式轉換為“沒有時區的時間戳”,**否則它們將預設為“帶有時區的時間戳”。兩個輸入的PostgreSQL 重載generate_series

問題timestamp with timezone

你可以在這裡看到缺點。

SET timezone = 'America/Santiago';
SELECT generate_series(date '2016-08-15', date '2016-08-15', '1 day');    
SELECT generate_series(date '2016-08-14', date '2016-08-15', '1 day');

以上兩者都返回相同的天數。你可以在這裡再次看到它。

SET timezone = 'America/Sao_Paulo';
SELECT generate_series(date '2016-10-16', date '2016-10-17', '1 day');
SELECT generate_series(date '2016-10-17', date '2016-10-17', '1 day');

上面顯示了一天的兩個範圍。

這種行為的原因是這些時區在午夜有其“DST 邊界,而不是在凌晨時更合理的時間”

那麼“做對了”是什麼樣子的,

SELECT generate_series(
 timestamp without time zone '2016-10-16',
 timestamp without time zone '2016-10-17',
 '1 day'
);

現在你可以投射到日期..

SELECT d::date
FROM generate_series(
 timestamp without time zone '2016-10-16',
 timestamp without time zone '2016-10-17',
 '1 day'
) AS gs(d);

這個問題和答案的靈感來自與 RhodiumToad 在 IRC (irc://irc.freenode.net/#postgresql) 上的對話。他改變了我這個問題並提供了解決方案。

更新:兩個潛在的修復

選項1:generate_series(date,date,interval)

玩弄,我發現我也許可以節省timestamp without time zone通過重載顯式轉換為的需要generate_series(date,date,interval)

這是我的功能,

CREATE FUNCTION generate_series( t1 date, t2 date, i interval )
RETURNS setof date
AS $$
 SELECT d::date
 FROM generate_series(
   t1::timestamp without time zone,
   t2::timestamp without time zone,
   i
 )
   AS gs(d)
$$
LANGUAGE sql
IMMUTABLE;

現在我可以重新執行上面的測試案例,它不再是可疑的了。這兩個都返回相同的東西,

SET timezone = 'America/Santiago';
SELECT d::date
FROM generate_series(date '2016-08-15', date '2016-08-15', '1 day')
 AS gs(d);

SELECT d::date
FROM generate_series(
 timestamp without time zone '2016-08-15',
 timestamp without time zone '2016-08-15',
 '1 day'
)
 AS gs(d);

就像這兩個一樣,

SELECT d::date
FROM generate_series(date '2016-08-14', date '2016-08-15', '1 day')
 AS gs(d);

SELECT d::date
FROM generate_series(
 timestamp without time zone '2016-08-14',
 timestamp without time zone '2016-08-15',
 '1 day'
)
 AS gs(d);

選項 2:generate_series(date,date,int)

另一種選擇是創建一個新功能,generate_series(date,date,int)由於此處提到的原因,您不能同時擁有這兩個功能。所以選擇其中之一,

generate_series(date,date,interval)
generate_series(date,date,int)

如果你想要第二個選項,試試這個:

CREATE FUNCTION generate_series( t1 date, t2 date, i int )
RETURNS setof date
AS $$
 SELECT d::date
 FROM generate_series(
   t1::timestamp without time zone,
   t2::timestamp without time zone,
   i * interval '1 day'
 )
   AS gs(d)
$$
LANGUAGE sql
IMMUTABLE;

注意事項

通過對irc的審查,這些想法存在一些問題,

<johto> generate_series(date, date, unknown)今天已經可以使用了。當您不使用 int 版本(例如generate_series(date, date, '1 day'))徹底打破它時,您將返回類型從 timestamptz 更改為 date。(date, date, interval)會破壞更少的情況,但您仍然會更改輸出類型。(date, date, '1 hour') (目前“正常”的情況應該發生什麼也不清楚 )

引用自:https://dba.stackexchange.com/questions/175963