Определить доступность ресурсов в n периодов дат - PullRequest
2 голосов
/ 28 мая 2020

Учитывая следующие таблицы:

ресурсы

| id | name |
| 1  | John |
| 2  | Anna |

бронирования

| id | start               | end                | resource_id |
| 1  | 2020-05-29 08:00:00 |2020-05-29 12:00:00 | 1           |

Я хочу найти все ресурсы, доступные для n периодов даты. Я могу создать динамический c запрос, используя следующий подзапрос:

SELECT name, (
    SELECT COUNT(id) FROM bookings WHERE
        resources.id = bookings.resource_id
        AND '2020-05-29 08:00:00' < `end`
        AND '2020-05-29 09:00:00' > `start` 
) AS 'dp1',
(SELECT COUNT(id) FROM bookings WHERE
        resources.id = bookings.resource_id
        AND '2020-05-29 09:00:00' < `end`
        AND '2020-05-29 10:00:00' > `start` 
) AS 'dp2'
FROM resources

, который дает мне следующий результат:

| name | dp1 | dp2 |
| John | 1   | 1   |
| Anna | 0   | 0   |

Это не очень хорошо масштабируется. Если я хочу определить доступность на целую неделю, с 08-17, это будет 70 подзапросов с текущим решением.

Как я могу определить доступность ресурсов для большей n заданной даты периоды элегантнее и эффективнее?

Ответы [ 2 ]

3 голосов
/ 29 мая 2020

Я бы посоветовал ставить точки в строках, а не в столбцах. Это дает вам возможность получить чистое решение SQL - в противном случае вам потребуется динамическое c SQL.

Если вы используете MySQL 8.0, вы можете использовать рекурсивный запрос для перечисления периоды, cross join это с таблицей resources, чтобы получить все комбинации, а затем привести таблицу с left join.

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

with period as (
    select '2020-05-22 08:00:00' ts1 
    union all
    select case when hour(ts1) = 16 
        then date_format(ts, '%Y-%m-%d 09:00:00') + interval 1 day
        else ts + interval 1 hour
    end
    from cte    
    where ts < '2020-05-29 17:00:00' 
)
select 
    p.ts period,
    r.name,
    count(b.id) is_reserved
from periods p
cross join resources r 
left join bookings b 
    on  b.start <= p.ts + interval 1 hour 
    and b.end   >= p.ts 
    and b.resource_id = r.id
group by p.ts, r.name

Если вы собираетесь часто запускать этот запрос, вам следует рассмотреть возможность материализации результатов cte в таблице (это концепция календарной таблицы ), который затем можно использовать в своих запросах.

1 голос
/ 29 мая 2020

Один из возможных подходов - создать временную таблицу, в которую вы поместите интересующие вас периоды под названием periods_table:

| period_id | period_start        | period_end         | 
|        1  | 2020-05-29 08:00:00 |2020-05-29 09:00:00 | 
|        2  | 2020-05-29 09:00:00 |2020-05-29 10:00:00 | 
|        3  | 2020-05-29 10:00:00 |2020-05-29 11:00:00 | 
|        4  | 2020-05-29 11:00:00 |2020-05-29 12:00:00 | 
    ... etc

И затем вы сопоставите эти периоды со своими значениями из бронирований. и ресурсы:

SELECT name, period_start, period_end, count(period_id) FROM (
SELECT bookings.start, bookings.end JOIN resources ON 
   resources.id = bookings.resource_id  
) AS `subquery` JOIN periods_table WHERE
        period_end > `start`
        AND 'period_start' < `end` 
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...