PostgreSQL 17的递归查询说明先分开两件容易混写的事:计算一个排序列,再用它排列最终结果,不等于控制查询求值时逐行访问的次序。文档说,实际访问顺序由实现决定。因而,资料上写着“深度优先结果”,首先描述读者拿到的数据怎样排列,并没有单独证明数据库内部已经按相同路线完成了访问。[1]
深度与广度的结果排列,也使用不同的辅助信息。深度优先示例为每一行累计一路已经访问过的值,形成路径数组;广度优先示例则记录搜索深度。路径保存的是走到当前位置经过哪些标识,深度保存的是处于哪一层。两者都能辅助排序,却没有因此成为同一个字段:只知道层数,不能还原从起点走过的全部节点。[1]
文档另有一个必须保留的限定:递归求值算法的输出呈广度优先,是实现细节,不宜据此依赖其顺序;同一层内各行的顺序更是未定义。这个限定解释了为什么“先看到上一层,再看到下一层”仍未回答一层内部如何排列。层次相同并不替同层节点建立稳定先后,结果排序信息不能从一个宽泛的遍历名称里自动补齐。[1]
SEARCH语法所描述的对象包括要计算深度还是广度排序、追踪哪些列,以及保存排序资料的列名。这个结果列会隐式加入公共表表达式的输出。这里增加的是一份可供结果排列使用的信息,而不是把原业务数据的某一列改称执行顺序。阅读文档时,保留“追踪对象”和“新增排序列”两个身份,才能看清原值与辅助值各自来自哪里。[1]
环路检测处理的是另外一个问题:沿某条链接路径,是否再次到达已经遇见的节点。文档说,有时用UNION消除与先前输出完全重复的行,可以终止重复;但有环不一定产生完全重复的输出行。如果输出还包含深度,绕回同一节点时深度已经变化,整行便不再相同。节点身份重复与输出整行重复,因而有不同的比较范围。[1]
可以据此作一个纯文字的假设:一条路径回到原节点,标识仍相同,但第二次到达时层数更大。以整行是否相同为问题,两个记录含有不同的层数;以这条路径是否重遇标识为问题,它们却指向同一个节点。这个例子没有运行查询,只把原文深度列会变化的理由展开,说明为什么不能把整行去重直接当作路径判环的同义词。[1]
判环所追踪的身份还可能由不止一个字段构成。原文分别讨论单字段值组成的数组,以及由多个字段组成的行数组。后者需要保留字段组合,不能只拿组合中的一个值代表完整身份。路径数组同时记录到达某行的经过,因此不仅有“重复了没有”的结果,也留下这次判断所依附的路径背景。[1]
CYCLE明确列出用于判环的字段、显示是否检测到环的标记列,以及追踪路径的列;标记和路径也会隐式加入输出。标记说明是否重遇,路径说明如何到达,深度排序值说明处于哪一层,三者不因同时出现在结果里就相互替代。文档还说明,判环路径的计算方式与前述深度排序路径相同,而广度排序和判环可以分别承担两个目的。[1]
本文限定PostgreSQL 17的公开语义,未执行SQL、调整查询或据示例承诺终止时效和性能。网页更新信息不作为该文首次发布日期。[1]
资料来源:
[1] [PostgreSQL 17: 7.8 WITH Queries (Common Table Expressions)](https://www.postgresql.org/docs/17/queries-with.html)
信息来源
本文基于上述公开资料整理,未使用来源页面的图片、视频或嵌入媒体。