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