【问题标题】:Get occupancy per every 15-minute slot每 15 分钟获得一次占用
【发布时间】:2021-01-27 20:36:02
【问题描述】:

由于目前的限制,我们有一个房间只能容纳 XX 人。他们来的时间不同,停留的时间也不同。

我正在尝试在特定日期的每 15 分钟内获取当前内部人员的总和。服务器是部署在 AWS RDS 上的 MySQL 8.0.21。

MySQL 8.0 表:预订

ID Name PartySize Date BookedFrom BookedTo
1 John 2 2021-01-01 2021-01-01 08:30:00 2021-01-01 10:00:00
2 Mary 4 2021-01-01 2021-01-01 09:00:00 2021-01-01 11:00:00
3 Nick 3 2021-01-01 2021-01-01 10:30:00 2021-01-01 12:30:00

我还有一个“帮助表”,每 24 小时 15 分钟有一个时间段

MySQL 表:时隙

ID Time
1 00:00:00
2 00:15:00
3 00:30:00
35 08:30:00
37 09:00:00
38 09:15:00

例如,当我在下面运行此查询时,我将获得 09:30 的正确人数(6 人)。对于每个 15 分钟的时段,获得此结果的最有效方法是什么?请注意,虽然BookedTo(日期时间字段)的值可能超过午夜,但我将始终只进行特定日期的查询。

SELECT 
    t.id, b.date, t.time, SUM(b.partysize) AS total
FROM
    booking b,
    timeslot t
WHERE
    b.date = '2021-01-01' 
        AND t.time = '09:15:00'
        AND b.bookedfrom <= '2021-01-01 09:15:00'
        AND b.bookedto >= '2021-01-01 09:15:00'

一直在寻找这个输出(包括零)

Slot_ID Date Time Total
33 2021-01-01 08:00:00 0
34 2021-01-01 08:15:00 0
35 2021-01-01 08:30:00 2
36 2021-01-01 08:30:00 2
37 2021-01-01 09:00:00 6
38 2021-01-01 09:15:00 6

【问题讨论】:

  • 您使用的是 MySQL 还是 Oracle?
  • 您通常 GROUP BY 与您选择的列相同,但那些作为设置函数的参数的列除外。
  • @jarlh 我正在使用 MySQL 服务器
  • 你必须使用LEFT JOIN 来获得零和。
  • 为什么要在分组中除以 900?时隙表已经以 15 分钟为边界。

标签: mysql sql


【解决方案1】:
SELECT
  t.id as slot_id,
  coalesce(b.date, '2021-01-01') as date,
  t.time,
  coalesce(sum(b.partysize),0) as total
FROM
  timeslot t
  LEFT JOIN booking b
  ON t.time >= TIME(b.bookedfrom) AND t.time < TIME(b.bookedto) AND b.date = '2021-01-01'
WHERE
  t.time BETWEEN '08:00:00' AND '17:00:00'
GROUP BY
  t.id,
  b.date,
  t.time

现在,您还有一些令人困惑的其他要求,但基本上这是可行的,因为由于所表达的时间范围,多行时间段将匹配单行预订。

令人困惑的要求是,您说它只针对下午 8 点到 5 点,但“预订可能会延长到第二天”.. 是否意味着预订将从下午 4 点开始并在第二天早上 9 点结束?在这种情况下,您可能需要将AND b.date = '2021-01-01' 调整为更像AND (DATE(b.bookedfrom) = '2021-01-01' OR DATE(b.bookedto) = '2021-01-01') ...

【讨论】:

  • 您可以使用fiddle 来测试各种RDBMS 上的查询
  • 感谢您的提示。我更喜欢 dbfiddle 自己;更多 dbs,但它们都不能很好地与手机配合使用
  • @CaiusJard 不幸的是,它没有返回任何值。此外,每个预订都有不同的聚会规模,我计算的是里面的总人数,而不是预订的数量。
  • 将其更改为使用 sum(partysize).. 但是当我运行它时它会返回数据...
  • @CaiusJard - 谢谢!它现在也为我工作。感谢您提供详细的答案,也感谢您包括聚会的人数。它也可能很方便。你得到了我的支持,我将不得不等待 24 小时才能获得赏金。感谢您的时间和帮助!
