sql >> Database teknologi >  >> RDS >> PostgreSQL

Valg af sum og løbende saldo for de sidste 18 måneder med gener_series

Grundlæggende løsning

Generer en komplet liste over måneder og LEFT JOIN resten til 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);

Relaterede svar med mere forklaring:

Avanceret løsning til dit tilfælde

Din forespørgsel er mere kompliceret, end jeg først forstod. Du skal bruge den løbende sum over alle rækker af det valgte element, så vil du trimme rækker, der er ældre end en minimumsdato, og udfylde manglende måneder med den forudberegnede sum af den foregående måned.

Jeg opnår dette 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 et minimum af testtilfælde.

Vigtigste punkter:

  • Jeg fjernede din VIEW fra forespørgslen fuldstændigt. Mange omkostninger uden gevinst.

  • Da du vælger en enkelt item_id , du behøver ikke at GROUP BY item_id eller PARTITION BY item_id .

  • Brug short table aliaser og gør al reference utvetydig - især når du poster i et offentligt forum.

  • Parenteser i dine joins var bare støj. Joins udføres alligevel fra venstre mod højre som standard.

  • Forenklede datogrænser (da jeg arbejder med tidsstempler):

    date_trunc('year', current_date)  - interval '540 days'
    date_trunc('month', current_date) - interval '1 day'
     

    tilsvarende, men enklere og hurtigere end:

    current_date - date_part('day',current_date)::integer - 540
    current_date - date_part('day',current_date)::integer 
  • Jeg udfylder nu manglende måneder efter alle beregninger med generate_series() opkald pr. række.

  • Det skal være LEFT JOIN LATERAL ... ON TRUE , ikke den korte form af en JOIN LATERAL for at fange hjørnekassen i den sidste række. Detaljeret forklaring:

Vigtige sidebemærkninger:

character(22) er en forfærdelig datatype for en primær nøgle (eller en hvilken som helst). kolonne). Detaljer:

Ideelt set ville dette være en int eller bigint kolonne eller muligvis en UUID .

Også lagring af pengebeløb som money type eller integer (repræsenterer Cents) klarer sig generelt meget bedre.

I det lange løb , er ydeevnen forringet, da du skal inkludere alle rækker helt fra begyndelsen i din beregning. Du bør afskære gamle rækker og materialisere saldoen i on_hold på årsbasis eller noget.




  1. Sammenligning af datoer ved hjælp af Dynamic Action på DatePicker Oracle Apex

  2. MOD() Funktion i Oracle

  3. SQL joins

  4. ændre begivenhedsplanlægningsstatus i mysql