Grundläggande lösning
Skapa en komplett lista över månader och LEFT JOIN
resten till det:
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);
Relaterade svar med mer förklaring:
- Gå med i en räkningsfråga på en generera_serie i postgres och hämta även null-värden som "0"
- Bästa sättet att räkna poster efter godtyckliga tidsintervall i Rails+Postgres
Avancerad lösning för ditt fall
Din fråga är mer komplicerad än jag först förstod. Du behöver den löpande summan över alla rader av det valda objektet, då vill du trimma rader äldre än ett minimidatum och fylla i saknade månader med den förberäknade summan av föregående månad.
Jag uppnår detta nu med 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 med ett minimum av testfall.
Huvudpunkter:
-
Jag tog bort din VIEW från frågan helt. Mycket kostnad utan vinst.
-
Eftersom du väljer en singel
item_id
, du behöver inteGROUP BY item_id
ellerPARTITION BY item_id
. -
Använd alias för korta tabeller och gör alla referenser entydiga - speciellt när du skriver i ett offentligt forum.
-
Parenteser i dina sammanfogningar var bara buller. Joins körs ändå från vänster till höger som standard.
-
Förenklade datumgränser (eftersom jag arbetar med tidsstämplar):
date_trunc('year', current_date) - interval '540 days' date_trunc('month', current_date) - interval '1 day'
motsvarande, men enklare och snabbare än:
current_date - date_part('day',current_date)::integer - 540 current_date - date_part('day',current_date)::integer -
Jag fyller nu i saknade månader efter alla beräkningar med
generate_series()
samtal per rad. -
Det måste vara
LEFT JOIN LATERAL ... ON TRUE
, inte den korta formen av enJOIN LATERAL
för att fånga hörnet på den sista raden. Detaljerad förklaring:
Viktig sidoanteckning:
character(22)
är en hemsk datatyp för en primärnyckel (eller vilken som helst kolumn). Detaljer:
Helst skulle detta vara en int
eller bigint
kolumn, eller möjligen en UUID
.
Lagrar också pengar som money
typ eller integer
(representerar Cents) presterar mycket bättre totalt sett.
På lång sikt , kommer prestandan att försämras, eftersom du måste inkludera alla rader från början i din beräkning. Du bör skära av gamla rader och materialisera balansen i on_hold
på årsbasis eller något.