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