2017-10-11 135 views
4

我造型在F#的應用程序,我遇到試圖建構以下遞歸式數據庫表時有困難:F#的遞歸類型的SQL表

type Base = 
    | Concrete1 of Concrete1 
    | Concrete2 of Concrete2 
and Concrete1 = { 
    Id : string 
    Name : string } 
and Concrete2 = { 
    Id : string 
    Name : string 
    BaseReference : Base } 

的解決方案,我已經得到了瞬間(在這裏我找到了靈感http://www.sqlteam.com/article/implementing-table-inheritance-in-sql-server)是:

enter image description here

我有兩個問題與此解決方案:

  1. 即使在我的模型中沒有意義,基表上仍會有行。但我可以忍受這一點。

  2. 似乎查詢所有有關Concrete2的BaseReference的信息將會很複雜,因爲我將不得不考慮類型的遞歸和不同的具體表。而且,向模型添加新的具體類型必須修改這些查詢。當然,除非在SQL中有與match F#關鍵字等效的內容。

我是否擔心這些問題太多?或者,有沒有更好的方法來模擬SQL表中的這種遞歸F#類型?

+1

在我看來,你的類型Base大致相當於(Id,Name)元組的非空列表?是這樣嗎?還是你調整了這個例子來問這個問題? –

+1

@RobertNielsen:起初,我修改了這個例子以問一個問題,但現在我想我會像這樣(一個(Id,Type)列表),我將創建另一個表來包含具體類型的公共信息,一個基礎信息表,每一個具體的表將用一個外鍵引用。我這樣做是因爲否則基表將在行之間包含相同的信息。 – Mario

回答

0

1部分:編碼Algrebraic數據類型在關係表

我掙扎着這事很多次。我終於發現了在關係表中建模代數數據類型的關鍵:Check constraints

使用檢查約束,您可以爲多態類型的所有成員使用公共表,但仍然強制實施每個成員的不變量。

考慮下面的SQL架構:

CREATE TABLE ConcreteType (
    Id TINYINT NOT NULL PRIMARY KEY, 
    Type VARCHAR(10) NOT NULL 
) 
INSERT ConcreteType 
VALUES 
(1,'Concrete1'), 
(2,'Concrete2') 

CREATE TABLE Base (
    Id INT NOT NULL PRIMARY KEY, 
    Name VARCHAR(100) NOT NULL, 
    ConcreteTypeId TINYINT NOT NULL, 
    BaseReferenceId INT NULL) 

GO 

ALTER TABLE Base 
ADD CONSTRAINT FK_Base_ConcreteType 
FOREIGN KEY(ConcreteTypeId) 
REFERENCES ConcreteType(Id) 

ALTER TABLE Base 
ADD CONSTRAINT FK_Base_BaseReference 
FOREIGN KEY(BaseReferenceId) 
REFERENCES Base(Id) 

簡單,對不對?

我們已經通過消除該表格解決了代表抽象基類的表中存在無意義數據的關注點#1。我們還組合了用於獨立建模每個具體類型的表,而不是將它們的所有Base實例存儲在同一個表中,而不管它們的具體類型如何。

按原樣,此架構不會限制您的Base類型的多態性。原樣,可以插入行ConcreteType1與非空BaseReferenceId或行ConcereteType2與空BaseReferenceId。 沒有什麼能夠阻止你插入無效數據,所以你需要非常勤奮的插入和編輯。

這是檢查約束真正發揮的地方。

ALTER TABLE Base 
ADD CONSTRAINT Base_Enforce_SumType_Properties 
CHECK 
(
    (ConcreteTypeId = 1 AND BaseReferenceId IS NULL) 
    OR 
    (ConcreteTypeId = 2 AND BaseReferenceId IS NOT NULL) 
) 

檢查約束Base_Enforce_SumType_Properties定義每個具體類型不變,保護在插入和更新數據。繼續運行所有的DDL,在您自己的數據庫中創建ConcreteTypeBase表。然後嘗試將行插入Base,這些行破壞了檢查約束中描​​述的規則。你不能!最後,你的數據模型保持在一起。

爲了解決問題#2:現在您的類型的所有成員都在單個表中(使用不變式實施),您的查詢將會更簡單。你甚至不需要「等同於SQL中的match F#關鍵字」。添加新的具體類型非常簡單,只需在ConcreteType表中插入新行,將任何新屬性添加爲表Base中的列,然後修改約束以反映任何新的不變量。

2部分:分層編碼(讀:遞歸),在SQL Server關係

關注#2部分,我認爲關於跨ConcreteType2Base之間存在的「父子」關係查詢的複雜性。有很多方法可以處理這種查詢並選擇一種,我們需要考慮一個特定的用例。

示例用例:我們希望查詢每個單個的Base實例並組合包含每一行的對象圖。這很容易;我們甚至不需要加入。我們只需要一個可變的Dictionary<int,Base>Id作爲鑰匙。

這裏有很多需要考慮的東西:有一個名爲HierarchyIDdocs)的MSSQL數據類型,它實現了'物化路徑'模式,可以更容易地建模類似於你的層次結構。您可以在Base.ID/Base.BaseReferenceID列中嘗試使用HierarchyID而不是INT

我希望這會有所幫助。