如何在 PostgreSQL 中生成日期系列?
假設我想在兩個日期之間生成一系列日期。我看到該功能
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')
(目前“正常”的情況應該發生什麼也不清楚 )