PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Junte-se a uma consulta de contagem em generate_series() e recupere valores nulos como '0'


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