Desembaraçado, simplificado e fixo, pode ficar assim:
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<>mexa aqui
Entre todo o ruído, identificadores enganosos e formato não convencional, o problema real estava escondido aqui:
WHERE version = 1
Você fez uso correto de
RIGHT [OUTER] JOIN
. Mas adicionar um WHERE
cláusula que requer uma linha existente de mytable
converte o RIGHT [OUTER] JOIN
para um [INNER] JOIN
efetivamente. Mova esse filtro para o
JOIN
condição para fazê-lo funcionar. Eu simplifiquei algumas outras coisas enquanto estava nisso.
Melhor ainda
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<>mexa aqui
É muito mais barato agregar primeiro e juntar depois - juntando uma linha por mês em vez de uma linha por dia.
É mais barato basear
GROUP BY
e ORDER BY
na date
value em vez do text
renderizado . count(*)
é um pouco mais rápido que count(id)
, enquanto equivalente neste inquerir. generate_series()
é um pouco mais rápido e seguro quando baseado em timestamp
em vez de date
. Ver:- Gerando séries temporais entre duas datas no PostgreSQL