Сначала создайте таблицу календаря, полную всех дат, которые вам когда-либо понадобятся.
Скажем, от '1900-01-01'
до '2099-12-31'
?
Тогда это в основном JOIN
...
SELECT
calendar_date, count(*)
FROM
(
SELECT
CALENDAR_TABLE.calendar_date,
visit.user_id,
COUNT(DISTINCT visit.time::date) as cnt
FROM
CALENDAR_TABLE
INNER JOIN
visit
ON visit.time >= CALENDAR_TABLE.calendar_date - interval '30 days'
AND visit.time < CALENDAR_TABLE.calendar_date + interval '01 days'
WHERE
CALENDAR_TABLE.calendar_date BETWEEN '2018-05-01' AND '2018-05-22'
GROUP BY
CALENDAR_TABLE.calendar_date,
visit.user_id
HAVING
COUNT(DISTINCT visit.time::date) >= 10
) t
GROUP BY
calendar_date
Или, возможно ...
SELECT
calendar_date, count(*)
FROM
(
SELECT
CALENDAR_TABLE.calendar_date,
visit.user_id,
COUNT(*) as cnt
FROM
CALENDAR_TABLE
INNER JOIN
(
SELECT
user_id,
time::date AS user_date
FROM
visit
GROUP BY
user_id,
time::date
)
visit
ON visit.user_date >= CALENDAR_TABLE.calendar_date - interval '30 days'
AND visit.user_date < CALENDAR_TABLE.calendar_date + interval '01 days'
WHERE
CALENDAR_TABLE.calendar_date BETWEEN '2018-05-01' AND '2018-05-22'
GROUP BY
CALENDAR_TABLE.calendar_date,
visit.user_id
HAVING
COUNT(*) >= 10
) t
GROUP BY
calendar_date
Это может снизить объем памяти, но может замедлить объединение и фильтрацию ...