不是您问题的直接答案,但您可能对如何在实际数据库中强制执行这种约束感兴趣......
假设您的航班上最多可以有 2 名飞行员。您可以简单地创建两个“0..N 到 0..1”的关系:
如果您只需要2 名飞行员,只需将PILOT1_ID和 PILOT2_ID 设为 NOT NULL(并通过 CHECK 确保它们不同)。
但是,正如您已经指出的那样,对于更大的基数,这很快就会变得笨拙,因此需要使用不同的技术。假设您需要将空乘人数限制为15人,您可以这样做......
...在联结表上具有以下约束:
CHECK (POSITION BETWEEN 1 AND 15)
请注意,在 {FLIGHT_ID, POSITION} 上有一个 UNIQUE 约束,如上U1
图所示。
从本质上讲,我们是每次飞行的特定职位的分类服务员。没有两个乘务员可以在同一个航班上占据相同的位置(感谢U1
),因此由于每个航班正好有 15 个位置,每个航班的乘务员不能超过 15 个。
不幸的是,没有很好的方法来强制所有职位都被填补,因此每个航班仍然可能少于 15 名乘务员。如果这很重要,您可能必须从应用程序代码中强制执行它。
- - 编辑 - -
要寻找一个空闲位置(用于下一个 INSERT),即使它是两个已填充位置之间的“洞”,您也可以执行以下操作(替换1
为所需的 FLIGHT_ID):
SELECT DISTINCT *
FROM (
SELECT POSITION + 1 FREE_POSITION
FROM FLIGHT_ATTENDANT
WHERE FLIGHT_ID = 1
UNION ALL
SELECT POSITION - 1 FREE_POSITION
FROM FLIGHT_ATTENDANT
WHERE FLIGHT_ID = 1
)
WHERE
FREE_POSITION NOT IN (
SELECT POSITION
FROM FLIGHT_ATTENDANT
WHERE FLIGHT_ID = 1
)
ORDER BY FREE_POSITION;
此查询可能有以下结果:
- 它可以不返回任何行,表示所有位置都是空闲的,因此只需选择有效范围内的任何一个。
- 它可能会返回行,其中第一行和最后一行可能超出有效范围,因此如果超出,请忽略它们。使用剩余的行之一。如果没有剩余的行,这表明所有位置都已被填满。
这是 Oracle 下的一个有效的 SQL Fiddle 示例,但同样的技术应该适用于任何 DBMS。有更优雅的、特定于 DBMS 的方法来执行这些类型的查询(特别是如果您想要所有空闲位置,而不仅仅是一些),但即使是这种“通用”解决方案对于大多数实际目的来说也应该绰绰有余......