【解决方案2】:

使用CTE 来返回您想要结果的具体日期,这可能与BookingCROSS 中的Date 列不同,将其加入Timeslot
结果应该是LEFT加入Booking然后聚合:

WITH cte(Date) AS (SELECT '2021-01-01')
SELECT t.ID, t.time, c.Date, 
       COALESCE(SUM(b.PartySize), 0) Total
FROM cte c CROSS JOIN Timeslot t 
LEFT JOIN Booking b
ON b.BookedFrom <= CONCAT(c.Date, ' ', t.time)
AND b.BookedTo >= CONCAT(c.Date, ' ', ADDTIME(t.time, '00:15:00'))
WHERE t.time BETWEEN '08:00:00' AND '17:00:00'
GROUP BY t.ID, c.Date, t.time

由于BookedFromBookedTo 可能不包含相同的日期,因此仅将两列的时间部分与Timeslot 的列time 进行比较是不安全的。
这就是为什么需要 ON 子句中的所有这些条件的原因。

请参阅demo

【讨论】:

  • 结束包容可能是个bug
  • @CaiusJard 不,这不是错误,因为我在time 中添加了 15 分钟。如果我添加了00:14:59,那么我会将&gt;= 更改为&gt;
  • 感谢您的详细解答和代码示例!一切似乎都正常工作。我将不得不运行一些测试,看看这些版本是否更快,以及我是否可以将其转换为 Django ORM。我想这样做而不是使用原始 SQL 的唯一原因是因为该应用程序是时区感知的,并且 bookingrom 和 bookingto 存储在 UTC 中。再次感谢您的帮助和您的时间!你得到了我的支持,明天将颁发赏金。
【解决方案3】:

这个查询效果很好......如果你想拥有所有时段的所有日期......你也必须有一个日期表(最好在时间段内 -> 交叉连接日期和时间段)......

如果您只想获得匹配的日期和时间段,请使用内部连接 ​​..

SELECT t.id as slot_id
, b.date 
, t.time as slot 
, sum(ifnull(party_size,0)) as total
FROM test.timeslot t
LEFT JOIN test.booking b 
ON t.time BETWEEN time(b.booked_from) AND time(b.booked_to)
GROUP BY t.id 
, b.date 
, t.time;

对于所有时间段和选定日期:

https://www.db-fiddle.com/f/gLt2Fs8HTDUakMahZHxcTi/0

匹配时间段和日期:

SELECT t.id as slot_id
, b.date 
, t.time as slot 
, sum(ifnull(party_size,0)) as total
FROM test.timeslot t
JOIN test.booking b 
ON t.time BETWEEN time(b.booked_from) AND time(b.booked_to)
GROUP BY t.id 
, b.date 
, t.time;

【讨论】:

  • 哇,这太棒了,似乎正在做我需要的事情!让我运行一些测试,看看我是否可以在我的应用程序中实现它。顺便说一句,1=1 到底是什么?
  • 这只是我的 'hack' .. 使调试更容易 :) 随意删除它
  • 你让我一时糊涂了。哈哈。谢谢!感谢您提供详细的答案,也感谢您提供 db-fiddle。你得到了我的支持。由于规则,我必须等待 24 小时才能获得赏金。感谢您的时间和帮助!
  • 不用担心 :) 很乐意提供帮助...如果您还有什么需要帮助的,请让我知道...
猜你喜欢
  • 2017-05-09
  • 1970-01-01
  • 2011-04-25
  • 2015-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-09
  • 1970-01-01
相关资源
最近更新 更多