【问题标题】:Modeling set-based code-listings of sql data manipulation operations为 sql 数据操作操作的基于集合的代码列表建模
【发布时间】:2015-10-01 18:47:05
【问题描述】:

像 LINQ 这样的技术在描述关系数据查询方面做得很好,使用 IQueryableIGroupingIOrderedQueryable 等类型建模投影选择 em>、聚合排序等。这些来自关系代数的概念允许我们在一台机器上用一种语言传达相当任意的查询,并用另一种语言执行它( ~sql) 在不同的机器上。

如果能够对更复杂的多部分查询甚至涉及INSERTs、UPDATEs 和DELETEs 的数据操作命令执行相同的操作,那就太好了,描述整个操作,而不需要首先在应用层中检索/混合数据的开销,这是典型的对象-关系映射器或 ORM。

在应用程序中,我们可以描述如下操作:删除所有 Customers(以及他们的 Orders),其最近的 Order 是超过 2 年(此外,假设没有为该关系打开级联删除)。这当然比使用 t-sql 脚本的 ADO 更有效,但不能在 ORM 中完成,因为没有在应用层中选择、传输、水合和跟踪数据以及可能发出单独的删除命令的开销。 (也许 ORM 有一些优化可以在某些情况下更有效地做到这一点,但通常 AFAIK 他们不能)当然,发出 t-sql 脚本的一个问题是语句中没有类型检查,也没有任何参数或返回数据。

除了减少运行时处理和网络通信开销之外,能够为远程执行建模这些任意命令的一个惊天动地的优势是可以在应用层中编码和注册域范围的不变量,然后可以自动发出以及任何临时命令。

我们可能有一个愚蠢的域不变量A对于所有客户订单的每个客户的总和 价格 不能超过 10,000,000.00 美元,除非 whoa 位是 1 和另一个愚蠢的域不变量 B,上面写着 对于所有 客户, lastName 不能包含超过三个下划线(尽管这些可能可以通过数据库引擎本身的检查约束或触发器的本机机制来强制执行)。然后,当我们发出更新现有订单的 价格的命令时,系统可以通过静态分析知道该命令可能会违反不变量A,并且不变的B 不能,因此系统会在原始命令之后发出一些A 的断言。整个发出的脚本将被包装在一个事务中(如果断言失败则回滚)并且不变量可以自动缩小以仅针对特定的客户的订单断言规则> 而不是不必要地重新检查所有 客户的总数。我相信这种优化的、集中的、DRY 的业务规则编码/执行在当今的产品中是不可能的。

为了实现这一潜力,我认为我们需要一个代数(超越SELECT 的关系代数)来描述对 INSERT、UPDATE 和 DELETE 的任意数据操作(统称为 DML) 甚至诸如中间计算值之类的东西,例如用于计算的临时表,在 t-sql 中表示为多语句列表。

不幸的是,我一直无法找到关于将 DML 形式化为代数的研究,或者能够对其建模,或者这种元编程。虽然 Tutorial-D 和 jOOq 似乎为讨论提供了一些东西——我只是不知道如何提取它。你能解释清楚吗?


一些我认为很有价值的讨论,但我想避免用它填满 cmets:

您是否建议域模型不适合保护不变量和建立事务边界?您提到的不变量使用适当的域模型并不难保护。你到底想避免什么问题?

--普拉克斯

据我了解,典型 ddd 中的大型域需要有界上下文,以避免将大型数据子集水合到应用层进行验证。我试图避免这种开销。此外,对于每个有界上下文,域不变量必须进行非平凡的重述,这很容易出错。通过对远程执行的操作进行建模,我们可以获得更智能/更小/更快/更正确的代码。

在某些核心库中,可以对域进行建模并注册不变量。然后,该库的使用者(例如 Web 服务)可以构建任意操作的类型检查描述,而无需明确考虑有界上下文或特定不变量。域核心向它的消费者提供“这是你可以在这个域上做的所有事情”,并且(也许)服务代码向它的客户提供“这些正是我们提供的功能”。

– uosɐſ

我不确定您是否正确理解限界上下文是什么以及它们如何相互通信。 “此外,对于每个容易出错的有界上下文,域不变量必须非常重要地重述/维护” 通常只有一个上下文具有数据所有权,并且该上下文应负责涉及它自己的数据的不变量。例如,想象一家在 Internet 上销售商品的公司。它们可能有一个用于维护产品的 Inventory 上下文和一个用于侦听 Inventory 中新可用产品的 Shopping 上下文。

--普拉克斯

我不太反对当前的 ddd 技术,所以我不会选择优秀的例子来反对它们。我对这种替代安排更感兴趣,我直觉它比当前的 ddd 技术更自然和先进。我见过非常交织在一起的数据模型,并且没有提供明显的界限(可能设计不佳,好吧)。我希望这种方式可以无边界且性能更高。

– uosɐſ

