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

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

给出下表:
资源

| 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个给定日期段的资源可用性?

rta7y2nd

rta7y2nd1#

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

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的结果具体化为一个表(这是日历表的概念),然后在查询中使用这个表。

sr4lhrrt

sr4lhrrt2#

一种可能的方法是创建一个临时表,在其中放置感兴趣的时段 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` 
)

相关问题