简体   繁体   English

SQL 调度 - 超额预订报告

[英]SQL Scheduling - Overbooked Report

I need a way to view a given resource (in this case rooms/beds) that are overbooked.我需要一种方法来查看超额预订的给定资源(在本例中为房间/床位)。 Here's my table structure.这是我的表结构。 Sorry about the Hungarian notation:对不起匈牙利符号:

tblRoom房间
--RoomID --房间ID

tblBooking tblBooking
--BookingID --BookingID
--BeginDate --开始日期
--EndDate - 结束日期
--AssignedRoomID (foreign key) --AssignedRoomID(外键)

I don't have any non-working SQL to post here because I really don't know where to start.我没有任何不工作的 SQL 可以在这里发布,因为我真的不知道从哪里开始。 I'm using MS Access but I'm looking for a database agnostic solution if possible.我正在使用 MS Access,但如果可能的话,我正在寻找与数据库无关的解决方案。 It's OK to have to have to change some of the keywords to match the dialect of a given SQL engine but I'd like avoid using other features that are proprietary or only available in one RDBMS.必须更改一些关键字以匹配给定 SQL 引擎的方言是可以的,但我想避免使用其他专有功能或仅在一个 RDBMS 中可用。

I realize that it's best to avoid overbooking from the beginning but that's not the point of this question.我意识到最好从一开始就避免超额预订,但这不是这个问题的重点。

In case it's helpful, I posted a related question a couple days ago about how to find resources that are not yet booked for a given data range.如果它有帮助,我几天前发布了一个相关问题,关于如何查找尚未为给定数据范围预订的资源。 You can see that question here .你可以在这里看到这个问题。

Edit1:编辑1:
In reply to the answer below, I've modified your SQL slightly to make it work in Access as well as to be more accurate when it comes to detecting conflicts.在回复下面的答案时,我稍微修改了您的 SQL 以使其在 Access 中工作以及在检测冲突时更准确。 If I err not your solution posted below allows some conflicts to go unnoticed but also shows conflicts when a given Booking's EndDate and a different Booking's BeginDate fall on the same day, which is actually allowable and should not show as a conflict.如果我错了,您在下面发布的解决方案允许与 go 发生一些冲突而不引起注意,但当给定的 Booking 的 EndDate 和不同的 Booking 的 BeginDate 落在同一天时也会显示冲突,这实际上是允许的,不应显示为冲突。 Am I understanding this correctly or am I missing something here?我是否正确理解这一点,或者我在这里遗漏了什么?

SELECT
  *
FROM
  tblBooking AS booking
INNER JOIN
  tblBooking AS conflict
    ON  [conflict].AssignedRoomID = [booking].AssignedRoomID
    AND (([conflict].BeginDate >= DateAdd("d", -1, [booking].BeginDate) AND [conflict].BeginDate < [booking].EndDate)
    OR  ([conflict].EndDate > [booking].BeginDate AND [conflict].EndDate < [booking].EndDate))
    AND [conflict].BookingID <> [booking].BookingID

So, what you're looking for is any record in tblBooking for which there is another record with the same AssignRoomID for an overlapping period?那么,您要查找的是 tblBooking 中的任何记录,其中在重叠期间有另一条具有相同 AssignRoomID 的记录?

A naive solution would be...一个天真的解决方案是......

SELECT
  *
FROM
  tblBooking  [booking]
INNER JOIN
  tblBooking  [conflict]
    ON  [conflict].AssignedRoomID = [booking].AssignedRoomID
    AND [conflict].BeginDate     <= [booking].EndDate
    AND [conflict].EndDate       >= [booking].BeginDate
    AND [conflict].BookingID     != [booking].BookingID

The last condition stops a booking from being it's own conflict.最后一个条件阻止预订成为它自己的冲突。 It can also be changed to AND [conflict].BookingID > [booking].BookingID so that you don't get the conflict repeating.也可以将其更改为AND [conflict].BookingID > [booking].BookingID以免重复发生冲突。 (If A conflicts with B, you only get A,B and not B,A .) (如果 A 与 B 冲突,您只会得到A,B而不是B,A 。)


EDIT编辑

The issue with the above solution is that it does not scale very well.上述解决方案的问题在于它不能很好地扩展。 When searching for a Conflict, all bookings for that room Before the booking's EndDate are found, then filtered based on the EndDate.搜索冲突时,该房间的所有预订在预订的 EndDate 之前找到,然后根据 EndDate 过滤。 After a few years, that first search (hopefully using an Index) will return many, many records.几年后,第一次搜索(希望使用索引)将返回很多很多记录。

One optimisation is to have a maximum booking length, and only look that many days back in time for a conflict...一种优化是有一个最大的预订时长,并且只查看那么多天的冲突......

INNER JOIN
  tblBooking  [conflict]
    ON  [conflict].AssignedRoomID = [booking].AssignedRoomID
    AND [conflict].BeginDate     <= [booking].EndDate
    AND [conflict].BeginDate     >= [booking].BeginDate - 7     -- Or however long the max booking length is
    AND [conflict].EndDate       >= [booking].BeginDate
    AND [conflict].BookingID     != [booking].BookingID

By having wrapped a >= AND a <= around the [conflict].BeginDate , an index search can now quickly return a reasonably limitted number of records.通过在[conflict].BeginDate周围包裹>= AND a <= ,索引搜索现在可以快速返回合理有限数量的记录。

For bookings longer than the maximum booking length, they can be entered into the database as multiple bookings.对于超过最大预订长度的预订,可以将它们作为多个预订输入数据库。 That's where the art of optimisation comes in, it's often all about trade-offs and compromises:)这就是优化艺术的用武之地,它通常都是关于权衡和妥协:)


EDIT编辑

Another option, giving different details, would be to join the bookings against a calendar table.提供不同详细信息的另一种选择是将预订加入日历表。 (Having, for example, one record per day.) (例如,每天有一条记录。)

SELECT
  [room].RoomID,
  [calendar].Date,
  COUNT(*)                      AS [total_bookings],
  MIN([booking].BookingID)      AS [min_booking_id],
  MAX([booking].BookingID)      AS [max_booking_id]
FROM
  [calendar]
CROSS JOIN
  tblRoom     [room]
INNER JOIN
  tblBooking  [booking]
    ON  [booking].AssignedRoomID = [room].RoomID
    AND [booking].BeginDate     <= [calendar].Date
    AND [booking].EndDate       >= [calendar].Date
GROUP BY
  [room].RoomID,
  [calendar].Date
HAVING
  COUNT(*) > 1

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM