Entwirrt, vereinfacht und fixiert könnte es so aussehen:
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<>hier fummeln
Unter all dem Lärm, den irreführenden Kennungen und dem unkonventionellen Format war das eigentliche Problem hier verborgen:
WHERE version = 1
Sie haben RIGHT [OUTER] JOIN korrekt verwendet . Aber ein WHERE hinzufügen -Klausel, die eine vorhandene Zeile aus mytable erfordert konvertiert den RIGHT [OUTER] JOIN zu einem [INNER] JOIN effektiv.
Verschieben Sie diesen Filter in den JOIN Bedingung, damit es funktioniert.
Dabei habe ich einige andere Dinge vereinfacht.
Noch besser
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<>hier fummeln
Es ist viel billiger, zuerst zu aggregieren und später beizutreten – eine Reihe pro Monat statt einer Reihe pro Tag beizutreten.
Es ist billiger, auf GROUP BY zu basieren und ORDER BY am date Wert anstelle des gerenderten text .
count(*) ist etwas schneller als count(id) , während es in this äquivalent ist Abfrage.
generate_series() ist etwas schneller und sicherer, wenn es auf timestamp basiert statt date . Siehe:
- Generieren von Zeitreihen zwischen zwei Daten in PostgreSQL