2009-02-11 74 views
2

我有一個棘手的問題,我一直在搞了幾天,現在找不到最佳的解決方案。棘手的關係數據庫設計問題

這些都是我的表:

  • 網站
  • site_node

該網站節點表包含代表一個分層結構(使用嵌套組)節點的列表。每個節點必須有一個或多個關聯的頁面。每個站點必須有一個關聯的錯誤頁面和一個未找到的頁面。

因此,一個頁面必須屬於一個節點,或者一個網站作爲錯誤或未找到頁面。我目前正在醞釀的解決方案是:在頁表,其中類型要麼是「節點」,「site_error」或「site_notFound」和ID將是該網站或節點

  1. ParentType的和parentId的領域id(取決於類型)。
  2. 頁表上可以爲null的nodeId字段,然後是站點表上的errorPageId和notFoundPageId字段。

選項#1確保每個頁面屬於一個並且只有一個其他實體,但由於parentId字段可指向多個位置,所以實際上不會強制關係。

選項#2更清晰,但它基本上是說該網站「屬於」兩個錯誤和未找到的頁面,這可能是不好的做法。

任何想法或建議?
謝謝,
傑克

回答

2

選項#1確保每個頁面 屬於一個且僅一個其它 實體,儘管這種關係斜面 實際上被執行作爲parentId的 字段可以指向一個以上的 地方。

沒錯。就關係理論而言,問題在於您的「parentId」列違反了third normal form,因爲它的含義根據parentType(非關鍵列)中的值每行不同而不同。

如果單列可能包含某人的電話號碼他們的出生日期,每行取決於其他標誌,那麼您將沒有設計合適的數據庫。這是關於這個人的兩個不同的事實,他們每個人都應該擁有自己的專欄。同樣,將site_id或node_id存儲在單個列中也會遇到同樣的問題。

這是一個有缺陷的設計的另一個線索是,您不能聲明一個外鍵約束指向或兩個引用表的

選項#2是清潔的,但它的基本 說,該網站 「屬於」兩個錯誤,而不是 找到的頁面,這可能是壞 實踐。

我看到你爲什麼這麼說,因爲屬於Rails中類似的框架約定。但這些都是公約;它們不一定是外鍵可以建模的唯一關係。您可以讓一個實體參考另一個實體,在中有一個的關係。在這種情況下,外鍵反轉方向。

我想說這是錯誤頁面和未找到頁面屬於該網站在邏輯上是正確的,而不是相反。而使它們成爲強制的方法是讓另一個實體引用這些頁面,並將NOT NULL約束應用於這些引用。這就是你所描述的。

CREATE TABLE site (
    . . . 
    error_page_id  INT NOT NULL, 
    notfound_page_id INT NOT NULL, 
    FOREIGN KEY (error_page_id) REFERENCES pages (page_id), 
    FOREIGN KEY (notfound_page_id) REFERENCES pages (page_id) 
); 

這滿足您的直接需求,它是可執行的,它是以正常形式。


@NXC suggests爲「錯誤」和「未找到」頁面創建虛擬節點。儘管這允許將這些節點存儲在節點層次結構中,但它不能強制網站必須具有這些頁面。也就是說,一個站點可以被存儲在而沒有對這些節點的引用。

@Tony Andrews suggests在每頁中存儲兩列,site_idsite_node_id,並添加一個CHECK約束以確保其中的一個非NULL。這似乎比parent_id/parent_type選項更好,但它仍不能提供任何強制措施,即每個站點都必須有「錯誤」和「未找到」頁面。

+0

謝謝,這確實是最有意義的。由於頁面無法通過級聯刪除與該關係(因爲FK被反轉),您會建議使用觸發器來刪除鏈接的錯誤頁面,還是其他? – 2009-02-11 17:40:36

3

使虛擬站點節點爲錯誤或找不到頁面。根據您的第一個選項,您可以將它們標記爲特定類型的節點。這將使製作通用處理機制變得更容易。它也將使聯接更簡單,這將有助於數據庫查詢性能。此外,它允許您添加更多類型的「特殊」頁面(可能是登錄屏幕),或者無需修改數據庫模式即可進行配置。

0

另一種選擇是有2列SITE_ID和site_node_id這樣的:

create table pages 
(page_id ... primary key 
, site_id references sites 
, site_node_id references site_nodes 
, ... 
, constraint site_or_node check ( site_id is null and site_node_id is not null 
           or site_id is not null and site_node_id is null 
           ) 
); 

現在你可以使用引用完整性,以確保每一頁屬於任何一個網站或一個節點,而不是兩個。

0

選項2更有意義,如果出現更多併發症,將會在晚些時候挽救您的大腦。站點與錯誤/未發現頁面的一對一關係使其適用於外鍵約束。

0

對選項1的修改。

包含兩個單獨的列ParentNodeID和ParentSiteID。根據情況,將這兩列中的一列留空。現在您仍然可以爲每個外鍵聲明一個外鍵(引用)約束。

我不太瞭解SiteNotFound案例。在這種情況下,你可以將兩個外鍵保留爲NULL嗎?

你的加入和搜索會更簡單。你也將堅持1NF。這不是巧合。

您的選項1將從不同域中繪製的值組合在一個字段中。這是不好的現場設計,IIRC違反了1NF。