我在 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)是:
我对此解决方案有两个担忧:
match
SQL 中的 F# 关键字。 我是不是太担心这些问题了?或者,是否有更好的方法在 SQL 表中对这种递归 F# 类型进行建模?
最佳答案
第 1 部分:在关系表中编码代数数据类型
我已经为这件事挣扎了很多次。我终于发现了在关系表中建模代数数据类型的关键: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 以创建 ConcreteType
和 Base
您自己的数据库中的表。然后尝试将行插入 Base
这违反了检查约束中描述的规则。你不能!最后,您的数据模型结合在一起。解决问题 #2:现在您的类型的所有成员都在一个表中(强制执行不变量),您的查询将更简单。您甚至不需要“等同于 SQL 中的
match
F# 关键字”。添加新的具体类型就像在 ConcreteType
中插入新行一样简单。表,将任何新属性添加为 Base
中的列表,并修改约束以反射(reflect)任何新的不变量。第 2 部分:在 SQL Server 中编码分层(阅读:递归)关系
部分关注#2 我考虑了跨
ConcreteType2
之间存在的“父子”关系进行查询的复杂性。和 Base
.有很多方法可以处理这种查询并选择一种,我们需要记住一个特定的用例。示例用例:我们希望查询每一个
Base
实例化并组装包含每一行的对象图。这很简单;我们甚至不需要加入。我们只需要一个可变的 Dictionary<int,Base>
与 Id
用作 key 。这里有很多内容,但需要考虑:有一个名为
HierarchyID
的 MSSQL 数据类型。 ( docs ) 实现了“物化路径”模式,从而可以更轻松地对像您这样的层次结构进行建模。您可以尝试使用 HierarchyID
而不是 INT
在您的 Base.ID
上/Base.BaseReferenceID
列。我希望这有帮助。
关于F# 递归类型到 SQL 表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46694251/