【问题标题】:Database Relationships many to many 3 way confusion数据库关系多对多3方式混淆
【发布时间】:2012-07-30 16:43:47
【问题描述】:

我有一个简单的数据库,我想创建一个预订和调度系统。为了达到我的目标,它是开放的,所以不要担心如果需要改变设计。

以下是 ERD:

问题如下: 我有成员可以在特定时间(预订期)为特定地点(可预订区域)进行预订 当前的关系集允许许多成员在许多地方多次预订,并且这些可能重叠。即它允许双重预订或人们同时预订两个地方(即同时在两个地方)。

我的问题是,如何修改此数据库的设计以严格允许以下内容: 会员只能预订一个地方,但可以预订多次(即 9-9:30、9:30-10、10-10:30 等)。有效地允许会员预订一个地点和时间。一经预订,其他会员不得预订同一地点和时间。他们可以预订同一个地方但在不同的时间,或者在同一时间但不同的地方但不能同时预订。

我需要改变什么才能实现这一点。我宁愿只在关系中这样做,因为这很明显,但是,作为最后的手段,如果没有办法使用关系来做到这一点,我会求助于触发器,但我不想使用它们,因为它们不是像 erd 一样明显。

提前感谢您的帮助。

编辑 1:触发器和检查约束确实可以作为答案,我绝不会说这不是规范或不正确。如果这是我能做到的唯一方法,那么我将不得不这样做。但是,我之所以这样问,是因为我更愿意让其他程序员尽可能清楚地看到这种情况,所以这就是为什么我正在寻找一种通过关系来做到这一点的方法。但是,我确实接受这可能是不可能的。我只是不确定,所以这个问题。

编辑 2:我知道我说过我想在不使用约束或触发器的情况下执行此操作,但似乎如果不采取会使事情变得过于复杂的极端措施,就不可能做到这一点。但是,我确实喜欢过于复杂的答案,因为它显示了一些关于关系的真正好的想法。谢谢大家。

【问题讨论】:

  • 请注意,到目前为止所有的答案都包含唯一键。
  • 我注意到了!干杯! :-) 我将尝试每一个答案,最清楚的答案(对我来说)将得到公认的答案。但是,所有人都在正确的方向上提供了指导,所以我会为大家 +1 表示感谢。

标签: sql sql-server database sql-server-2008 relational-database


【解决方案1】:

如果您只想支持 30 或 15 分钟的间隔,我们可以执行以下操作。

周期表如下所示:

   ID     StartPeriod End Period
   1      0:00        0:15
   2      0:15        0:30 
   3      0:30        0:45 
   4      0:45        1:00 
   5      1:00        1:15

*等等

因此,如果您希望会员 1 在 2012 年 7 月 30 日午夜至凌晨 12:30 预订区域 1,预订描述为“睡眠”,则记录将如下所示

    BookingInfo
    Id    Description
    1     "Sleep"

    Bookings
    Id     MemberId PeriodID BookingInfoId BookableAreaId Day
    1      1        1        1             1              7/30/2012
    2      1        2        1             1              7/30/2012

然后,您可以在 Bookings 上放置两个唯一索引,其中一个为 MemberId,Day,PeriodId(这将防止它们被安排在同一天的同一时间)

还有一个用于 BookableAreaId、Day、PeriodId(这将防止某人在同一天以相同的 15 分钟间隔重复预订同一区域)

您还可以删除 Id 字段作为 Bookings 表上的主键,并将其中一个唯一索引替换为这些字段上的主键。

对不起,我是这个网站的新手。将 ERD 作为链接附加,因为我无法发布图片(直到获得足够的代表)

ERD

