sql >> Database >  >> RDS >> PostgreSQL

Som en lopend saldo voor de afgelopen 18 maanden selecteren met Genereer_series

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:

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 niet GROUP BY item_id of PARTITION 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 een JOIN 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.




  1. Maak twee arrays voor twee velden, waarbij de sorteervolgorde van arrays synchroon blijft (zonder subquery)

  2. Opgeslagen procedure die automatisch rijen verwijdert die ouder zijn dan 7 dagen in MYSQL

  3. Hoe krijg ik de IDENTITY / AUTONUMBER-waarde voor de rij die ik in pymysql heb ingevoegd

  4. Vreemd gedrag met mysql_affected_rows() in PHP