Solusi dasar
Buat daftar lengkap bulan dan LEFT JOIN
sisanya:
SELECT *
FROM (
SELECT to_char(m, 'YYYY-MON') AS yyyymmm
FROM generate_series(<start_date>, <end_date>, interval '1 month') m
) m
LEFT JOIN ( <your query here> ) q USING (yyyymmm);
Jawaban terkait dengan penjelasan lebih lanjut:
- Bergabunglah dengan kueri penghitungan pada generate_series di postgres dan juga ambil nilai Null sebagai "0"
- Cara terbaik untuk menghitung catatan dengan interval waktu sewenang-wenang di Rails+Postgres
Solusi lanjutan untuk kasus Anda
Permintaan Anda lebih rumit daripada yang pertama kali saya pahami. Anda membutuhkan jumlah berjalan di atas semua baris item yang dipilih, lalu Anda ingin memangkas baris yang lebih lama dari tanggal minimum, dan mengisi bulan yang hilang dengan jumlah yang telah dihitung sebelumnya dari bulan sebelumnya.
Saya mencapai ini sekarang dengan LEFT JOIN LATERAL
.
SELECT COALESCE(m.yearmonth, c.yearmonth)::date, sold_qty, on_hand
FROM (
SELECT yearmonth
, COALESCE(sold_qty, 0) AS sold_qty
, sum(on_hand_mon) OVER (ORDER BY yearmonth) AS on_hand
, lead(yearmonth) OVER (ORDER BY yearmonth)
- interval '1 month' AS nextmonth
FROM (
SELECT date_trunc('month', c.change_date) AS yearmonth
, sum(c.sold_qty / s.qty)::numeric(18,2) AS sold_qty
, sum(c.on_hand) AS on_hand_mon
FROM item_change c
LEFT JOIN item i USING (item_id)
LEFT JOIN item_size s ON s.item_id = i.item_id AND s.name = i.sell_size
LEFT JOIN item_plu p ON p.item_id = i.item_id AND p.seq_num = 0
WHERE c.change_date < date_trunc('month', now()) - interval '1 day'
AND c.item_id = (SELECT item_id FROM item_plu WHERE number = '51515')
GROUP BY 1
) sub
) c
LEFT JOIN LATERAL generate_series(c.yearmonth
, c.nextmonth
, interval '1 month') m(yearmonth) ON TRUE
WHERE c.yearmonth > date_trunc('year', now()) - interval '540 days'
ORDER BY COALESCE(m.yearmonth, c.yearmonth);
SQL Fiddle dengan kasus uji minimum.
Poin utama:
-
Saya menghapus VIEW Anda dari kueri sepenuhnya. Banyak biaya tanpa keuntungan.
-
Karena Anda memilih tunggal
item_id
, Anda tidak perluGROUP BY item_id
atauPARTITION BY item_id
. -
Gunakan alias tabel pendek dan buat semua referensi tidak ambigu - terutama saat memposting di forum publik.
-
Tanda kurung di gabungan Anda hanyalah kebisingan. Gabungan tetap dijalankan dari kiri ke kanan secara default.
-
Batas tanggal yang disederhanakan (karena saya beroperasi dengan cap waktu):
date_trunc('year', current_date) - interval '540 days' date_trunc('month', current_date) - interval '1 day'
setara, tetapi lebih sederhana &lebih cepat dari:
current_date - date_part('day',current_date)::integer - 540 current_date - date_part('day',current_date)::integer -
Saya sekarang mengisi bulan yang hilang setelah semua perhitungan dengan
generate_series()
panggilan per baris. -
Harus
LEFT JOIN LATERAL ... ON TRUE
, bukan bentuk pendek dariJOIN LATERAL
untuk menangkap kasus sudut dari baris terakhir. Penjelasan detail:
Catatan sampingan penting:
character(22)
adalah mengerikan tipe data untuk kunci utama (atau apa saja kolom). Detail:
Idealnya ini adalah int
atau bigint
kolom, atau mungkin UUID
.
Juga, menyimpan jumlah uang sebagai money
ketik atau integer
(mewakili Cents) berkinerja jauh lebih baik secara keseluruhan.
Dalam jangka panjang , kinerjanya pasti akan menurun, karena Anda harus memasukkan semua baris sejak awal dalam perhitungan Anda. Anda harus memotong baris lama dan mewujudkan keseimbangan on_hold
setiap tahun atau semacamnya.