【讨论】:

    【解决方案2】:

    原始回复

    如果我理解正确,以下场景显示了允许和不允许的示例。

    Member            Book Time              Book Place
    -----------------------------------------------------
    John              0900-1000              Room 1
    John              1000-1100              Room 1
    John              1100-1200              Room 1
    John              0900-1000              Room 2        <--- not allowed, member double booked
    Jane              0900-1000              Room 1        <--- not allowed, room double booked
    

    因此,您想要强制的是两个不同组的唯一性。一组是会员和预订时间,另一组是预订时间和预订地点。

    您可以使用这些列在 Bookings 上创建两个唯一键,以强制任何组都不能重复。也就是说,具有重复 Book Time 的 Member 不能重复,因为它违反了唯一约束,而具有重复 Book Places 的 Book Time 不能重复。

    编辑:我看到您在这种情况下完全反对约束,所以我的回答可能毫无意义。

    EDIT2:我的解决方案假设您建立一个代理键作为Bookings 表的主键。很抱歉没有提到这一点。

    备选方案

    这是我想到的一个古怪想法。您可以维护 6 个表。

    Member
    BookTime
    BookPlace
    Member_BookTime
    BookPlace_BookTime
    Member_BookTime_BookTime_BookPlace
    

    好的,在你注销我之前,请听我说完!您的图表有 4/6 的这些表。我提议拆分成员、BookTimes 和 BookPlaces 之间的关系。

    下面有一个代理主键和一个复合唯一键,以保证在一段时间内没有成员被重复预订。

    Member_BookTime
    ---------------
    ID (PK)
    Member_ID (CUK)
    BookTime_ID (CUK)
    

    并且下面有一个代理主键和一个复合唯一键,以保证一次没有房间被重复预订。

    BookPlace_BookTime_ID
    ------------------
    ID (PK)
    BookPlace_ID (CUK)
    BookTime_ID (CUK)
    

    并且下面有一个复合主键,以及会员预订时间和房间预订时间之间的关系,最终将会员绑定到房间特定时间。

    Member_BookTime_BookTime_BookPlace
    ----------------------------------
    Member_BookTime_ID (CPK)
    BookPlace_BookTime_ID (CPK)
    

    这些有意义吗?在上述模式中,会员不能重复预订,房间不能重复预订。我意识到这可能是“过度规范化”,但它使关系更加清晰。

    建议的约束并不是真正“隐藏”的,因为它们显示在用于定义表的 SQL 中。我了解您希望从 ERD 中显示有关数据约束的所有内容,但有时约束过于复杂。

    【讨论】:

    • 您的建议是正确的。澄清一下,您是否建议在我的情况下,我在 bookings.bookingperiodid 和 bookings.bookableareaid 上放置唯一键,然后添加一个单独的主键来标识每个预订。如果是这样,您确定这将满足我的要求。同样,唯一键本质上是另一种形式的隐藏约束。我不反对,但只是不想使用。但是,如果没有别的办法,那我有什么选择,我确实接受这是常态。谢谢你的回答。
    • 回应原文:您将删除当前的主键设置,在 Bookings 上创建一个单独的自动递增主键 ID,在 Bookings.MembersId 和 Bookings.BookingPeriodID 上创建一个复合唯一键,以及最后是 Bookings.BookableAreasID 和 Bookings.BookingPeriodID 上的另一个复合唯一键。或者你可以看看我刚刚用一个古怪的新想法所做的编辑。
    【解决方案3】:

    我会像这样改变你的数据模型:

    BookingPeriods 将包含时间段列表。例如。您可以在早上和下午预订,这将使 StartTime 上午 9 点和 EndTime 下午 1 点。或者以一个小时为单位,这意味着记录上午 9 点到 10 点、上午 10 点到 11 点等。

    Create table BookingPeriods (
        id int primary key
      , StartTime Time
      , EndTime Time
      , Description varchar(max)
    )
    

    BookingAvailability 是时间段和日期之间的简单映射。所以你有一个每个日期的列表,其中的时间段是可能的

    Create table BookingAvailability(
        id int primary key
      , Day Date
      , PeriodId int references BookingPeriods(id)
        constraint uq_bookingAvailability unique (Day, PeriodId)
    )
    

    BookableAreas 只是成为物理位置的列表。

    Create table BookableAreas(
        id int primary key
      , Name varchar(100)
      , ActivityTypeId int
      , Description varchar(max)
    )
    

    成员保持不变

    Create table Members(
        id int primary key
      , FirstName varchar(100)
      , ...
     )
    

    然后,bookings 就变成了所有东西都汇集在一起​​的桌子。您每个可用性记录和区域的唯一记录。 memberid 只是记录的一些辅助信息。

    Create table Bookings(
        id int primary key
      , AvailabilityId int references BookingAvailability(id)
      , AreaId int references BookableAreas(id)
      , MemberId int references Members(id)
        constraint uq_Bookings unique (AvailabilityId, AreaId)
    )
    

    此数据模型将按如下方式使用:

    确保永远不要在此处的时隙中注册“重叠”。例如,如果您想要更细粒度的细节,您需要每小时创建一条记录。

    INSERT INTO BookingPeriods (id, StartTime, EndTime, Description)
    VALUES (1, '9:00 AM', '12:00 AM', 'Morning'), 
           (2, '1:00 PM', '4:00 PM', 'Afternoon')
    

    这是您可以预订的可能时间段列表。 (例如周末的日期不包括在内)

    INSERT INTO BookingAvailability(id, [Day], PeriodId)
    VALUES (1, '20120801', 1),
           (2, '20120801', 2),
           (3, '20120801', 1),
           (4, '20120801', 2),
           (5, '20120801', 1),
           (6, '20120801', 2)
    
    INSERT INTO BookableAreas (id, Name, ActivityTypeId, Description)
    VALUES (1, 'Ground Floor', 1, 'The ground floor room'),
           (2, 'East Wing Room', 1, 'The east wing room on the first floor'),
           (3, 'West Wing Room', 1, 'The west wing room on the first floor')
    
    INSERT INTO Members(id, FirstName)
    VALUES (1, 'Barak'),
           (2, 'Tony'),
           (3, 'George')
    

    在这里我们尝试创建实际的预订。

    INSERT INTO Bookings(id, AvailabilityId, AreaId, MemberId)
    VALUES (1, 3, 1, 1) -- Barak books the ground floor on 20120801 in the morning
    
    INSERT INTO Bookings(id, AvailabilityId, AreaId, MemberId)
    VALUES (2, 3, 1, 2)  -- Tony books the ground floor on 20120801 in the morning --> error
    
    INSERT INTO Bookings(id, AvailabilityId, AreaId, MemberId)
    VALUES (2, 4, 1, 2)  -- Tony books the ground floor on 20120801 in the afternoon --> ok
    

    【讨论】:

    • 感谢您的回答。很抱歉花了这么长时间发表评论,但我正在依次尝试其他答案。你已经看到了我和其他人没有看到的东西。我需要包括日期,否则相同的人将无法在不同的日期在相同的时间预订相同的区域。好眼力。只是一个简单的问题,您的建议是否允许我考虑日期(请注意,这不是接受答案的要求,但是由于您的远见,我倾向于接受您的答案)。如果没有,您能否建议如何解决此问题并保证接受。
    • 我也刚刚尝试完您的建议,但它根本不起作用。也许我做错了什么。但是在所有三个字段中,我都可以输入相同的 id 值并且没有问题,这与我开始时相同。不过,感谢您的时间和帮助。
    • 我得到了它的工作,但我不得不做一个小的修改。也许我没有正确阅读。我确实必须在 day 和 periodid 列的 bookingavailability 表上使用唯一约束。否则,这些时期将不是唯一的。在您上面的示例中,有人可以预订时段 1 3 和 5,虽然表中的不同时段与相同的时间段相关。我会接受你的回答,因为你在包括日子方面的远见,因此你省去了问另一个问题的麻烦。感谢您的帮助。
    • 我发现了一个问题。我不得不取消接受的答案,因为即使考虑了所有因素,它也无法满足问题中的一项要求。多个成员可以同时预订同一区域(双重预订)。有什么办法可以在不丢失其他东西的情况下解决这个问题。
    猜你喜欢
    • 2022-11-30
    • 2017-02-10
    • 2015-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-25
    • 1970-01-01
    • 2017-03-18
    相关资源
    最近更新 更多