Запрос в вопросе делает cross join views на items и разумеется данные в результате будут совершенно верные, но бессмысленные.
Не думаю, что перебирать потенциально большие views и items полностью не прокидывая заведомо известные условия поиска по датам - хорошая мысль даже для аналитического запроса.
select d as day,
(select count(*) from views where created_at >= d and created_at < (d + interval '1 day')) as views_count,
(select count(*) from items where created_at >= d and created_at < (d + interval '1 day')) as items_count
from generate_series('2018-12-26'::date, '2018-12-30', '1 day' ) as dates(d)
order by day desc
Либо похожим образом
select d as day, views_count, items_count
from generate_series('2018-12-26'::date, '2018-12-30', '1 day' ) as dates(d)
left join lateral (select count(*) as views_count from views where created_at >= d and created_at < (d + interval '1 day')) as v on true
left join lateral (select count(*) as views_count from items where created_at >= d and created_at < (d + interval '1 day')) as i on true
order by day desc
И посмотреть, что по этому поводу думает планировщик