2013-07-26 63 views
0

我想弄清楚什麼是最佳數據庫和表結構來存儲類型(var)char的節點之間的關係。我多年前最後一次使用MySQL作爲一些簡單的PHP網頁的後端,並且從未超越過。我希望一些經驗豐富的用戶可以給我他們的意見。節點關係的MySQL表

比方說,我有一大堆的名字:

  • 托馬斯
  • 菲利克斯
  • 馬克
  • 安妮

我現在想存儲他們的關係。我的想法是有可能看起來像這樣兩個表:

names (id, name)  relationships (id_1, id_2) 
0 Thomas    0 1 
1 Jane     0 3 
2 Felix     1 2 
3 Marc     3 4 
4 Anne     ... 
...      

數據的範圍如下:

  • 表「名稱」將包含約。 500萬行。
  • 表'關係'將包含150-200萬行。
  • 數據庫將只能由我本地訪問(服務器和客戶端是同一臺機器)
  • 我不需要Web服務器的響應能力,在我訪問它的幾個場合中只有很高的吞吐量(以減少等待時間)

我的問題是:

  • 我記得正確使用PRIMARY_KEY是很重要的。我隱約記得有可能將鍵分配到兩列(即id_1,id_2在我的情況);這有助於查詢我想象?
  • 有沒有辦法在MySQL內部防止在插入過程中創建重複關係(例如0:4 & 4:0)?
  • MySQL默認爲InnoDB。這是你會爲我的場景推薦的數據庫嗎?

任何指針歡迎。謝謝。

+1

你還沒有談到節點關係。他們是隨機分配多對多關係還是某種樹形結構?這種關係是否有方向性(即親子關係)?關於存儲引擎選擇,你可以談談你正在使用的數據訪問模式和MySQL版本嗎? –

+0

@Mike Brant我應該更具體,但沒有考慮到這些區別。在我的情況下,這種關係不是有方向性的,即我只需要存儲兩個節點是否連接。沒有任何類型的層次結構。每個表格將在一個長期會話中填充(首先是左側,然後是右側)(通過Python進行SQL查詢),然後保持這種狀態。稍後我會完整地讀出兩張表格,結合字符串及其關係。我可能會這樣做十幾次,直到我的最終產品令人滿意。 – Adrian

回答

0

首先,你需要考慮你的關係是否有與他們相關的「方向」。例如,「是......的孩子」的關係與其他方面相同的關係有相反的方向,「是父母的」。另一方面,「是兄弟姐妹」的關係是無向的(或者是雙向的,取決於個人的觀點)。

您描述的結構對於定向關係是完美的。

另一方面,雙向關係通常最好由故意地表示執行第二項bulletpoint中描述的複製;而這會消耗更多的存儲空間,它大大簡化了查詢,如「尋找X的所有兄弟姐妹」 —否則可能不得不採取兩個單獨的查詢的工會:

SELECT id_2 FROM my_table WHERE id_1=X 
UNION 
SELECT id_1 FROM my_table WHERE id_2=X 

因爲沒有指數的結果列上,如果想要對結果做更多​​的事情(比如按id排序,或者加入names表—,儘管在特定情況下可以在聯合之前執行聯接,但這只是增加了這些查詢可能會很慢數據操作代碼中的冗餘和複雜性)。

可以使用triggers確保每當寫入(插入,更新或刪除)關係到表示雙向關係的表時,都會自動對相反關係執行相同的操作。其次,你描述的表示被稱爲「鄰接表」,它非常簡單易懂。但是,在處理通過數據層次結構的深層搜索方面並不是很好,特別是在MySQL上(與其他RDBMS不同,它不支持遞歸函數)。因此,找到「X的所有後代」或「Y的所有祖先」實際上是相當困難的。其他數據模型,如「nested sets」或「transitive closure」對於這些任務來說更好。

