英文:
PostgreSQL query to get if a requested interval intersects with existing booked intervals
问题
标题一点都不清楚,这是因为很难解释我试图做什么,以及在查询期间或在后端执行它是否更快,甚至是否可能。
首先,我正在使用PostgreSQL。
主要问题是有会议室,它们将被预订。
有两种方法可以选择。存储每个房间的可用时间或存储房间的已预订时间(当然是按天计算)。
当我存储可用时间间隔时,使用简单的查询查找空闲时间非常容易。
例如,我有一个表格看起来像这样
CREATE TABLE "Calendar"
(
"id" bigserial PRIMARY KEY,
"calendar_day" date NOT NULL,
"room_id" integer NOT NULL,
"available_interval_start" time NOT NULL,
"available_interval_end" time NOT NULL,
"booked" boolean NOT NULL DEFAULT false
);
要找到一个可用时间间隔,我只需在日期、room_id 和间隔内的时间进行查询。简单又快速。
当订单涉及多个房间和日期时(因为可能会有折扣),我可以在一个事务内快速完成。通过在具有所需查询值的连接表中计数成功的结果,检查是否会成功也非常快。
类似于
select count(*) from "Calendar" as c
join (
values('2023-06-03'::date,5,'09:00'::time,'09:30'::time),
('2023-06-03'::date,5,'11:00'::time,'11:30'::time)
) as t (dd,room,st,en)
on c.calendar_day= t.dd
and c.room_id=t.room
and c.available_interval_start<=t.st
and c.available_interval_end<=t.st;
一切都很顺利。但现在我们必须考虑取消。在成功预订后,数据库中会有
a) 原始区间 [a,e] 作为已预订
b) [a,b] 和 [c,e] 作为空闲
c) 以及 [b,c] 作为已预订
与此同时,这些空闲时间间隔可以被预订并分段。即使它们没有被分段,如果取消了 [b,c],它也必须标记为已预订,然后必须收集并进行碎片整理,如果可能的话,标记为已预订,并将新的时间间隔插入数据库。
大量的账务处理。
现在第二种情况是我不存储可用时间间隔,而是存储已预订时间间隔。
在这种情况下,没有任何账务处理。如果取消了一个时间间隔,它将从数据库中删除(或标记为无效),不需要碎片整理。
CREATE TABLE "Calendar"
(
"id" bigserial PRIMARY KEY,
"calendar_day" date NOT NULL,
"room_id" integer NOT NULL,
"booked_interval_start" time NOT NULL,
"booked_interval_end" time NOT NULL,
"booked" boolean NOT NULL DEFAULT true
);
我在这种情况下遇到的问题是查询。我必须获取所有已预订的时间间隔,并对每个结果检查请求的时间间隔是否与已预订的时间间隔相交。这必须针对每天、每个房间和每个请求的时间间隔都进行,这意味着在购买事务中需要更多时间。
有没有关于第二种情况如何减少查询数量的想法?
感谢大家的任何想法。
英文:
The title is not clear at all, and that is because it is very hard to explain what I am trying to do and if it's faster to do it, if it's even possible, during the query or on the backend.
First of all, I am using PostgreSQL.
The main problem is having rooms, and they will be booked.
There are two ways to go. Store the available times per room or store the booked times for the room (per day, of course).
When I store availability intervals, finding empty slots is very easy with simple queries.
For example, I have a table that looks like that
CREATE TABLE "Calendar"
(
"id" bigserial PRIMARY KEY,
"calendar_day" date NOT NULL,
"room_id" integer NOT NULL,
"available_interval_start" time NOT NULL,
"available_interval_end" time NOT NULL,
"booked" boolean NOT NULL DEFAULT false
);
To find an available interval, I just have to query on day, room_id, and times inside the interval. Easy and fast.
When the order is for multiple rooms and days (since there might be a discount), I can do it quickly inside a transaction. It is also very fast to check if it will pass, by just counting the successful results, in a joined table that has the required queried values.
Something like
select count(*) from "Calendar" as c
join (
values('2023-06-03'::date,5,'09:00'::time,'09:30'::time),
('2023-06-03'::date,5,'11:00'::time,'11:30'::time)
) as t (dd,room,st,en)
on c.calendar_day= t.dd
and c.room_id=t.room
and c.available_interval_start<=t.st
and c.available_interval_end<=t.st;
Everything is roses. But now we have to consider canceling. After a successful booking, we will have in the database
a) the original interval [a,e] as booked
b) [a,b] and [c,e] as free
c) and [b,c] as booked
In the meantime, those free intervals can be booked and fragmented. Even if they are not, if [b,c] is cancelled, it has to be marked with booked = true, and then, all available intervals for the (day, room) have to be collected and defragmented, if possible, marked booked if required, and new intervals inserted in the database.
A lot of bookkeeping.
Now the second case is I do not store availability intervals but booked intervals.
In this case, there is zero bookkeeping. If an interval is cancelled, it will just be removed from the database (or marked as invalid), and there is no need for defragmentation.
CREATE TABLE "Calendar"
(
"id" bigserial PRIMARY KEY,
"calendar_day" date NOT NULL,
"room_id" integer NOT NULL,
"booked_interval_start" time NOT NULL,
"booked_interval_end" time NOT NULL,
"booked" boolean NOT NULL DEFAULT true
);
The problem I have in this case is querying. I have to get all booked intervals, and for every result, check if the requested interval intersects with a booked interval. And this has to be done for every day, room, and requested interval, which means more time spent during the purchase transaction.
Any ideas on which is the best way to go?
Is there a way to reduce the number of queries in the second case?
Thanks to everyone for any ideas.
答案1
得分: 1
开始实现时间戳范围(tsrange或tstzrange),当需要时也需要时区。使用时间戳范围,您可以创建约束,查找重叠,查找可用时间段等等。一旦开始将它们整合到您的工作流程中,您会发现很难相信曾经在没有范围的情况下工作。
链接:https://www.postgresql.org/docs/current/rangetypes.html
英文:
Do yourself a big favor and start implementing the timestamp range, the tsrange or tstzrange when the timezone is needed as well. Using a timestamp range you can create constraints, look for overlap, look for available time slots, etc. etc. Once you start integrating them into your workflow, you'll find it hard to believe that you once worked without ranges.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论