1

我正在用Rails編寫一個新應用程序,所以我在每個表上都有一個id列。使用外鍵實施域約束的最佳做法是什麼?我將概述我的想法和挫敗感。如何在存在代理鍵時爲域約束定義複合外鍵?

下面是我想象的「The Rails Way」。這是我開始的。

Companies: 
    id: integer, serial 
    company_code: char, unique, not null 

Invoices: 
    id: integer, serial 
    company_id: integer, not null 

Products: 
    id: integer, serial 
    sku: char, unique, not null 
    company_id: integer, not null 

LineItems: 
    id: integer, serial 
    invoice_id: integer, not null, references Invoices (id) 
    product_id: integer, not null, references Products (id) 

的問題,這是從一個公司的產品可能會出現在一個不同的公司的發票。我向LineItems添加了一個(company_id:integer,不爲null),就像我只會使用自然鍵和連續字符一樣,然後添加一個組合外鍵。

LineItems (product_id, company_id) references Products (id, company_id) 
LineItems (invoice_id, company_id) references Invoices (id, company_id) 

這妥善限制了LineItem到一個單一的公司,但它似乎過度設計和錯誤的。 LineItems中的company_id是無關的,因爲代理外鍵在外表中已經是唯一的。 Postgres要求我爲引用的屬性添加一個唯一的索引,因此我在產品和發票中的(id,company_id)上創建了唯一索引,儘管id是唯一的。

下表自然鍵和一個串行發票號碼就不會有這種增加的複雜性,因爲引用的列都已經自然鍵,以便他們已經有一個唯一索引。

LineItems: 
    company_code: char, not null 
    sku: char, not null 
    invoice_id: integer, not null 

我可以忽略LineItems表中的代理鍵,但這也似乎是錯誤的。爲什麼在數據庫中有一個已經存在的整數可以使用時加入char?此外,完全按照上述要求,我需要將company_code(一種自然的外鍵)添加到Products和Invoices。

妥協...

LineItems: 
    company_id: integer, not null 
    sku: integer, not null 
    invoice_id: integer, not null 

不需要其它表自然外鍵,但它仍然是在焦炭在接合時有可用的整數。

有沒有乾淨的方式執行與外國鍵域約束像上帝打算,但在代理人的情況下,頭也不回的模式和索引到一個複雜的爛攤子?

回答

1

您提到的id在上面已經是唯一的,但是您必須使id,company_id具有唯一性,因爲您正在FK參考中使用它,需要確保它只引用1個唯一項目,而不是其他任何項目。

我知道你想「保護」的數據,但我會說這是過度設計和同意你的看法。任何使用此數據且無法正常工作的系統(爲公司提供正確的產品)將立即引起注意。編程時可能會增加處理更多數據的負擔,並可能使事情變得更加混亂。

我要保護的數據的努力,但成本可能增長,因爲你得到更多的數據,並肯定是有更復雜的數據模型的額外負擔是這樣高。

我認爲通過應用程序中的'自然'使用足以保護數據。當您開始創建新發票時,如果您正在查找要發票的產品,那麼您將使用相同的公司ID來搜索與創建發票相同的產品。如果你不知何故在那裏得到錯誤的ID(錯誤的代碼,直接在DB中的用戶),那麼不好的結果將立即顯現。

我不太瞭解您的帖子的第二部分。它似乎就像你試圖走下一條複雜的路徑,而不是創建一個簡單的規範化模式。

爲什麼你非常擔心非感應鍵被放入?你會讓用戶以非標準的方式訪問這些數據嗎?