Как сделать SQL запрос, который возвращал бы все записи за текущий месяц и несколько записей из предыдущего и следующего, как в календаре

введите сюда описание изображения

Как составить запрос так, чтобы он возвращал данные не только для дней в текущем месяце, а также и для нескольких дней предыдущего месяца и следующего, то есть для серых дней на картинке

Текущий запрос выглядит так:

SELECT e.id, e.name, e.city_id, e.address, e.description, e.url, array(SELECT tag_id FROM event_tags WHERE event_id = e.id), 
        e.age, jsonb_agg(jsonb_build_object('eventDate', dh.event_date, 'startTime', dh.start_time, 'endTime', dh.end_time))
        FROM events e
        INNER JOIN dates_hours dh on e.id = dh.event_id
        WHERE extract(year from dh.event_date) = 2021 and extract(month from dh.event_date) = 7 and city_id = 1
        group by e.id

и возвращает:

id |  name  | city_id |    address    |  description  |          url          |  array  | age |                                   jsonb_agg
----+--------+---------+---------------+---------------+-----------------------+---------+-----+-------------------------------------------------------------------------------
  4 | 123321 |       1 | улица Пушкина | Awesome event | http://event_url.com/ | {4,5,6} |   0 | [{"endTime": "16:10:00", "eventDate": "2021-07-01", "startTime": "15:10:00"}]

а хотелось бы, чтобы возвращал:

id |  name  | city_id |    address    |  description  |          url          |  array  | age |                                   jsonb_agg
----+--------+---------+---------------+---------------+-----------------------+---------+-----+-------------------------------------------------------------------------------
  4 | 123321 |       1 | улица Пушкина | Awesome event | http://event_url.com/ | {4,5,6} |   0 | [{"endTime": "16:10:00", "eventDate": "2021-07-01", "startTime": "15:10:00"}, {"endTime": "14:10:00", "eventDate": "2021-06-30", "startTime": "15:10:00"}, {"endTime": "14:10:00", "eventDate": "2021-06-29", "startTime": "15:10:00"}, {"endTime": "14:10:00", "eventDate": "2021-06-28", "startTime": "15:10:00"}, {"endTime": "14:10:00", "eventDate": "2021-08-01", "startTime": "15:10:00"}]

Ответы (2 шт):

Автор решения: Денис

Если решением будет выбрать события 5 дней прошлого месяца, и 5 дней следующего месяца, попробуйте такой код:

SELECT e.id, e.name, e.city_id, e.address, e.description, e.url, array(SELECT tag_id FROM event_tags WHERE event_id = e.id), 
    e.age, jsonb_agg(jsonb_build_object('eventDate', dh.event_date, 'startTime', dh.start_time, 'endTime', dh.end_time))
    FROM events e
    INNER JOIN dates_hours dh on e.id = dh.event_id
    WHERE extract(year from dh.event_date) = 2021 and extract(month from dh.event_date) in (6, 7, 8) and dh.event_date between ('2021-06-26' and '2021-08-05') and city_id = 1
    group by e.id
→ Ссылка
Автор решения: Mike

Старайтесь при выборках никогда не применять функции к колонкам базы данных в различных условиях, это сильно замедляет работу запроса. Стройте условия так, что бы колонка сравнивалась с заранее подготовленными граничными условиями.

Исходя из этого нам надо придумать формулы, которые вычислят понедельник календарной недели начала месяца и воскресенье календарной недели окончания месяца. Для их получения нам надо знать только номер дня недели первого и последнего дня, после чего вычесть/добавить их к этим дням.

SELECT e.id, e.name, e.city_id, e.address, e.description, e.url, array(SELECT tag_id FROM event_tags WHERE event_id = e.id), 
        e.age, jsonb_agg(jsonb_build_object('eventDate', dh.event_date, 'startTime', dh.start_time, 'endTime', dh.end_time))
  FROM events e
 INNER JOIN dates_hours dh on e.id = dh.event_id
 WHERE city_id = 1
   and dh.event_date between
        '2021-07-01'::date - make_interval(days => extract(isodow from '2021-07-01'::date)::int-1)
    and '2021-07-01'::date + interval  '1 MONTH - 1 day' + make_interval(days => 7 - extract(isodow from '2021-07-01'::date + interval  '1 MONTH - 1 day')::int)
 group by e.id
→ Ссылка