繁体   English   中英

SQL表设计 - 存储行程的多个分支

[英]SQL Table Design - Storing multiple legs of a trip

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

例如“'东海岸自驾游':波士顿 - >纽约,纽约 - >费城,费城 - >巴尔的摩,巴尔的摩 - > DC,DC - >罗利”

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

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

关于如何有效地存储这样的旅行信息,我有点难过。 行程标识符密钥并将行程支路存储为纯文本列可能不是最有效的解决方案。

非常感谢有关如何处理此问题的任何提示。

听起来很简单。

你想要一个旅行表,有一个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")

我会创建以下内容:

具有可能的每个位置的位置表和ID值

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

腿表包括旅行的每一段。 它具有Leg的ID,其Origin和Destination的位置ID充当Location Table的外键

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)
);

一个TripLeg表,它将Trip和Leg细节与TripID和LegID连接起来

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)

这将允许您根据城市,个人腿或总旅行进行查询。 希望这可以帮助。

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

在此输入图像描述

(如果他们一次性的,想象一下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中的差距(基本上是基于集合的)。

有一个trip桌,与leg有1:多的关系。 专家组将包含tofrom外键location

然后,您可以做一个做你的查询SELECT从尽可能多的leg S作为你喜欢,每个人被别名为一个不同的名称,并确保它们都具有相同的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子句添加到leg表中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM