sql >> Databáze >  >> RDS >> PostgreSQL

Výběr součtu a průběžného zůstatku za posledních 18 měsíců pomocí create_series

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:

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íte GROUP BY item_id nebo PARTITION 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á forma JOIN 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.




  1. Pro urychlení zredukujte dotazy MySQL na jeden dotaz

  2. Jak generovat skripty pro přidání výchozích omezení do sloupce ve více tabulkách v databázi SQL Server - SQL Server / Výukový program TSQL část 94

  3. vybrat všechny sloupce, které nejsou v jiné tabulce laravel 5.5

  4. Aktivní záznam CodeIgniter – příkazy skupiny NEBO