F#递归类型到SQL表

问题描述 投票:4回答:1

我正在使用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找到灵感)是:

enter image description here

我对此解决方案有两个顾虑:

  1. Base表上会有行,即使在我的模型中没有意义。但我可以忍受这一点。
  2. 似乎查找有关Concrete2的BaseReference的所有信息的查询将是复杂的,因为我将不得不考虑类型的递归性和不同的具体表。此外,向模型添加新的具体类型必须修改这些查询。当然除非SQL中有与match F#关键字相同的内容。

我是否担心这些担忧?或者,有没有更好的方法来在SQL表中建模这种递归F#类型?

f# data-modeling modeling recursive-query recursive-type
1个回答
0
投票

第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行与非null BaseReferenceIdConcereteType2行与null 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数据类型,它实现了“物化路径”模式,允许像你这样的层次结构更容易建模。您可以尝试在HierarchyID / INT列上使用Base.ID而不是Base.BaseReferenceID

我希望这有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.