Giải pháp cơ bản
Tạo danh sách đầy đủ các tháng và LEFT JOIN
phần còn lại cho nó:
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);
Các câu trả lời liên quan có giải thích thêm:
- Tham gia truy vấn đếm trên create_series trong postgres và cũng truy xuất giá trị Null là" 0 "
- Cách tốt nhất để đếm bản ghi theo khoảng thời gian tùy ý trong Rails + Postgres
Giải pháp nâng cao cho trường hợp của bạn
Truy vấn của bạn phức tạp hơn tôi hiểu lần đầu. Bạn cần số tiền đang chạy trên tất cả các hàng của mục đã chọn, sau đó bạn muốn cắt các hàng cũ hơn một ngày tối thiểu và điền vào các tháng còn thiếu bằng tổng được tính trước của tháng trước.
Tôi đạt được điều này ngay bây giờ với 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 với một trường hợp thử nghiệm tối thiểu.
Những điểm chính:
-
Tôi đã xóa hoàn toàn CHẾ ĐỘ XEM của bạn khỏi truy vấn. Nhiều chi phí mà không đạt được.
-
Vì bạn chọn một đơn
item_id
, bạn không cần phảiGROUP BY item_id
hoặcPARTITION BY item_id
. -
Sử dụng bí danh bảng ngắn gọn và làm rõ ràng tất cả các tham chiếu - đặc biệt là khi đăng trên diễn đàn công khai.
-
Dấu ngoặc đơn trong các phép nối của bạn chỉ là tiếng ồn. Các phép nối vẫn được thực thi từ trái sang phải theo mặc định.
-
Giới hạn ngày được đơn giản hóa (vì tôi hoạt động với dấu thời gian):
date_trunc('year', current_date) - interval '540 days' date_trunc('month', current_date) - interval '1 day'
tương đương, nhưng đơn giản và nhanh hơn:
current_date - date_part('day',current_date)::integer - 540 current_date - date_part('day',current_date)::integer -
Bây giờ tôi điền vào các tháng còn thiếu sau tất cả các phép tính với
generate_series()
cuộc gọi trên mỗi hàng. -
Nó phải là
LEFT JOIN LATERAL ... ON TRUE
, không phải dạng rút gọn củaJOIN LATERAL
để bắt trường hợp góc của hàng cuối cùng. Giải thích chi tiết:
Ghi chú quan trọng bên lề:
character(22)
là một khủng khiếp kiểu dữ liệu cho khóa chính (hoặc bất kỳ cột). Chi tiết:
Tốt nhất đây sẽ là một int
hoặc bigint
hoặc có thể là UUID
.
Ngoài ra, lưu trữ số tiền dưới dạng money
nhập hoặc integer
(đại diện cho Cents) nói chung hoạt động tốt hơn nhiều.
Về lâu dài , hiệu suất chắc chắn sẽ giảm đi, vì bạn phải bao gồm tất cả các hàng ngay từ đầu trong tính toán của mình. Bạn nên cắt bỏ các hàng cũ và hiện thực hóa số dư của on_hold
trên cơ sở hàng năm hoặc một cái gì đó.