隨着該序言說,對您的問題:

  • 我記得正確使用PRIMARY_KEY是很重要的。我隱約記得有可能將鍵分配到兩列(即id_1,id_2在我的情況);這有助於查詢我想象?

    有您relationship表四種可能的主鍵:

    • (id_1)

    • (id_2)

    • (id_1, id_2)

    • (id_2, id_1)

    根據定義,主鍵必須是你的表內唯一。事實上,它是主要識別記錄的手段。但是,如果需要的話,還可以定義更多的UNIQUE鍵,它們與主鍵具有相同的約束效果(差異相對較小並且超出了本答案的範圍):因此,實際上可以實施上述約束的任意組合。

    上述約束條件分別是:將每個名稱限制在關係的一側不超過一次;將每個姓名限制在關係的另一方不超過一次;並且最後兩個限制每個組合的名字在之間相同的關係不超過一次(差別僅僅是存儲索引的順序)。如果表格表示無向關係,那麼顯然第二個和第四個約束在語義上分別等同於第一個和第三個約束。

    一些例子:

    • ,如果你的表是「id_1是​​遺傳父親」,那麼id_1可能有許多兒童。所以(id_1)不可能是是主鍵,因爲它不會唯一標識擁有多個孩子的父親的記錄。另一方面,​​只能有一個遺傳父親(拋開胚胎學的進展),所以(id_2)唯一地標識一條記錄和可以是是主鍵(也就是說,這種多對一的關係可能是以及通過names表中的father_id列來模擬)。另外兩個(複合)鍵允許兒童有許多父親,因此必須是不正確的。

    • ,如果你的表是「id_1是​​父」,那麼這兩個家長可以有很多孩子孩子能有一個以上的父(這被稱爲一個多一對多的關係)。因此,前兩個約束是不正確的,必須在後兩個之間進行選擇(如前所述,差異僅僅是索引存儲的順序,因此MySQL在查找第二列之前必須找到第一列)。順便提一句,在這種情況下,人們可能會考慮在relationship表中添加一個額外的列,指出該關係表示哪個父代;如果一個孩子只能有一個父母,那麼可以將主鍵定義爲(child_id, parent_type)

    • ,如果你的表是「id_1和​​結婚」,那麼這兩個(id_1)(id_2)是「候選鍵」,因爲沒有人可以娶一個以上的其他人(至少在英國,一夫多妻制除外)。因此,人們可以定義(id_1),因爲主鍵定義第二UNIQUE鍵,而不是(id_2)。如前所述,人們可能希望將這些記錄放在—周圍的表格中,並且這些約束不會妨礙這一點。

  • 是否有從內部MySQL的一種方式,以防止重複關係的建立(如0:4 & 4:0)插入時?

    是的,人們可以使用觸發器:但是請注意上面關於雙向關係(常常需要這種「重複」)的說法。將實施這種約束觸發的一個例子是:

    CREATE TRIGGER rel_ins BEFORE INSERT ON relationships FOR EACH ROW 
    IF EXISTS (
        SELECT * FROM relationships WHERE id_1=NEW.id_2 AND id_2=NEW.id_1 
    ) THEN 
        SIGNAL SQLSTATE '45000' 
         SET MESSAGE_TEXT = 'Reverse relationship already exists'; 
    END IF;; 
    

    一個可能還需要「更新前」類似的觸發器。

    這種情況下,這種約束可能是可取的將是表中「父母」的地方,因爲父母不能是他們孩子的孩子(但在這種情況下,值得注意的是,在這樣的情況下一個關係表,實際上可能希望進一步去阻止全部循環—例如防止孩子成爲其祖父母的父母)。另一方面,「鄰接表」並不是執行這種約束條件的最佳模型,另一方面,純粹依靠其結構完全防止所有的循環。

  • MySQL默認爲InnoDB。這是你會爲我的場景推薦的數據庫嗎?

    InnoDB的最大優勢在於它完全兼容ACID,從而提供事務支持。如果您可能一次向多個位置寫入數據庫,這將特別有用。如果您只是將一堆靜態數據一次性加載到數據庫中以供後續查詢,它可能會比MyISAM慢一點。

+0

非常感謝您的詳細解答,它幫助我取得了進步! – Adrian