我有一张预订我资产的桌子。
我们有n个资产$nof
,可以在开放时间$open
和结束时间$closed
如果所选时间点没有任何可用资产,我想找到下一个可用时间,其中至少有一项资产可用60分钟。
我现在在表格上以15分钟的增量进行迭代以检查可用性,并在availability > 0
发生超过4次(4 * 15m)时将其重置为availability ==0
时重新启动循环。
但这感觉不对。而且它很慢:(
这是我从数据库中提取的一个例子。 asset_type
来自另一个表,该表链接在asset_id
表中的asset
上。为清楚起见,我省略了这个连接,并将其表示为列。
Reservations
id | asset_id | asset_type | start | end |
-----+----------+------------+------------------+------------------+
2 | 67 | 99 | 2017-01-16 11:00 | 2017-01-16 14:00 |
4 | 67 | 99 | 2017-01-16 14:30 | 2017-01-16 15:45 |
3 | 54 | 99 | 2017-01-16 12:30 | 2017-01-16 16:00 |
总共有2种99型资产可供选择:67和54.
所以:
// nextAvailable($assesTime, $gap_in_minutes, $nof_assets, $asset_type);
echo nextAvailable('2017-01-16 11:30', 60, 2, 99);
// 15:45,
// not 14:00 as there is only 30 minutes between res#2 and #4
解决这个问题的最佳方法是什么?
答案 0 :(得分:1)
好的,让我们做一些数据!
DECLARE @DateToCheck DATETIME = '2017-01-16 11:30'
DECLARE @LengthOfTimeInMinutes INT = 60
DECLARE @TimeSlotLengthInMinutes INT = 15
DECLARE @Reservations TABLE
(
id INT,
asset_id INT,
asset_type INT,
startdt DATETIME,
enddt DATETIME
)
INSERT INTO @Reservations
( id, asset_id, asset_type, startdt, enddt )
VALUES
( 2, 67, 99, '2017-01-16 11:00', '2017-01-16 14:00' ),
( 4, 67, 99, '2017-01-16 14:30', '2017-01-16 15:45' ),
( 3, 54, 99, '2017-01-16 12:30', '2017-01-16 16:00' );
现在我要创建一个数字表来生成大约15分钟的时隙,这样我们就可以看到已填充的插槽和仍然可用的插槽
DECLARE @number_of_numbers INT = 672; --Total Number of TimeSlots to Generate at 15 Minutes, 4 per Hour, 96 per day, 672 per week
;WITH
a AS (SELECT 1 AS i UNION ALL SELECT 1),
b AS (SELECT 1 AS i FROM a AS x, a AS y),
c AS (SELECT 1 AS i FROM b AS x, b AS y),
d AS (SELECT 1 AS i FROM c AS x, c AS y),
e AS (SELECT 1 AS i FROM d AS x, d AS y),
f AS (SELECT 1 AS i FROM e AS x, e AS y),
numbers AS
(
SELECT TOP(@number_of_numbers)
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS number
FROM f
), Rooms AS
(
SELECT DISTINCT r.asset_id FROM @Reservations r
), TimeSlots AS
(
SELECT DATEADD(MINUTE, (n.number - 1) * @TimeSlotLengthInMinutes, DATEADD(DAY, DATEDIFF(DAY, 0, @DateToCheck), 0)) AS TimeSlot
FROM numbers n
), RoomSlots AS
(
SELECT r.asset_id, t.TimeSlot
FROM TimeSlots t
CROSS JOIN Rooms r
), Reserved AS
(
SELECT rs.asset_id, rs.TimeSlot, r.id, r.startdt, r.enddt
FROM RoomSlots rs
LEFT JOIN @Reservations r
ON r.asset_id = rs.asset_id
AND rs.TimeSlot >= r.startdt AND rs.TimeSlot < r.enddt
), LookingForSlots AS
(
SELECT st.asset_id, st.TimeSlot AS StartSlot, DATEADD(MINUTE, @LengthOfTimeInMinutes, st.TimeSlot) AS EndSlot
FROM RoomSlots st
)
现在在上面的代码中,我为我正在检查的那一周的每个可用资产制作了一系列15分钟的时间段 - (roomslots) 然后我加入了roomlots到保留的资产,以全面了解每个房间的使用/可用时段。
现在让我们来寻找一些开放点:
SELECT top 10 l.asset_id, l.StartSlot, l.EndSlot, r.*
FROM LookingForSlots l
LEFT JOIN Reserved r
ON l.asset_id = r.asset_id
AND l.StartSlot < r.enddt
AND l.EndSlot > r.startdt
WHERE r.id IS NULL
AND l.StartSlot >= @DateToCheck
ORDER BY l.StartSlot, l.asset_id
这是输出:
asset_id StartSlot EndSlot asset_id TimeSlot id startdt enddt
54 2017-01-16 11:30:00.000 2017-01-16 12:30:00.000 NULL NULL NULL NULL NULL
67 2017-01-16 15:45:00.000 2017-01-16 16:45:00.000 NULL NULL NULL NULL NULL
54 2017-01-16 16:00:00.000 2017-01-16 17:00:00.000 NULL NULL NULL NULL NULL
67 2017-01-16 16:00:00.000 2017-01-16 17:00:00.000 NULL NULL NULL NULL NULL
54 2017-01-16 16:15:00.000 2017-01-16 17:15:00.000 NULL NULL NULL NULL NULL
67 2017-01-16 16:15:00.000 2017-01-16 17:15:00.000 NULL NULL NULL NULL NULL
54 2017-01-16 16:30:00.000 2017-01-16 17:30:00.000 NULL NULL NULL NULL NULL
67 2017-01-16 16:30:00.000 2017-01-16 17:30:00.000 NULL NULL NULL NULL NULL
54 2017-01-16 16:45:00.000 2017-01-16 17:45:00.000 NULL NULL NULL NULL NULL
67 2017-01-16 16:45:00.000 2017-01-16 17:45:00.000 NULL NULL NULL NULL NULL
现在你可以看到第一个找到的插槽是11:30的asset_id 54,下一个可用于15:45的asset_id 67.
它以基于集合的方式运行得相当快,你可以根据生成的房间数/时间段来调整速度(我只生成了一周的数据,你可以将它设置得更低,只是生成一天来反对如果你有更多的房间。
Eek,对不起,我没有在那里意识到MySQL,你可以将其中的大部分转化为表来进行相同的计算(或者如果你有sql server或postgres,他们理解ctes)