簡體   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