Desenredado, simplificado y arreglado, podría verse así:
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<>violín aquí
Entre todo el ruido, los identificadores engañosos y el formato poco convencional, el problema real estaba oculto aquí:
WHERE version = 1
Hiciste un uso correcto de RIGHT [OUTER] JOIN
. Pero agregando un WHERE
cláusula que requiere una fila existente de mytable
convierte RIGHT [OUTER] JOIN
a un [INNER] JOIN
efectivamente.
Mueve ese filtro a JOIN
condición para que funcione.
Simplifiqué algunas otras cosas mientras estaba en ello.
Mejor, aún
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<>violín aquí
Es mucho más económico agregar primero y unirse después:unirse a una fila por mes en lugar de una fila por día.
Es más barato basar GROUP BY
y ORDER BY
en la date
valor en lugar del text
representado .
count(*)
es un poco más rápido que count(id)
, mientras que es equivalente en este consulta.
generate_series()
es un poco más rápido y seguro cuando se basa en timestamp
en lugar de date
. Ver:
- Generando series de tiempo entre dos fechas en PostgreSQL