Rozmotané, zjednodušené a opravené by to mohlo vypadat takto:
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<>zde hrajte
Mezi vším tím šumem, zavádějícími identifikátory a nekonvenčním formátem byl skutečný problém skrytý zde:
WHERE version = 1
Správně jste použili RIGHT [OUTER] JOIN
. Ale přidáním WHERE
klauzule, která vyžaduje existující řádek z mytable
převede RIGHT [OUTER] JOIN
do [INNER] JOIN
efektivně.
Přesuňte tento filtr do JOIN
podmínkou, aby to fungovalo.
Zjednodušil jsem některé další věci, když jsem u toho byl.
Ještě lepší
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<>zde hrajte
Je mnohem levnější nejprve agregovat a připojit se později – místo jednoho řádku za den spojíte jeden řádek za měsíc.
Je levnější založit GROUP BY
a ORDER BY
v date
hodnotu namísto vykresleného text
.
count(*)
je o něco rychlejší než count(id)
, zatímco ekvivalentní v tomto dotaz.
generate_series()
je o něco rychlejší a bezpečnější, když je založen na timestamp
místo date
. Viz:
- Generování časových řad mezi dvěma daty v PostgreSQL