PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Joignez une requête de comptage sur generate_series() et récupérez les valeurs Null comme '0'

Démêlé, simplifié et corrigé, cela pourrait ressembler à ceci :

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<>jouez ici

Parmi tout le bruit, les identifiants trompeurs et le format non conventionnel, le problème réel était caché ici :

WHERE version = 1

Vous avez utilisé correctement RIGHT [OUTER] JOIN . Mais en ajoutant un WHERE clause qui nécessite une ligne existante de mytable convertit la RIGHT [OUTER] JOIN à un [INNER] JOIN efficacement.

Déplacez ce filtre dans le JOIN condition pour que cela fonctionne.

J'ai simplifié d'autres choses tout en y étant.

Mieux, encore

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<>jouez ici

Il est beaucoup moins cher d'agréger d'abord et de rejoindre plus tard - rejoindre une ligne par mois au lieu d'une ligne par jour.

Il est moins cher de baser GROUP BY et ORDER BY à la date valeur au lieu du text rendu .

count(*) est un peu plus rapide que count(id) , alors qu'équivalent dans ceci requête.

generate_series() est un peu plus rapide et plus sûr lorsqu'il est basé sur timestamp au lieu de date . Voir :

  • Générer des séries temporelles entre deux dates dans PostgreSQL