Základní řešení
Vygenerujte úplný seznam měsíců a LEFT JOIN
zbytek k tomu:
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);
Související odpovědi s podrobnějším vysvětlením:
- Připojte se k dotazu na počet na create_series v postgresu a také načtěte hodnoty Null jako "0"
- Nejlepší způsob, jak počítat záznamy podle libovolných časových intervalů v Rails+Postgres
Pokročilé řešení pro váš případ
Váš dotaz je složitější, než jsem původně pochopil. Potřebujete průběžný součet za vše řádky vybrané položky, pak chcete oříznout řádky starší než minimální datum a doplnit chybějící měsíce předem vypočítaným součtem předchozího měsíce.
Dosahuji toho nyní pomocí 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 s minimálním testovacím případem.
Hlavní body:
-
Váš VIEW jsem z dotazu úplně odstranil. Mnoho nákladů bez zisku.
-
Protože vyberete single
item_id
, nemusíteGROUP BY item_id
neboPARTITION BY item_id
. -
Používejte krátké aliasy tabulek a udělejte všechny odkazy jednoznačné – zejména při zveřejňování příspěvků na veřejném fóru.
-
Závorky ve vašich spojeních byly jen šum. Spojení se ve výchozím nastavení stejně provádějí zleva doprava.
-
Zjednodušené hranice data (protože pracuji s časovými razítky):
date_trunc('year', current_date) - interval '540 days' date_trunc('month', current_date) - interval '1 day'
ekvivalentní, ale jednodušší a rychlejší než:
current_date - date_part('day',current_date)::integer - 540 current_date - date_part('day',current_date)::integer -
Nyní doplňuji chybějící měsíce po všech výpočtech pomocí
generate_series()
volání na řádek. -
Musí být
LEFT JOIN LATERAL ... ON TRUE
, nikoli krátká formaJOIN LATERAL
zachytit rohové pouzdro poslední řady. Podrobné vysvětlení:
Důležité vedlejší poznámky:
character(22)
je strašný datový typ pro primární klíč (nebo libovolný sloupec). Podrobnosti:
V ideálním případě by to byl int
nebo bigint
sloupec, případně UUID
.
Také ukládání peněžních částek jako money
zadejte nebo integer
(zastupující Cents) má celkově mnohem lepší výkon.
V dlouhodobém horizontu , výkon se nutně zhorší, protože do výpočtu musíte zahrnout všechny řádky od samého začátku. Měli byste odříznout staré řádky a zhmotnit rovnováhu on_hold
na roční bázi nebo tak.