SQL中的递归?但为什么?哦,这有很多用途。在 SQL 中存储分层数据是很常见的,递归查询是从此类图中提取信息的便捷方式。组织结构、应用程序菜单结构、项目中带有子任务的一组任务、网页之间的链接、将设备模块分解为部件和子部件是分层数据的示例。这篇文章不会详细介绍这些许多用例,而是通过两个玩具示例来理解这个概念——最简单的数字递归和从家谱中查询数据的可能案例。让我们将查询视为一个函数。从某种意义上说,函数接受输入并产生输出。查询对关系进行操作,或者可以说是表。我们将它们表示为 Rn。这是查询的图片。它需要三个关系 R1、R2、R3 并产生一个输出 R。足够简单。 Query (SELECT 1 AS n) 现在有一个名字——R。我们可以稍后引用这个名字。这里 R 是一个包含数字 1 的单行单列表。整个表达式的结果是数字 2。为了使递归起作用,我们需要从一些事情开始并决定何时停止递归。为了实现这一点,通常递归的 with 语句具有以下形式。重要的是要注意,基本查询不涉及 R,但递归查询引用 R。乍一看,这似乎是无限循环,要计算 R,我们需要计算 R。但这里有一个问题。 R 实际上不引用自身,它只是引用先前的结果,当先前的结果为空表时,递归停止。实际上,它可以帮助将其视为迭代而不是递归!这是发生的事情:首先执行基本查询,获取计算结果 R0 所需的任何内容。第二个递归查询以 R0 作为输入执行,即 R 在第一次执行时引用递归查询中的 R0。递归查询产生结果 R1,这是 R 将在下一次调用时引用的结果。依此类推,直到递归查询返回空结果。此时,所有中间结果都组合在一起。基本查询返回数字 1 ,递归查询在 countUp 名称下使用它并产生数字 2,这是下一次递归调用的输入。当递归查询返回空表 (n >= 3) 时,调用的结果会堆叠在一起。
当心,这样数数只能走那么远。递归是有限制的。它默认为 100,但可以使用 MAXRECURSION 选项进行扩展。实际上,提高递归限制可能是一个坏主意。图可能有循环,有限的递归深度可以作为一种很好的防御机制来阻止表现不佳的查询。基本查询找到 Frank 的父母——Mary,递归查询在 Ancestor 名称下获取这个结果,并找到 Mary 的父母,即 Dave 和 Eve,这一直持续到我们再也找不到任何父母为止。现在,这个树遍历查询可以成为用一些其他感兴趣的信息来扩充查询的基础。例如,在表中有出生年份,我们可以计算出孩子出生时父母的年龄。下一个查询正是这样做的,同时显示血统。为此,它从上到下遍历树。第一行中的 parentAge 为零,因为我们不知道 Alice 是什么时候出生的。带走 - 递归查询引用基本查询的结果或递归查询的先前调用。当递归查询返回空表时,链停止。在后续文章中,我们将对 SQL 递归进行代数分析,并将研究递归存储过程。