Saya memiliki kueri seperti ini yang dengan baik menghasilkan serangkaian tanggal antara 2 tanggal tertentu:
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
Ini menghasilkan 162 tanggal antara 2004-03-07
dan 2004-08-16
dan ini yang saya inginkan. Masalah dengan kode ini adalah tidak akan memberikan jawaban yang benar jika kedua tanggal tersebut berasal dari tahun yang berbeda, misalnya ketika saya mencoba 2007-02-01
dan 2008-04-01
.
Apakah ada solusi yang lebih baik?
postgresql
date
time-series
postgresql-9.1
generate-series
f. asouri
sumber
sumber
Jawaban:
Dapat dilakukan tanpa konversi ke / dari int (tetapi ke / dari stempel waktu sebagai gantinya)
SELECT date_trunc('day', dd):: date FROM generate_series ( '2007-02-01'::timestamp , '2008-04-01'::timestamp , '1 day'::interval) dd ;
sumber
date_trunc
dibutuhkan?Untuk menghasilkan rangkaian tanggal ini adalah cara yang optimal :
SELECT t.day::date FROM generate_series(timestamp '2004-03-07' , timestamp '2004-08-16' , interval '1 day') AS t(day);
Tambahan
date_trunc()
tidak diperlukan. Cast todate
(day::date
) melakukan itu secara implisit.Tetapi tidak ada gunanya mentransmisikan literal tanggal
date
sebagai parameter masukan. Au contraire,timestamp
adalah pilihan terbaik . Keuntungan dalam performa memang kecil, tetapi tidak ada alasan untuk tidak mengambilnya. Dan Anda tidak perlu melibatkan aturan DST (waktu musim panas) yang digabungkan dengan konversi daridate
ketimestamp with time zone
dan ke belakang. Lihat di bawah.Sintaks pendek yang setara dan kurang eksplisit:
SELECT day::date FROM generate_series(timestamp '2004-03-07', '2004-08-16', '1 day') day;
Atau dengan fungsi set-return dalam
SELECT
daftar:SELECT generate_series(timestamp '2004-03-07', '2004-08-16', '1 day')::date AS day;
Kata
AS
kunci diperlukan dalam varian terakhir, Postgres akan salah menafsirkan alias kolomday
sebaliknya. Dan saya tidak akan menyarankan varian itu sebelum Postgres 10 - setidaknya tidak dengan lebih dari satu fungsi set-return dalamSELECT
daftar yang sama :(Selain itu, varian terakhir biasanya tercepat dengan selisih kecil.)
Kenapa
timestamp [without time zone]
?Ada sejumlah varian yang kelebihan muatan
generate_series()
. Saat ini (Postgres 11):(
numeric
varian ditambahkan dengan Postgres 9.5.) Yang relevan adalah dua yang terakhir dalam pengambilan dan pengembaliantimestamp
/timestamptz
.Tidak ada varian yang mengambil atau kembali
date
. Pemeran eksplisit diperlukan untuk kembalidate
. Panggilan dengantimestamp
argumen menyelesaikan ke varian terbaik secara langsung tanpa turun ke aturan resolusi jenis fungsi dan tanpa cast tambahan untuk input.timestamp '2004-03-07'
sangat valid, btw. Bagian waktu yang dihilangkan secara default00:00
dengan format ISO.Berkat fungsi jenis resolusi kita masih bisa lewat
date
. Tapi itu membutuhkan lebih banyak pekerjaan dari Postgres. Ada pemeran implisit daridate
ketimestamp
serta daridate
ketimestamptz
. Akan ambigu, tetapitimestamptz
adalah "disukai" antara "jenis tanggal / waktu". Jadi pertandingan diputuskan pada langkah 4d. :Selain pekerjaan ekstra dalam resolusi jenis fungsi, ini menambahkan pemeran tambahan
timestamptz
- yang tidak hanya menambah biaya, tetapi juga dapat menyebabkan masalah dengan DST yang mengarah ke hasil yang tidak terduga dalam kasus yang jarang terjadi. (DST adalah konsep tolol, btw, tidak bisa cukup menekankan hal ini.)Saya menambahkan demo ke biola yang menunjukkan paket kueri yang lebih mahal:
db <> biola di sini
Terkait:
sumber
SELECT generate_series(timestamp '2004-03-07', '2004-08-16', '1 day') :: DATE AS day;
AS t(day)
dalamSELECT * FROM func() AS t(day)
alias tabel dan kolom. KataAS
kunci adalah kebisingan opsional dalam konteks ini. Lihat: stackoverflow.com/a/20230716/939860Anda dapat membuat serial langsung dengan tanggal. Tidak perlu menggunakan int atau cap waktu:
select date::date from generate_series( '2004-03-07'::date, '2004-08-16'::date, '1 day'::interval ) date;
sumber
Anda juga bisa menggunakan ini.
select generate_series ( '2012-12-31'::timestamp , '2018-10-31'::timestamp , '1 day'::interval) :: date
sumber