Usammenfiltret, forenklet og fikset kan det se sådan ud:
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<>spil her
Blandt al støjen, vildledende identifikatorer og det ukonventionelle format var det faktiske problem skjult her:
WHERE version = 1
Du har brugt RIGHT [OUTER] JOIN
korrekt . Men tilføjelse af en WHERE
klausul, der kræver en eksisterende række fra mytable
konverterer RIGHT [OUTER] JOIN
til en [INNER] JOIN
effektivt.
Flyt det filter ind i JOIN
betingelse for at få det til at fungere.
Jeg forenklede nogle andre ting, mens jeg var i gang.
Bedre, dog
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<>spil her
Det er meget billigere at samle først og slutte sig til senere - slutte sig til én række om måneden i stedet for én række om dagen.
Det er billigere at basere GROUP BY
og ORDER BY
på date
værdi i stedet for den gengivede text
.
count(*)
er en smule hurtigere end count(id)
, mens det svarer til dette forespørgsel.
generate_series()
er en smule hurtigere og sikrere, når den er baseret på timestamp
i stedet for date
. Se:
- Generering af tidsserier mellem to datoer i PostgreSQL