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

Unisciti a una query di conteggio su generate_series() e recupera i valori Null come '0'

Districato, semplificato e corretto, potrebbe assomigliare a questo:

SELECT to_char(s.tag,'yyyy-mm') AS monat
     , count(t.id) AS eintraege
FROM  (
   SELECT generate_series(min(date_from)::date
                        , max(date_from)::date
                        , interval '1 day'
          )::date AS tag
   FROM   mytable t
   ) s
LEFT   JOIN mytable t ON t.date_from::date = s.tag AND t.version = 1   
GROUP  BY 1
ORDER  BY 1;

db<>gioca qui

Tra tutto il rumore, gli identificatori fuorvianti e il formato non convenzionale il vero problema era nascosto qui:

WHERE version = 1

Hai fatto un uso corretto di RIGHT [OUTER] JOIN . Ma aggiungendo un WHERE clausola che richiede una riga esistente da mytable converte il RIGHT [OUTER] JOIN a un [INNER] JOIN efficacemente.

Sposta quel filtro nel JOIN condizione per farlo funzionare.

Ho semplificato alcune altre cose mentre ci lavoravo.

Meglio, ancora

SELECT to_char(mon, 'yyyy-mm') AS monat
     , COALESCE(t.ct, 0) AS eintraege
FROM  (
   SELECT date_trunc('month', date_from)::date AS mon
        , count(*) AS ct
   FROM   mytable
   WHERE  version = 1     
   GROUP  BY 1
   ) t
RIGHT JOIN (
   SELECT generate_series(date_trunc('month', min(date_from))
                        , max(date_from)
                        , interval '1 mon')::date
   FROM   mytable
   ) m(mon) USING (mon)
ORDER  BY mon;

db<>gioca qui

È molto più economico aggregare prima e unirsi in seguito, unendo una riga al mese anziché una riga al giorno.

È più economico basare GROUP BY e ORDER BY alla date valore invece del text visualizzato .

count(*) è un po' più veloce di count(id) , mentre è equivalente in questo interrogazione.

generate_series() è un po' più veloce e sicuro se basato su timestamp invece di date . Vedi:

  • Generazione di serie temporali tra due date in PostgreSQL