确定n个日期段上资源的可用性

vptzau2j  于 2021-08-13  发布在  Java
关注(0)|答案(2)|浏览(462)

给出下表:
资源

  1. | id | name |
  2. | 1 | John |
  3. | 2 | Anna |

预订

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

我要查找n个日期段的所有可用资源。我可以使用子查询构建动态查询,如下所示:

  1. SELECT name, (
  2. SELECT COUNT(id) FROM bookings WHERE
  3. resources.id = bookings.resource_id
  4. AND '2020-05-29 08:00:00' < `end`
  5. AND '2020-05-29 09:00:00' > `start`
  6. ) AS 'dp1',
  7. (SELECT COUNT(id) FROM bookings WHERE
  8. resources.id = bookings.resource_id
  9. AND '2020-05-29 09:00:00' < `end`
  10. AND '2020-05-29 10:00:00' > `start`
  11. ) AS 'dp2'
  12. FROM resources

结果如下:

  1. | name | dp1 | dp2 |
  2. | John | 1 | 1 |
  3. | Anna | 0 | 0 |

这个比例不太好。如果我想确定整个星期的可用性,从08-17这将是70个子查询与当前的解决方案。
如何更优雅、更有效地确定n个给定日期段的资源可用性?

rta7y2nd

rta7y2nd1#

我建议把句点放在行中而不是列中。这为您提供了一个纯sql解决方案的机会—否则您将需要动态sql。
如果您运行的是mysql 8.0,可以使用递归查询来枚举句点, cross join 它与 resources 得到表中的所有组合,然后把表与 left join .
下面是一个查询,它将为您提供一周内所需的结果。您可以根据需要调整cte中的boudaries。

  1. with period as (
  2. select '2020-05-22 08:00:00' ts1
  3. union all
  4. select case when hour(ts1) = 16
  5. then date_format(ts, '%Y-%m-%d 09:00:00') + interval 1 day
  6. else ts + interval 1 hour
  7. end
  8. from cte
  9. where ts < '2020-05-29 17:00:00'
  10. )
  11. select
  12. p.ts period,
  13. r.name,
  14. count(b.id) is_reserved
  15. from periods p
  16. cross join resources r
  17. left join bookings b
  18. on b.start <= p.ts + interval 1 hour
  19. and b.end >= p.ts
  20. and b.resource_id = r.id
  21. group by p.ts, r.name

如果要经常运行此查询,应该考虑将cte的结果具体化为一个表(这是日历表的概念),然后在查询中使用这个表。

展开查看全部
sr4lhrrt

sr4lhrrt2#

一种可能的方法是创建一个临时表,在其中放置感兴趣的时段 periods_table :

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

然后将时段与来自预订和资源的值进行匹配:

  1. SELECT name, period_start, period_end, count(period_id) FROM (
  2. SELECT bookings.start, bookings.end JOIN resources ON
  3. resources.id = bookings.resource_id
  4. ) AS `subquery` JOIN periods_table WHERE
  5. period_end > `start`
  6. AND 'period_start' < `end`
  7. )
展开查看全部

相关问题