F#递归类型到SQL表

时间:2017-10-11 17:28:36

标签: f# data-modeling modeling recursive-query recursive-type

我正在使用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. 基表上会有行,即使在我的模型中没有意义。但我可以忍受。

  2. 似乎查找有关Concrete2的BaseReference的所有信息的查询将是复杂的,因为我将不得不考虑类型的递归性和不同的具体表。此外,向模型添加新的具体类型必须修改这些查询。当然,除非SQL中有match F#关键字。

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

1 个答案:

答案 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我想到了查询ConcreteTypeBase之间存在的“亲子关系”的复杂性。有很多方法可以处理这种查询并选择一种,我们需要考虑一个特定的用例。

示例用例:我们希望查询每个ConcreteType2实例并组装包含每一行的对象图。这很简单;我们甚至不需要加入。我们只需要一个带有Base的可变Base作为密钥。

进入这里会有很多东西,但需要考虑的事项:有一个名为Dictionary<int,Base>docs)的MSSQL数据类型,它实现了“物化路径”模式,允许更容易的层次结构建模像你的。您可以尝试在Id / HierarchyID列上使用HierarchyID代替INT

我希望这会有所帮助。