如果存在产品名称不能包含“宣传”一词的规则,则它只会在库存上下文中强制执行。如果我们要在每个其他上下文中复制每个上下文的不变量,那确实会成为维护的噩梦。

--普拉克斯

但您可能有一个以客户为中心的有界上下文和以订单为中心的第二个有界上下文。也许我提到的 $10,000,000.00 LimitCustomer 中的一列(因此是可变的),因此可以通过两种方式违反此业务规则:要么删除它限制 客户或增加订单的总数。因此,非平凡互惠规则必须根据更改检查任一有界上下文中的违规行为。如果 Prices 和 Limits 没有改变,我们的系统可能会决定跳过断言,这会很巧妙,不是吗?在传统的 ddd 中,您可能还需要一些优化的变体来进行批量操作(向每个 客户 添加一个 1000 美元的 Order),这些变体可以自动进行由我们的新系统派生而来。

– uosɐſ

【问题讨论】:

  • “对于所有客户,他们的订单价格总和不能超过 10,000,000.00 美元”:您是指他们所有订单的全球总和,还是每个客户的总和?
  • 文中澄清

标签: database domain-driven-design metaprogramming dsl


【解决方案1】:

看起来不太可能,您不需要的一件事是“超越”关系代数。这根本不是一个理论问题,而是一个想象力和工程问题。您正在谈论的问题跨越多个领域:编程语言、库支持和 DBMS。它可以(并且应该)完成。但首先它需要被普遍理解为现实和可取的,而我们还没有做到。

就代数而言,所缺少的只是赋值。如果您阅读过 Date 的 第三宣言,您可能会记得插入/更新/删除只是分配的变体:

S += f(R)        -- insert
S += f(R) - g(S) -- update
S -= f(R)        -- delete

(Python 在其标准库中的 set 类中很好地证明了这一点,顺便说一句,除了您没有开箱即用的元组集运算符。)

所以这不是理论上的问题;代数很好。而且您也不是纯粹询问语法。在我看来,您想要的是一个 DBMS,您可以在功能上 进行操作,而无需 SQL 和 SQL 生成器作为中介。如果您的数据库中的表在您的编程语言中显示为变量,并且有一个支持 selectproject 的关系代数库(用于该语言),那不是很好吗? em>,然后加入

就此而言,为什么不将关系运算符合并到适当的语言中呢?为什么在关系理论发明 40 年后,它的使用仅限于数据库?事实上,几十年来这一直是数据库社区的悲哀。虽然它已经完成 - cf。例如,Datalog ——我们近年来看到的过多的新语言以延续不支持集合论操作的 C 传统而著称。

不过,仅仅在语言中内置关系和关系运算符是不够的。编程语言通常期望定义它们的变量,并独占它们。这实际上是编程语言的定义:定义和操作内存块的东西,其生命周期受程序执行的限制。有趣的数据通常从“外面”的某个地方开始,而不是在程序内存中。

所以,您真正想要的是“在数据库中”操作数据就好像这些表是程序变量(也称为远距离操作) ,然后是一些超级方便、理想透明的方式将结果移动到程序内存中。比如,哦,任务。要在这个方向上取得任何进展,您需要 DBMS 的合作。

如今,要与典型的 DBMS 进行交互,您需要用它的语言(通常是 SQL)来表述您的问题,并将输出逐行提取到程序内存中。这是一个 I/O 模型:写入字符串,读取结果。要将 I/O 从编程模型中取出,您需要一个不同的 API,更像是 RPC。如果编程语言和 DBMS 使用相同的数据模型(关系)和函数(关系代数)和数据类型,那么您就有机会以相同的方式操作远程和本地数据.

这就是套件:

  • 关系和关系操作的语言支持
  • 本地和机器外变量的语言识别
  • DBMS 支持以编程方式公开表定义,以便编译器/解释器可以“链接”到它们,作为库符号
  • DBMS 支持远程调用关系运算符,逐个函数,而不是逐个语句

您可能已经注意到,在一个合理的近似值上,没有人试图做上述事情。语言设计者普遍忽略集合论和谓词逻辑。 DBMS 供应商——以及流行的免费项目——被 SQL 束缚,对修复 SQL 的集合论缺陷或通过逻辑函数 API 暴露他们的系统完全不感兴趣。最远离任何人的想法是开发一组一致的类型和运算符。

那么我们有什么呢? Linc 是跳舞熊的一个很好的例子,它将来自字符串和原始类型的 SQL 组合在一起,将其喷射到管道上,并将数据库表表示为宿主语言提供的逐行操作。考虑到当今环境的现实,这是一个相当不错的节目。但是,正如你的问题所暗示的,新奇感消失了,工作也没有变得更容易。不过,您可能想保留自己的票:根据当前的速度和方向判断进度,您将在同一个座位上再待 40 年。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-08
    • 2011-10-06
    • 2021-10-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多