Basisoplossing
Genereer een volledige lijst van maanden en LEFT JOIN
de rest:
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);
Gerelateerde antwoorden met meer uitleg:
- Doe mee aan een count-query op een generator_series in postgres en haal ook Null-waarden op als "0"
- Beste manier om records te tellen met willekeurige tijdsintervallen in Rails+Postgres
Geavanceerde oplossing voor uw zaak
Uw vraag is ingewikkelder dan ik eerst begreep. Je hebt de lopende som nodig over alle rijen van het geselecteerde item, dan wilt u rijen die ouder zijn dan een minimumdatum inkorten en ontbrekende maanden invullen met de vooraf berekende som van de vorige maand.
Ik bereik dit nu met 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 met een minimale testcase.
Belangrijkste punten:
-
Ik heb je VIEW volledig uit de zoekopdracht verwijderd. Veel kosten zonder winst.
-
Aangezien u een enkele . selecteert
item_id
, u hoeft nietGROUP BY item_id
ofPARTITION BY item_id
. -
Gebruik korte tabelaliassen en maak alle verwijzingen ondubbelzinnig - vooral wanneer u op een openbaar forum plaatst.
-
Haakjes in je joins waren gewoon ruis. Joins worden standaard toch van links naar rechts uitgevoerd.
-
Vereenvoudigde datumgrenzen (omdat ik met tijdstempels werk):
date_trunc('year', current_date) - interval '540 days' date_trunc('month', current_date) - interval '1 day'
gelijkwaardig, maar eenvoudiger en sneller dan:
current_date - date_part('day',current_date)::integer - 540 current_date - date_part('day',current_date)::integer -
Ik vul nu ontbrekende maanden in na alle berekeningen met
generate_series()
oproepen per rij. -
Het moet
LEFT JOIN LATERAL ... ON TRUE
. zijn , niet de korte vorm van eenJOIN LATERAL
om de hoekkast van de laatste rij te vangen. Gedetailleerde uitleg:
Belangrijke kanttekeningen:
character(22)
is een verschrikkelijke gegevenstype voor een primaire sleutel (of elke kolom). Details:
Idealiter zou dit een int
. zijn of bigint
kolom, of mogelijk een UUID
.
Ook het opslaan van geldbedragen als money
type of integer
(voor Cents) presteert over het algemeen veel beter.
Op de lange termijn , zullen de prestaties zeker verslechteren, omdat u alle rijen vanaf het begin in uw berekening moet opnemen. U moet oude rijen afsnijden en het saldo van on_hold
material materialiseren op jaarbasis of zoiets.