Разплетено, опростено и фиксирано, може да изглежда така:
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<>цигулка тук
Сред целия шум, подвеждащи идентификатори и нетрадиционен формат действителният проблем беше скрит тук:
WHERE version = 1
Използвахте правилно RIGHT [OUTER] JOIN
. Но добавяне на WHERE
клауза, която изисква съществуващ ред от mytable
преобразува RIGHT [OUTER] JOIN
към [INNER] JOIN
ефективно.
Преместете този филтър в JOIN
условие, за да работи.
Опростих някои други неща, докато бях в него.
Още по-добре
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<>цигулка тук
Много по-евтино е първо да обобщите и да се присъедините по-късно – да се присъедините към един ред на месец вместо един ред на ден.
По-евтино е да се базира GROUP BY
и ORDER BY
на date
стойност вместо изобразения text
.
count(*)
е малко по-бърз от count(id)
, докато е еквивалентен в това заявка.
generate_series()
е малко по-бърз и по-безопасен, когато се базира на timestamp
вместо date
. Вижте:
- Генериране на времеви серии между две дати в PostgreSQL