关系数据库:有一个引用关联 table 的外键可以吗?

Relational Databases: Is it fine to have a foreign key referencing an Associative table?

假设我们有乘客,其中乘客可以参加许多航班,而一个航班可能有很多乘客。在每个航班中,每个乘客都可以选择 he/she 可以观看的独特电影。

对于乘客、航班以及它们之间的关联,我们有三个 table:

Flights 
-----------
id
flight_number

Passengers
--------
id
name

PassengerFlights
---------------
passenger_id
flight_id

对于电影,它必须指定适用于哪个乘客和哪个航班。

在这种情况下,使用外键引用 PassengerFlights 联结点 table 是否可以,或者使用三个外键创建联结点 table 会更好吗(passenger_id, flight_id, movie_id)?例如:

选项 #1

MoviePassengerFlights
---------------------
movie_id
passenger_flight_id

选项 #2

MoviePassengerFlights
----------------------
movie_id
passenger_id
flight_id

我对似乎最直接的选项 #2 的担忧是,即使乘客和航班之间的关系被删除,它仍然是有效的记录。

对于此类用例,是否推荐选项 #1 或选项 #2?或者有更好的选择吗?

select取决于乘客购买电影的时间。如果他们只能在机上购买电影,那么选项 #2 就可以了,因为您不希望 passenger/flight 关系在航班开始后被删除。

另一方面,如果电影可以提前购买,如果乘客取消预订,你想退还购买的款项,那么选项 1 将保证你不能删除航班记录,如果电影购买仍然存在。

考虑是否可以在 passenger_flight_movie 中输入一行
该航班上没有的电影,或未在该航班上预订的乘客。这过于简单化了,但显示了限制。

-- Movie MOVIE_ID exists
--
movie {MOVIE_ID}
   PK {MOVIE_ID}


-- Flight FLIGHT_ID exists
--
flight {FLIGHT_ID}
    PK {FLIGHT_ID}


-- Passenger PASSENGER_ID exists.
--
passenger {PASSENGER_ID}
       PK {PASSENGER_ID}


-- Movie MOVIE_ID is available on flight FLIGHT_ID.
--
movie_flight {MOVIE_ID, FLIGHT_ID}
          PK {MOVIE_ID, FLIGHT_ID}
         FK1 {FLIGHT_ID} REFERENCES flight {FLIGHT_ID}
         FK2 {MOVIE_ID}  REFERENCES movie  {MOVIE_ID}


-- Passenger PASSENGER_ID is booked on flight FLIGHT_ID.
--
passenger_flight {PASSENGER_ID, FLIGHT_ID}
              PK {PASSENGER_ID, FLIGHT_ID}
             FK1 {FLIGHT_ID}    REFERENCES flight {FLIGHT_ID}
             FK2 {PASSENGER_ID} REFERENCES passenger {PASSENGER_ID}


-- Movie MOVIE_ID is available to passenger PASSENGER_ID
-- on flight FLIGHT_ID.
--
passenger_flight_movie {PASSENGER_ID, FLIGHT_ID, MOVIE_ID}
                    PK {PASSENGER_ID, FLIGHT_ID, MOVIE_ID}

                   FK1 {PASSENGER_ID, FLIGHT_ID} REFERENCES
      passenger_flight {PASSENGER_ID, FLIGHT_ID}

                   FK2 {MOVIE_ID, FLIGHT_ID} REFERENCES
          movie_flight {MOVIE_ID, FLIGHT_ID}

为了回答您的担忧,即即使乘客不再预订航班,m_p_f 记录仍可能保留 — 在 p_f table 中,将 (flight_id, passenger_id) table 的复合主键,并使用 ON DELETE CASCADE 规则将其引用为 m_p_f table 中的外键。那么如果删除了p_f中的记录,那么m_p_f中对应的记录也会被引擎自动删除。

junction/join table

在关系范式中,没有这样的东西。该术语是 Associative table。它 关联 两个父 table,并解析它们之间的多对多关系。 JOIN 是一个 SQL 动词。

The concern I'm having with Option #2, which seems the most straightforward, is that it remains a valid record even if the relationship between a Passenger and Flight gets deleted.

这是错误的。如果"the relationship between a Passenger and Flight gets deleted",乘客不再乘坐该航班,因此没有乘客可以为其预订电影的航班乘客。

更糟糕的是,如果乘客和航班之间的关系是 而不是 删除,您可以为任何旧航班保留任何旧乘客的电影......这是不正确的:电影预订应仅限于实际乘坐特定航班(PassengerFlight)的乘客。

我完全不明白那是怎么回事 "straight-forward"。

Would Option #1 or Option #2 be recommended for such use cases?

对于您声明的文件:

  • 选项 1 正确。
  • 选项 2 严重不正确。

这也是此类 "use cases" 的通用答案。

Is it fine to have a foreign key referencing an Associative table?

答案是肯定的。它在关系数据库中很常见,一旦你超过了几个 tables。每个 table 都是一个事实,每个关系都是两个事实之间的关系。 Associative table 只是一个 Fact,可以被任何从属 Fact 引用。

Or is there a better alternative?

是的。关系数据库。您声明的文件不是关系文件,它们是典型的 1960 年代、关系之前的记录归档系统,为方便起见部署在 SQL 中。这不完全是你的错:大量 "literature"; "theoreticians" 和其他作者销售的所有教科书,声称是 "relational" 实际上都是反关系的。他们所努力的就是他们所理解的:1960 年代,关系前的 RFS,"relational" 名存实亡。这是一场巨大的悲剧。关系数据库具有更高的完整性;力量;和速度。

这超出了问题的范围。有空再问问题,我会回答的。