确定资源在 n 个日期期间的可用性
Determine availability for resources on n date periods
给出下表:
资源
| 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 日期期间的所有可用资源。我可以使用子查询构建动态查询,如下所示:
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 给定日期期间的资源可用性?
我建议将句点放在行中而不是列中。这为您提供了一个纯 SQL 解决方案的机会——否则您将需要动态 SQL.
如果你是运行ning MySQL 8.0,你可以使用递归查询来枚举句点,cross join
它与resources
table获得所有组合,然后将 table 与 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的结果具体化在一个table中(也就是日历table的概念),即然后您可以在查询中使用。
一种可能的方法是创建一个临时的 table,您可以在其中放置您感兴趣的时间段 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`
)
给出下表:
资源
| 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 日期期间的所有可用资源。我可以使用子查询构建动态查询,如下所示:
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 给定日期期间的资源可用性?
我建议将句点放在行中而不是列中。这为您提供了一个纯 SQL 解决方案的机会——否则您将需要动态 SQL.
如果你是运行ning MySQL 8.0,你可以使用递归查询来枚举句点,cross join
它与resources
table获得所有组合,然后将 table 与 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的结果具体化在一个table中(也就是日历table的概念),即然后您可以在查询中使用。
一种可能的方法是创建一个临时的 table,您可以在其中放置您感兴趣的时间段 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`
)