我正在使用F#对应用程序进行建模,并且在尝试为以下递归类型构建数据库表时遇到了一个困难:
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找到了灵感)是:
我对这个解决方案有两个顾虑:
基表上会有行,即使在我的模型中没有意义。但我可以忍受。
似乎查找有关Concrete2的BaseReference的所有信息的查询将是复杂的,因为我将不得不考虑类型的递归性和不同的具体表。此外,向模型添加新的具体类型必须修改这些查询。当然,除非SQL中有match
F#关键字。
我是否担心这些担忧?或者,有没有更好的方法在SQL表中建模这种递归F#类型?
答案 0 :(得分: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
的{{1}}行或带有空BaseReferenceId
的{{1}}行。
没有什么可以阻止您插入无效数据,所以您需要非常勤奋地插入和编辑。
这是检查约束真正发挥作用的地方。
ConcereteType2
检查约束BaseReferenceId
定义每种具体类型的不变量,保护插入和更新时的数据。继续并运行所有DDL以在您自己的数据库中创建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
表。然后尝试在ConcreteType
中插入违反check约束中描述的规则的行。你不能!最后,您的数据模型结合在一起。
解决问题#2:现在您所有类型的成员都在一个表中(强制执行不变量),您的查询将更简单。您甚至不需要“等同于SQL中的Base
F#关键字”。添加新的具体类型就像在Base
表中插入新行一样简单,在match
表中添加任何新属性作为列,并修改约束以反映任何新的不变量。
第2部分:在SQL Server中编码分层(读取:递归)关系
关注的一部分#2我想到了查询ConcreteType
和Base
之间存在的“亲子关系”的复杂性。有很多方法可以处理这种查询并选择一种,我们需要考虑一个特定的用例。
示例用例:我们希望查询每个ConcreteType2
实例并组装包含每一行的对象图。这很简单;我们甚至不需要加入。我们只需要一个带有Base
的可变Base
作为密钥。
进入这里会有很多东西,但需要考虑的事项:有一个名为Dictionary<int,Base>
(docs)的MSSQL数据类型,它实现了“物化路径”模式,允许更容易的层次结构建模像你的。您可以尝试在Id
/ HierarchyID
列上使用HierarchyID
代替INT
。
我希望这会有所帮助。