2012-03-20 156 views
35

我相信標題是不言自明的。你如何在PostgreSQL中創建表結構來建立多對多的關係。如何在PostgreSQL中實現多對多的關係?

我的例子:

Product(name, price); 
Bill(name, date, Products); 
+1

查詢不建立m-t-m關係 - 表結構。 – 2012-03-20 15:30:16

+0

我沒有很好地表達自己,我該如何定義我的表格結構? – 2012-03-20 15:32:04

+1

從帳單表中刪除產品,創建一個名爲「bill_products」的新表,其中有兩個字段:一個指向產品,一個指向帳單。使這兩個字段成爲這個新表的主鍵。 – 2012-03-20 15:34:56

回答

164

DDL語句可能看起來像這樣:

CREATE TABLE product (
    product_id serial PRIMARY KEY -- implicit primary key constraint 
, product text NOT NULL 
, price  numeric NOT NULL DEFAULT 0 
); 

CREATE TABLE bill (
    bill_id serial PRIMARY KEY 
, bill  text NOT NULL 
, billdate date NOT NULL DEFAULT CURRENT_DATE 
); 

CREATE TABLE bill_product (
    bill_id int REFERENCES bill (bill_id) ON UPDATE CASCADE ON DELETE CASCADE 
, product_id int REFERENCES product (product_id) ON UPDATE CASCADE 
, amount  numeric NOT NULL DEFAULT 1 
, CONSTRAINT bill_product_pkey PRIMARY KEY (bill_id, product_id) -- explicit pk 
); 

我做了一些調整:

  • N:M關係是通常由單獨的表格執行 - bill_product是這樣的。

  • 我加serial列爲代理主鍵。我強烈建議,因爲產品的名稱不是唯一的。此外,在外鍵中強制實施唯一性和引用列對於使用4字節的integer比使用存儲爲textvarchar的字符串要便宜得多。
    在Postgres 10或更高版本中,請改爲使用IDENTITY column。詳細信息:

  • 不要使用基本數據類型的名稱,如date標識符。雖然這是可能的,但它是不好的風格,並導致錯誤和錯誤消息混淆。使用​​。如果可以的話,切勿使用reserved words並避免使用雙引號混合大小寫標識符。

  • name不是一個好名字。我將表productname列重命名爲product。這是一個更好的命名約定。否則,當你在一個查詢中加入幾個表時 - 你在關係數據庫中做很多 - 你最終會得到多個名爲name的列,並且必須使用列別名來整理混亂。這沒有幫助。另一個廣泛的反模式將只是id作爲列名。
    我不確定bill會是什麼名字。在這種情況下,可能bill_id可以是名稱

  • price數據類型numeric來存儲小數精確地輸入(任意精度類型,而不是浮點型)。如果你專門處理整個號碼,那就做integer。例如,您可以將價格節省作爲分銷商

  • amount"Products"在您的問題)進入鏈接表bill_product並且類型numeric以及。如果你專門處理整數,integer

  • 你看到外鍵bill_product?我創建了兩個級聯更改(ON UPDATE CASCADE):如果product_idbill_id應該更改,則更改會級聯到bill_product中的所有相關條目,並且不會有任何中斷。
    我還使用ON DELETE CASCADE代替bill_id:如果您刪除帳單,詳細信息將隨之刪除。
    並非如此產品:您不想刪除在帳單中使用的產品。如果你嘗試這個,Postgres會拋出一個錯誤。您可以將另一列添加到product以標記廢棄的行。

  • 在這個基本的例子中的所有列最終是NOT NULL,所以NULL值是不允許的。 (是的,全部列 - 主鍵中使用的列自動定義爲UNIQUE NOT NULL)。這是因爲NULL值在任何列中都沒有意義。它使初學者的生活更輕鬆。但你不會輕易離開,無論如何你需要了解NULL handling。附加列可能允許NULL價值,功能和連接可以查詢等

  • 閱讀CREATE TABLE in the manual章介紹NULL值。

  • 主鍵在關鍵列上使用唯一的索引實現,這使得快速查詢PK列中的條件。但是,鍵列的順序與多列鍵相關。由於bill_product上的PK位於(bill_id, product_id),在我的示例中,如果您有查詢給定product_idbill_id的查詢請求,您可能需要在product_id(product_id, bill_id)上添加另一個索引。詳細信息:

  • 閱讀chapter on indexes in the manual

+22

主席先生,爲了提高答案的質量,即使在幾周後,你也絕對不會回來多次。我感謝你成爲社區的忠誠貢獻者! – 2014-12-05 12:11:53

+3

@RaduGheorghiu:謝謝。我多次提到這個答案,所以我一直在提煉它。 – 2014-12-05 12:33:37

+0

如何爲映射表「bill_product」創建索引?通常它看起來應該是這樣的:'CREATE INDEX idx_bill_product_id ON booked_rates(bill_id,product_id)'。這是正確的嗎? – codyLine 2015-01-05 23:46:04

相關問題