Otrasslat, förenklat och fixat kan det se ut så här:
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<>spela här
Bland allt brus, vilseledande identifierare och okonventionella format gömdes det faktiska problemet här:
WHERE version = 1
Du använde RIGHT [OUTER] JOIN
korrekt . Men att lägga till en WHERE
sats som kräver en befintlig rad från mytable
konverterar RIGHT [OUTER] JOIN
till en [INNER] JOIN
effektivt.
Flytta filtret till JOIN
villkor för att få det att fungera.
Jag förenklade en del andra saker medan jag höll på.
Ännu bättre
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<>spela här
Det är mycket billigare att lägga ihop först och gå med senare - gå med en rad per månad istället för en rad per dag.
Det är billigare att basera GROUP BY
och ORDER BY
på date
värde istället för den renderade text
.
count(*)
är lite snabbare än count(id)
, medan motsvarande i denna fråga.
generate_series()
är lite snabbare och säkrare baserat på timestamp
istället för date
. Se:
- Genererar tidsserier mellan två datum i PostgreSQL