3

我想创建一个数据库来存储行程的行程,其中每条行程在另一个表中都有一个 FK,行程标识符将是键/唯一。

例如“'东海岸公路旅行':波士顿 -> 纽约市,纽约市 -> 费城,费城 -> 巴尔的摩,巴尔的摩 -> DC,DC -> 罗利”

稍后,我想要运行查询,例如,

"Which trips contain the NYC -> Philly and Philly -> Baltimore legs?"

对于如何有效地存储此类旅行信息,我感到有些困惑。行程标识符键并将行程段存储为纯文本列可能不是最有效的解决方案。

将不胜感激有关如何解决此问题的任何提示。

4

4 回答 4

2

听起来很简单。

您想要一个带有trip_id 的行程表,可能是诸如“东海岸公路旅行”之类的标签,也可能是日期、旅行的人、出发日期/时间或其他任何内容。

您可能需要一个节点表来存储城市(“波士顿”、“费城”等)或任何地方作为您每条腿的起点和终点。所以这将包含一个 node_id 及其名称或标签。

行程的每一段连接两个节点。您需要一个trip_leg 表,其中包含trip_id、from_node_id 和to_node_id。您可能需要其他信息,例如到达目的地的日期/时间。

  SELECT t.label
    FROM trips as t
    INNER JOIN trip_legs as x1  ON (t.trip_id = x1.trip_id)
    INNER JOIN trip_legs as x2  ON (t.trip_id = x2.trip_id 
                                AND x1.to_node_id = x2.from_node_id)
    WHERE x1.from_node_id IN (SELECT node_id FROM nodes WHERE name = "NYC")
      AND x1.to_node_id   IN (SELECT node_id FROM nodes WHERE name = "Philly")
      AND x2.to_node_id   IN (SELECT node_id FROM nodes WHERE name = "Baltimore")
于 2013-03-23T00:52:52.030 回答
2

我将创建以下内容:

一个位置表,其中包含每个可能的位置和一个 ID 值

CREATE TABLE Location(
    LocationID int NOT NULL AUTO_INCREMENT,
    Location nchar(10) NOT NULL,
    PRIMARY KEY 
(LocationID) 
);

行程表,包括行程的每一段。它有一个腿的 ID,以及作为位置表的外键的起点和终点的位置 ID

CREATE TABLE Leg(
    LegID int NOT NULL AUTO_INCREMENT,
    Origin int NOT NULL,
    Destination int NOT NULL,
PRIMARY KEY(LegID) 
);

FOREIGN KEY(Origin) REFERENCES Location(LocationID)

FOREIGN KEY(Destination) REFERENCES Location(LocationID)

包含行程的每条线路的行程表,是行程 ID 和基本详细信息:

CREATE TABLE Trip(
    TripID int NOT NULL AUTO_INCREMENT,
PRIMARY KEY (TripID)
);

将 Trip 和 Leg 详细信息与 TripID 和 LegID 连接起来的 TripLeg 表

CREATE TABLE TripLeg(
    LegID int NOT NULL,
    TripID int NOT NULL,
PRIMARY KEY (LegID ,TripID)
);

FOREIGN KEY(LegID) REFERENCES Leg(LegID)
FOREIGN KEY(TripID) REFERENCES Trip(TripID)

这将允许您根据城市、单程或总行程进行查询。希望这可以帮助。

于 2013-03-23T01:04:03.560 回答
2

假设您的旅行不是“一次性”,而是在预定线路上进行(并且一条线路可以产生多次旅行),那么您将需要这样的东西:

在此处输入图像描述

(如果它们一次性的,想象一下 LINE 就是一次旅行。)

注意LEG的结构:

  • 它的 PK 包含 LEG_NO,但不包含 STOP_ID:LEG_NO 确定给定线路中支线的顺序,如果需要(例如在往返行程中),还允许多条支线穿过同一个停靠点。
  • 此外,在腿中只有“开始”(而不是“结束”)停止 - 无论“上一个”(由 LEG_NO 定义)腿是什么,都决定了下一个腿的开始停止。这样,您永远不会有断开的腿(即,前腿的结束停止点与下腿的开始停止点不匹配)。

TRIP 的 PK 包含 TRIP_NO 而不是(例如)START_DATE_TIME,以允许在同一时间在同一条线路上开始多个行程,如果您需要的话。

您的示例“东海岸公路旅行”线上的第 23 次旅行可以表示如下:

TRIP: LINE_ID  TRIP_NO
      -------  -------
          100       23

LINE: LINE_ID  LINE_NAME
      -------  ---------
          100  'East coast roadtrip'

LEG:  LINE_ID  LEG_NO  STOP_ID
      -------  ------  -------
          100       1       55
          100       2       11
          100       3       66
          100       4       22
          100       5       44
          100       6       33

STOP: STOP_ID  STOP_NAME
      -------  ---------
           22  'Baltimore'
           11  'NYC'
           33  'Raleigh'
           66  'Philly'
           55  'Boston'
           44  'DC'

(注意:我故意使用非连续数字来更清楚地充实连接。)


使用此数据库结构,您可以轻松获取经过所有给定站点的行程,例如:

SELECT *
FROM TRIP
WHERE
    LINE_ID IN (
        SELECT LINE_ID
        FROM LEG JOIN STOP ON LEG.STOP_ID = STOP.STOP_ID
        WHERE STOP_NAME IN ('NYC', 'Philly', 'Baltimore')
        GROUP BY LINE_ID
        HAVING COUNT(DISTINCT STOP_ID) = 3
    )

(注意:在旧版本的 MySQL 上,由于查询优化器对 IN 的问题,您希望将此查询重写为 JOIN。)

但是,如果您想按顺序通过这些站点并且中间没有“间隙”,那么大腿会很快长毛。可能你最好的选择是获取上面子查询的结果并在客户端分析它们,而不是尝试建立顺序并检测 SQL 中的间隙(这基本上是基于集合的)。

于 2013-03-23T12:51:08.293 回答
1

有一个trip表,它与 1:many 关系leg。腿将包含tofrom外键location

然后,您可以通过执行任意数量的 s 来进行查询SELECTleg每个都被别名为不同的名称,并确保它们都具有相同的trip_id.

也许是这样的:

SELECT
    trip.name
FROM
    trip
    INNER JOIN leg leg1 ON (trip.id = leg1.trip_id)
    INNER JOIN leg leg2 ON (trip.id = leg2.trip_id)
    INNER JOIN location location_from1 ON (
        location_from1.id = leg1.location_from_id
    )
    INNER JOIN location location_to1 ON (
        location_to1.id = leg1.location_to_id)
    )
    INNER JOIN location location_from2 ON (
        location_from2.id = leg2.location_from_id
    )
    INNER JOIN location location_to2 ON (
        location_to2.id = leg2.location_to_id
    )
WHERE
    location_from1.name = 'NYC'
    AND location_to1.name = 'Philly'
    AND location_from2.name = 'Philly'
    AND location_to2.name = 'Baltimore'

然而,所有这些额外的位置连接都很昂贵,因此您可能希望在此查询之前查找各个位置的主键,然后您只需将WHERE子句添加到腿表中即可。

于 2013-03-23T00:52:31.027 回答