PostgreSQL'de iki tarih arasında zaman serisi oluşturma


92

Verilen 2 tarih arasında güzelce bir tarih dizisi oluşturan böyle bir sorgum var:

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

Bu arasına 162 tarihleri üretir 2004-03-07ve 2004-08-16bu benim istediğim. Bu kodla ilgili sorun, iki tarih farklı yıllardan olduğunda, örneğin 2007-02-01ve denediğimde doğru cevabı vermemesidir 2008-04-01.

Daha iyi bir çözüm var mı?


Yanıtlar:


175

İnt'e / int'e dönüştürme olmadan yapılabilir (ancak bunun yerine zaman damgasına / zaman damgasından)

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

3
neden date_truncgerekli?
Idefixx

2
Bu sadece sunum. Bu durumda her zaman sıfır olan zaman damgasının zaman kısmının yazdırılmasını ortadan kaldırır.
beemtee

73

Bir dizi tarih oluşturmak için bu en uygun yoldur:

SELECT t.day::date 
FROM   generate_series(timestamp '2004-03-07'
                     , timestamp '2004-08-16'
                     , interval  '1 day') AS t(day);
  • Ek date_trunc()gerekli değildir. İçin dökme date( day::date) örtülü olarak bunu yapmaz.

  • Ancak tarih değişmezlerini dategirdi parametresi olarak dönüştürmenin de bir anlamı yoktur . Au contraire, timestampen iyi seçimdir . Performanstaki avantaj küçüktür, ancak onu almamak için hiçbir neden yoktur. Ve DST (gün ışığından yararlanma saati) kurallarını, datearasında timestamp with time zoneve geri dönüştürmeyle birlikte gereksiz yere dahil etmezsiniz . Aşağıya bakınız.

Eşdeğer, daha az açık kısa sözdizimi:

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

Veya SELECTlistedeki küme döndürme işlevi ile :

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

ASAnahtar kelime olup gerekli son varyantta Postgrees sütun diğer adı yanlış anlaşılabilir olurdu dayaksi. Ve olur değil Postgres 10 önce o varyantı tavsiye - en azından değil aynı birden fazla set dönen fonksiyonu ile SELECTlistenin:

(Bu bir yana, son değişken genellikle küçük bir farkla en hızlıdır.)

Neden timestamp [without time zone]?

Çok sayıda aşırı yüklenmiş varyantları vardır generate_series(). Şu anda (Postgres 11):

SELECT oid::regprocedure   AS function_signature
     , prorettype::regtype AS return_type
FROM   pg_proc
where  proname = 'generate_series';
function_signature | dönüş_türü                
: ------------------------------------------------- ------------------------------- | : --------------------------
generate_series (tamsayı, tamsayı, tamsayı) | tamsayı                    
generate_series (tamsayı, tamsayı) | tamsayı                    
create_series (bigint, bigint, bigint) | Bigint                     
create_series (bigint, bigint) | Bigint                     
üretme_serisi (sayısal, sayısal, sayısal) | sayısal                    
create_series (sayısal, sayısal) | sayısal                    
generate_series (saat dilimi olmadan zaman damgası, saat dilimi olmadan zaman damgası, aralık) | saat dilimi olmayan zaman damgası
generate_series (saat dilimiyle zaman damgası, saat dilimiyle zaman damgası, aralık) | saat dilimli zaman damgası

( numericVaryantlar Postgres 9.5 ile eklenmiştir.) İlgili olanlar son iki uydusu kalın alma ve geri dönen timestamp/ ' timestamptz.

Orada alarak veya dönen hiçbir varyantıdate . Geri dönmek için açık bir kadroya ihtiyaç vardır date. Bağımsız timestampdeğişkenlerle yapılan çağrı, işlev türü çözümleme kurallarına inmeden ve girdi için ek atama yapmadan doğrudan en iyi değişkeni çözer.

timestamp '2004-03-07'tamamen geçerlidir, btw. İhmal edilen zaman bölümü varsayılan olarak 00:00ISO formatındadır.

Fonksiyon tipi çözünürlük sayesinde hala geçebiliriz date. Ancak bu, Postgres'ten daha fazla çalışma gerektirir. Bir yoktur örtülü döküm gelen dateetmek timestamphem de gelen bir dateetmek timestamptz. Belirsiz olurdu, fakat timestamptzedilir "tercih edilen" "tarih / saat türleri" arasında. Böylece maç 4d adımında kararlaştırılır . :

Tüm adayları çalıştırın ve tercih edilen türleri (giriş veri türünün tür kategorisinin) kabul edenleri, tür dönüşümünün gerekli olacağı çoğu konumda tutun. Hiçbiri tercih edilen türleri kabul etmiyorsa tüm adayları saklayın. Yalnızca bir aday kalırsa, onu kullanın; yoksa sonraki adıma geçin.

Fonksiyon tipi çözümlemede ekstra çalışmaya ek olarak, bu, fazladan bir döküm sağlar timestamptz- bu sadece daha fazla maliyet eklemekle kalmaz, aynı zamanda nadir durumlarda beklenmedik sonuçlara yol açan DST ile ilgili sorunlara da yol açabilir. (DST aptalca bir kavramdır, btw, bunu yeterince vurgulayamaz.) İlgili:

Daha pahalı sorgu planını gösteren kemanlara demolar ekledim:

db <> burada fiddle

İlişkili:


7
Daha da kısa versiyon:SELECT generate_series(timestamp '2004-03-07', '2004-08-16', '1 day') :: DATE AS day;
Václav Kužel

T (gün) sözdizimi neyi ifade eder?
03'te rendang

@rendang: AS t(day)in SELECT * FROM func() AS t(day)tablo ve sütun takma adlarıdır . ASAnahtar kelime, bu bağlamda isteğe gürültüdür. Bakınız: stackoverflow.com/a/20230716/939860
Erwin Brandstetter

35

Doğrudan tarihlerle seri oluşturabilirsiniz. Giriş veya zaman damgası kullanmaya gerek yok:

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

Saat diliminize bağlı olarak, bu beklenmedik bir sonuç verebilir. Bu sorunu yaşadım. Bunun yerine zaman damgasını kullanın. SET oturum TIME zone 'America / Sao_Paulo' SELECT d :: date FROM generate_series ('2019-11-01' :: date, '2019-11-03' :: date, '1 day') d SELECT d :: date FROM generate_series ('2019-11-01' :: date, '2019-11-04' :: date, '1 day') d
palhares

1

Bunu da kullanabilirsiniz.

select generate_series  ( '2012-12-31'::timestamp , '2018-10-31'::timestamp , '1 day'::interval) :: date 
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.