Ontward, vereenvoudigd en gefixeerd, het zou er zo uit kunnen zien:
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<>viool hier
Tussen al het lawaai, de misleidende identificatiecodes en het onconventionele formaat was het eigenlijke probleem hier verborgen:
WHERE version = 1
Je hebt correct gebruik gemaakt van RIGHT [OUTER] JOIN
. Maar het toevoegen van een WHERE
clausule die een bestaande rij vereist uit mytable
converteert de RIGHT [OUTER] JOIN
naar een [INNER] JOIN
effectief.
Verplaats dat filter naar de JOIN
voorwaarde om het te laten werken.
Ik heb wat andere dingen vereenvoudigd terwijl ik bezig was.
Beter, maar toch
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<>viool hier
Het is veel goedkoper om eerst te verzamelen en later mee te doen - één rij per maand in plaats van één rij per dag.
Het is goedkoper om GROUP BY
te baseren en ORDER BY
op de date
waarde in plaats van de weergegeven text
.
count(*)
is een beetje sneller dan count(id)
, terwijl het equivalent is in dit vraag.
generate_series()
is een beetje sneller en veiliger wanneer gebaseerd op timestamp
in plaats van date
. Zie:
- Tijdreeksen genereren tussen twee datums in PostgreSQL