在关系数据库中表示受约束的多对多关系的最佳方法是什么?

时间:2011-07-31 01:13:00

标签: database relationship

我想建立一个多对多关系,其约束条件是,任何时候只能在关系的每一侧链接一个或没有实体。

这个问题的一个很好的比喻是汽车和停车库空间。有很多车和很多空间。一个空间可以包含一辆车或是空的;一辆车一次只能在一个空间内,或者没有空间(不能停放)。

我们有一个Cars表和一个Spaces表(可能还有一个链接表)。 cars表中的每一行代表汽车的唯一实例(具有许可证,所有者,型号等),Spaces表中的每一行代表一个独特的停车位(车库楼层,行和数字的地址)。在数据库中链接这些表并强制执行上述约束的最佳方法是什么?

(我正在使用C#,NHibernate和Oracle。)

3 个答案:

答案 0 :(得分:2)

如果您不担心历史 - 即只担心“现在”,请执行以下操作:

create table parking (
  car_id references car,
  space_id references space,
  unique car_id,
  unique space_id
);

通过使汽车和空间参考都是独一无二的,您可以将每一侧限制为最多一个链接 - 即汽车最多可以停放在一个空间中,并且一个空间最多可以停放一辆汽车。

答案 1 :(得分:1)

在任何关系数据库中,多对多关系必须有一个连接表来表示组合。正如答案中所提供的(但没有太多理论背景),如果没有中间的表来存储所有组合,则无法表示多对多的关系。

解决方案中还提到,如果您不需要历史记录,它只能解决您的问题。相信我,当我告诉你真实世界的应用程序几乎总是需要代表历史数据。有很多方法可以做到这一点,但一个简单的方法可能是创建一个所谓的三元关系与一个额外的表。理论上,您可以创建一个“时间”表,该表还将其主键(例如不同的时间戳)与其他两个源表的继承键相链接。这将使您能够防止两辆车在同一时间位于同一停车位的错误。使用时间表可以让您使用简单的整数id重复使用多个停车位的相同时间数据。

因此,您的数据表可能如此

桌车 car_id(整数/数字索引最快) ......

桌子停车位 space_id 位置

表时间段 time_id整数 begin_datetime(除非必须,否则不要使用秒!) end_time(除非必须,否则不要使用秒!)

现在,这里变得有趣。您可以使用由car.car_id + parking_space.space_id + time_id组成的复合主键添加中间表。还有其他一些你可以添加来优化的东西,但我希望你能得到这个想法。

表预订 car_id PK parking_space_id PK time_id PK(它是一个整数 - 只是尝试保持它尽可能高度粒度 - 30分钟增量或其他 - 如果你允许这包括秒/毫秒/等优点被取消,因为你不能重复使用它时间表中的值) (这也是存储与特定账户,预订等不同的可变利率,折扣等的地方。)

现在,您可以减少数据量,因为您没有复制连接表(预留)中的时间戳。通过使用整数,您可以将该时间段重新用于多个停车位,但您也可以应用约束,以防止两辆汽车在给定的日期/时间范围内为同一“时段”租用该给定地点。这也可以更容易地存储关于客户的一些历史记录 - 谁知道,您可能希望查看有关更频繁租用并为其提供折扣的客户的报告。

通过使用三元关系模型,您可以使每个点对给定时间段唯一(可能具有一些添加的验证规则),因此系统只能在一个给定时间段内将一辆车存储在一个停车位中。

通过使用整数作为键而不是时间戳,可以确保数据库不需要做任何繁重的操作来索引键和排序/查询。当您拥有大量数据集并且需要高效时,这是数据仓库/ OLAP报告中的常见做法。我认为这也适用于此。

答案 2 :(得分:0)

创建第三个表。

parking
--------
car_id
space_id
start_dt
end_dt

对于约束,我猜您的情况问题是您需要针对交集表本身检查复杂规则。如果您在触发器中尝试此操作,它将报告突变。

避免这种情况的一种方法是复制表,并针对约束查询此复制。