如何选择using WITH RECURSIVE子句[关闭]

关闭。这个问题需要更加关注。它目前不接受答案。

<hr class=“my12大纲无baw0 bb bc-POWER-400”/

想改进此问题吗?编辑此帖子,更新问题,使其只关注一个问题。

已关闭8年前

改进这个问题

我在谷歌上搜索并通读了一些文章,比如
此postgreSQL手册页
或者这个博客页面
我自己也尝试过提问,但取得了一定的成功(其中一部分被挂起,而另一部分工作得又好又快),
但到目前为止,我还不能完全理解这种魔法是如何运作的

有谁能给出非常清晰的解释来演示这样的查询语义和执行过程,
更好的方法是基于典型样本,如因子计算或来自(id,parent\u id,name)表的全树扩展

对于递归的查询,人们应该知道哪些基本的指导原则和典型的错误来弥补

首先,我们要简化和澄清手册页上给出的算法描述。为了简化它,只考虑联合< > >递归条款现在(和联合后面):

递归伪实体名称(列名)为(
初选
联合所有
使用伪实体名递归选择
)
使用伪实体名称进行外部选择

为了澄清这一点,让我们用伪代码描述查询执行过程:

工作记录集=初始选择的结果
将工作记录集追加到空的外部记录集
当(工作记录集不为空)开始时
新工作记录集=递归选择的结果
将以前的工作记录集作为伪实体名称
将工作记录集附加到外部记录集
终止
总体结果=外部选择的结果
将外部记录集作为伪实体名称

甚至更短-数据库引擎执行初始选择,将其结果行作为工作集。然后它在工作集上重复执行递归选择,每次都用获得的查询结果替换工作集的内容。当递归选择返回空集时,此过程结束。所有结果行首先由init给出ial select和递归select被收集并反馈给外部select,外部select的结果成为整体查询结果

此查询正在计算3的因数:

递归阶乘(F,n)为(
选择1F,3N
联合所有
从阶乘中选择F*n F,n-1 n,其中n&gt;1
)
从n=1的阶乘中选择F

初始选择select1f,3n为我们提供初始值:3表示参数,1表示函数值。

递归选择从阶乘中选择F*nf,n-1n,其中n&gt;1说明每次我们需要将最后一个函数值乘以最后一个参数值和递减参数值。

数据库引擎按如下方式执行:

首先,它执行initail select,它给出工作记录集的初始状态:

F|n
--+--
1 | 3

然后使用递归查询转换工作记录集,并获得其第二个状态:

F|n
--+--
3 | 2

第三个国家:

F|n
--+--
6 | 1

在第三种状态下,递归选择中没有紧跟n&gt;1条件的行,因此工作集是循环出口

外部记录集现在保存由初始和递归选择返回的所有行:

F|n
--+--
1 | 3
3 | 2
6 | 1

外部选择从外部记录集中筛选出所有中间结果,仅显示最终阶乘值,该值将成为整体查询结果:

F
--
6.

现在让我们考虑表林(id,Palthyid,name) >:

id |父| id |名称
---+-----------+-----------------
1 | |第1项
2 | 1 |子项目1.1
3 | 1 |子项目1.2
4 | 1 |子项目1.3
5 | 3 |子项1.2.1
6 | |项目2
7 | 6 |子项目2.1
8 | |项目3

扩展完整树”这里的意思是在计算树项的级别和(可能)路径时,按人类可读的深度优先顺序对树项进行排序。如果不使用WITH RECURSIVE子句,这两项任务(正确排序和计算级别或路径)都无法在一个(或任何常量)SELECT中解决(或Oracle CONNECT BY子句,PostgreSQL不支持该子句)。但此递归查询可以完成此任务(几乎可以,请参见下面的注释):

,递归fulltree(id、父\u id、级别、名称、路径)为(
选择id,parent_id,1作为level,name,name | |“”,作为parent_id为null的林的路径
联合所有
选择t.id、t.parent|u id、ft.level+1作为level、t.name、ft.path | |'/'| | t.name作为path
从林t开始,满树ft,其中t.parent_id=ft.id
)
从fulltree中选择*按路径排序

数据库引擎按如下方式执行:

首先,它执行initail select,它给出表中所有最高级别的项(根):

id |父| id |级别|名称|路径
---+-----------+-------+------------------+----------------------------------------
1 | | 1 |项目1 |项目1
8 | | 1 |项目3 |项目3
6 | | 1 |项目2 |项目2

然后,它执行递归选择,从forest表中给出所有第二级项目:

id |父| id |级别|名称|路径
---+-----------+-------+------------------+----------------------------------------
2 | 1 | 2 |第1.1项|第1项/第1.1项
3 | 1 | 2 |第1.2项|第1项/第1.2项
4 | 1 | 2 |第1.3项|第1项/第1.3项
7 | 6 | 2 |子项2.1 |子项2/子项2.1

然后,它再次执行递归选择,检索三维级别的项目:

id |父| id |级别|名称|路径
---+-----------+-------+------------------+----------------------------------------
5 | 3 | 3 |子项1.2.1 |第1项/子项1.2/子项1.2.1

现在它再次执行递归选择,试图检索第四级项目,但没有,因此循环退出

外部选择设置正确的人类可读的行顺序,按路径列排序:

id |父| id |级别|名称|路径
---+-----------+-------+------------------+----------------------------------------
1 | | 1 |项目1 |项目1
2 | 1 | 2 |第1.1项|第1项/第1.1项
3 | 1 | 2 |第1.2项|第1项/第1.2项
5 | 3 | 3 |子项1.2.1 |第1项/子项1.2/子项1.2.1
4 | 1 | 2 |第1.3项|第1项/第1.3项
6 | | 1 |项目2 |项目2
7 | 6 | 2 |子项2.1 |子项2/子项2.1
8 | | 1 |项目3 |项目3

注意:只有当项目名称中的/之前没有标点字符排序规则时,生成的行顺序才会保持正确。如果我们在项目1*中重命名项目2,它将打破行顺序,位于项目1及其后代之间。

更稳定的解决方案是在查询中使用制表符(E'\t')作为路径分隔符(以后可以用可读性更强的路径分隔符代替:在外部选择中,在显示给人之前,等等).制表符分隔的路径将保持正确的顺序,直到项目名称中有制表符或控制字符-可以轻松检查和排除,而不会丢失可用性

修改上一个查询以扩展任意子树非常简单-您只需将条件parent\u id为null替换为perent\u id=1(例如)。请注意,此查询变量将返回与项1相关的所有级别和路径

现在谈谈典型错误。递归查询最显著的典型错误是在递归选择中定义病态停止条件,这会导致无限循环

例如,如果我们在上面的阶乘样本中省略了where n&gt;1条件,则递归选择的执行将永远不会给出一个空集(因为我们没有条件过滤掉单个行),循环将继续

发表评论