PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Wybieranie sumy i salda bieżącego za ostatnie 18 miesięcy za pomocą generate_series

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:

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 musisz GROUP BY item_id lub PARTITION 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 forma JOIN 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.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Zapytanie między serwerami PostgreSQL?

  2. nie można połączyć się z serwerem dla Postgres

  3. Znajdź identyfikator rodzica, gdy wszystkie dzieci mają tę samą wartość

  4. Jak zezwolić na zdalny dostęp do bazy danych PostgreSQL

  5. Jak zaimportować zrzut Heroku PG na komputer lokalny?