简体   繁体   中英

SQL Table Design - Storing multiple legs of a trip

I want to create a database to store the legs of a trip, where each leg has a FK in another table the trip identifier will be the key/unique.

Eg "'East coast roadtrip': Boston -> NYC, NYC -> Philly, Philly -> Baltimore, Baltimore -> DC, DC -> Raleigh"

Later on, I would like the run queries such as,

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

I am a bit stumped as to how to effectively store such trip information. A trip identifier key and storing the trip legs as a plain text column probably isn't the most efficient solution.

Would appreciate any tips on how to approach this.

It sounds rather simple.

You want a trip table, with a trip_id, perhaps a label such as "East coast roadtrip", perhaps dates, who took the trip, departure date/time, or whatever.

You probably want a node table, to store the cities ("Boston", "Philly", etc) or whatever places are your begin and end points of each leg. So this would contain a node_id and its name or label.

Each leg of the trip joins two nodes. You want a trip_leg table, containing the trip_id, from_node_id, and to_node_id. You might possibly want other info here such as date/time arrival at your destination.

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

I would create the following:

A Location Table which has each location possible and an ID value

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

A Leg Table which includes each leg of the trip. It has an ID for the Leg with a Location ID for both the Origin and Destination which act as a foreign key to the 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)

A Trip table which contains each Leg of the Trip and is a Trip ID and basic details:

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

A TripLeg table which joins the Trip and Leg details with a TripID and 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)

This will allow you to query based on city, individual leg or total trip. Hope this helps.

Assuming your trips are not "one offs" but instead are conducted on predetermined lines (and a single line can generate multiple trips), then you'll need something like this:

在此输入图像描述

(If they are one-offs, just imagine LINE is the trip.)

Pay attention to the structure of LEG:

  • Its PK contains LEG_NO, but doesn't contain STOP_ID: the LEG_NO determines the order of legs in the given line, and also allows multiple legs to traverse the same stop, if necessary (eg on a round-trip).
  • Also, there is only "starting" (and not "ending") stop in the leg - whatever is the "previous" (as defined by LEG_NO) leg determines the starting stop for the next leg. This way, you can never have the disconnected legs (ie where previous leg's ending stop doesn't match next leg's starting stop).

The TRIP's PK contains TRIP_NO instead of (for example) START_DATE_TIME, to allow multiple trips to be started on the same line at the same time, should you ever need that.

A 23-rd trip on your example 'East coast roadtrip' line could be represented like this:

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'

(NOTE: I intentionally used non-sequential numbers to flesh-out the connections more clearly.)


With this database structure, you can easily get trips that went through all of the given stops, for example:

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
    )

(NOTE: On older versions of MySQL, you'd want to rewrite this query as JOIN due to the problems query optimizer had with IN.)

However, if you want to get trips that went through these stops in that order and with no "gaps" in-between, thighs get hairy in a hurry. Probably your best bet is to get the results of the sub-query above and analyze them client-side, rather than trying to establish the order and detect gaps in SQL (which is fundamentally set-based).

Have a trip table, which has a 1:many relationship with leg . The leg will contain to and from foreign keys to location .

You can then do your query by doing a SELECT from as many leg s as you like, each one being aliased as a different name, and ensuring that they all have the same trip_id .

Maybe something like:

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'

All those extra location joins are expensive however, so you may wish to look up the primary keys for your various locations before this query, and then you can just add your WHERE clauses to the leg tables.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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