Podstawowe rozwiązanie
Wygeneruj pełną listę miesięcy i LEFT JOIN
reszta do tego:
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);
Powiązane odpowiedzi z dodatkowym wyjaśnieniem:
- Dołącz do zapytania licznika w generatorze serii w postgresie, a także pobierz wartości Null jako „0”
- Najlepszy sposób liczenia rekordów w dowolnych odstępach czasu w Rails+Postgres
Zaawansowane rozwiązanie dla Twojej sprawy
Twoje zapytanie jest bardziej skomplikowane, niż początkowo zrozumiałem. Potrzebujesz sumy bieżącej ponad wszystko wierszy wybranego elementu, a następnie chcesz przyciąć wiersze starsze niż data minimalna i uzupełnić brakujące miesiące wstępnie obliczoną sumą z poprzedniego miesiąca.
Osiągam to teraz dzięki 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);
Skrzypce SQL z minimalnym przypadkiem testowym.
Główne punkty:
-
Całkowicie usunąłem Twój VIEW z zapytania. Duży koszt bez zysku.
-
Ponieważ wybierzesz singla
item_id
, nie musiszGROUP BY item_id
lubPARTITION BY item_id
. -
Używaj krótkich aliasów tabel i postaraj się, aby wszystkie odniesienia były jednoznaczne - zwłaszcza podczas publikowania na forum publicznym.
-
Nawiasy w sprzężeniach to tylko hałas. Połączenia są domyślnie wykonywane od lewej do prawej.
-
Uproszczone granice dat (ponieważ używam znaczników czasu):
date_trunc('year', current_date) - interval '540 days' date_trunc('month', current_date) - interval '1 day'
równoważne, ale prostsze i szybsze niż:
current_date - date_part('day',current_date)::integer - 540 current_date - date_part('day',current_date)::integer -
Teraz uzupełniam brakujące miesiące po wszystkich obliczeniach za pomocą
generate_series()
połączeń na rząd. -
Musi być
LEFT JOIN LATERAL ... ON TRUE
, a nie krótka formaJOIN LATERAL
aby złapać narożnik w ostatnim rzędzie. Szczegółowe wyjaśnienie:
Ważne uwagi dodatkowe:
character(22)
jest straszne typ danych dla klucza podstawowego (lub dowolny kolumna). Szczegóły:
Idealnie byłoby to int
lub bigint
kolumna lub ewentualnie UUID
.
Ponadto przechowywanie kwot pieniężnych jako money
wpisz lub integer
(reprezentujący centy) ogólnie działa znacznie lepiej.
Na dłuższą metę , wydajność na pewno się pogorszy, ponieważ w obliczeniach musisz uwzględnić wszystkie wiersze od samego początku. Powinieneś odciąć stare wiersze i zmaterializować saldo on_hold
co roku czy coś takiego.