AnalyticDB for PostgreSQL 6.0 新特性解析:Recursive CTE (Common Table Expressions)

简介: Recursive CTE (Common Table Expressions) 能够实现SQL的递归查询功能,一般用于处理逻辑上为层次化或树状结构的数据(如查询组织结构、物料清单等),方便对该类数据进行多级递归查询。

作者简介:陈毅能(苇度)毕业于中国科学院,曾任职于百度、微软亚洲研究院、华为、阿里巴巴等公司,专注于分布式数据库内核开发。

Recursive CTE (Common Table Expressions) 能够实现SQL的递归查询功能,一般用于处理逻辑上为层次化或树状结构的数据(如查询组织结构、物料清单等),方便对该类数据进行多级递归查询。与Oracle的CONNECT BY语法的功能类似。

在AnalyticDB for PostgreSQL 6.0版本中,Recursive CTE不再作为待验证特性,而是默认打开。可以通过参数gp_recursive_cte打开或关闭Recursive CTE,默认情况下,gp_recursive_cte是打开的。

show gp_recursive_cte;

 gp_recursive_cte
------------------
 on
(1 row)

例子:1到100求和

使用Recursive CTE可以完成一些普通SQL语句无法完成的功能。使用Recursive CTE后,SQL语句可以引用它自己的输出。首先来看一个例子,计算1、2、……、100的和。

WITH RECURSIVE cte(n) AS (
    VALUES (1)
  UNION ALL
    SELECT n+1 FROM cte WHERE n < 100
)
SELECT sum(n) FROM cte;

 sum
------
 5050
(1 row)

上述例子中,CTE递归调用了自己,从而生成了1到100的序列,进而在主查询中进行求和。

Recursive CTE语法及执行逻辑

使用Recursive CTE的语法如下:

WITH RECURSIVE cte_name AS (
    non_recursive_term
  UNION [ ALL ]
    recursive_term
)
sql_statement;

在recursive_term的部分,Recursive CTE对自身进行自引用。其整体执行逻辑如下:

  • 执行non_recursive_term部分。如果是UNION,还需要执行一次去重。然后将数据作为本轮执行的结果,并将其结果放入一个临时工作表
  • 如果这个临时工作表非空,则循环执行如下步骤。如果这个临时工作表为空,则返回所有轮的执行结果
    (1)执行recursive_term部分。如果是UNION,需要去除重复数据,且要去除和之前结果重复的数据。然后将数据作为本轮执行的结果,并将其结果放入一个中间结果表

(2)将临时工作表的内容替换为中间结果表,并且清空中间结果表

在使用Recursive CTE的时候,需要确保执行结果是可收敛的,即总有一轮的执行结果为空,以结束循环,否则查询将出现无限循环。在前面1到100求和的例子中,有一个WHERE条件限制循环执行100步,数字达到100后因不满足WHERE条件,返回0行数据,循环终止,查询结束。

从上述执行逻辑看,Recursive CTE将对数据进行广度优先遍历。

例子:通过省市上下级关系表查询

建立省市上下级关系表:

CREATE TABLE city_relation
(
    id int           -- 当前省市id
  , parent_id int    -- 上级省市id
  , name varchar(10) -- 当前省市名称
);

插入省市关系数据:

INSERT INTO city_relation values( 2,  NULL, '浙江省');
INSERT INTO city_relation values( 1,  NULL, '广东省');
INSERT INTO city_relation values( 3,  2,    '衢州市');
INSERT INTO city_relation values( 4,  2,    '杭州市');
INSERT INTO city_relation values( 5,  2,    '湖州市');
INSERT INTO city_relation values( 6,  2,    '嘉兴市');
INSERT INTO city_relation values( 7,  2,    '宁波市');
INSERT INTO city_relation values( 8,  2,    '绍兴市');
INSERT INTO city_relation values( 9,  2,    '台州市');
INSERT INTO city_relation values(10,  2,    '温州市');
INSERT INTO city_relation values(11,  2,    '丽水市');
INSERT INTO city_relation values(12,  2,    '金华市');
INSERT INTO city_relation values(13,  2,    '舟山市');
INSERT INTO city_relation values(14,  4,    '上城区');
INSERT INTO city_relation values(15,  4,    '下城区');
INSERT INTO city_relation values(16,  4,    '拱墅区');
INSERT INTO city_relation values(17,  4,    '余杭区');
INSERT INTO city_relation values(18, 11,    '金东区');
INSERT INTO city_relation values(19,  1,    '广州市');
INSERT INTO city_relation values(20,  1,    '深圳市');

查询浙江省及其下属城市列表:

WITH RECURSIVE cities AS 
( 
    SELECT id, name, parent_id, name::text as path FROM city_relation WHERE id=2
  UNION ALL
    SELECT t.id, t.name, t.parent_id, c.path || '>' || t.name as path
    FROM city_relation t JOIN cities c ON t.parent_id = c.id
)
SELECT id, name, path FROM cities;

查询结果:

  id |  name  |         path
----+-------+----------------------
  2 | 浙江省 | 浙江省
 13 | 舟山市 | 浙江省>舟山市
 11 | 丽水市 | 浙江省>丽水市
 10 | 温州市 | 浙江省>温州市
  9 | 台州市 | 浙江省>台州市
  6 | 嘉兴市 | 浙江省>嘉兴市
  5 | 湖州市 | 浙江省>湖州市
 12 | 金华市 | 浙江省>金华市
  8 | 绍兴市 | 浙江省>绍兴市
  7 | 宁波市 | 浙江省>宁波市
  4 | 杭州市 | 浙江省>杭州市
  3 | 衢州市 | 浙江省>衢州市
 18 | 金东区 | 浙江省>丽水市>金东区
 17 | 余杭区 | 浙江省>杭州市>余杭区
 14 | 上城区 | 浙江省>杭州市>上城区
 15 | 下城区 | 浙江省>杭州市>下城区
 16 | 拱墅区 | 浙江省>杭州市>拱墅区
(17 rows)

引用

[1] https://gpdb.docs.pivotal.io/6-0/relnotes/gpdb-60-release-notes.html
[2] https://gpdb.docs.pivotal.io/6-0/admin_guide/query/topics/CTE-query.html
[3] https://blog.csdn.net/zengshaotao/article/details/84753796 (版权不明,互联网上可找到的发表最早的一篇)

相关实践学习
数据库实验室挑战任务-初级任务
本场景介绍如何开通属于你的免费云数据库,在RDS-MySQL中完成对学生成绩的详情查询,执行指定类型SQL。
阿里云云原生数据仓库AnalyticDB MySQL版 使用教程
云原生数据仓库AnalyticDB MySQL版是一种支持高并发低延时查询的新一代云原生数据仓库,高度兼容MySQL协议以及SQL:92、SQL:99、SQL:2003标准,可以对海量数据进行即时的多维分析透视和业务探索,快速构建企业云上数据仓库。 了解产品 https://www.aliyun.com/product/ApsaraDB/ads
相关文章
|
1月前
|
消息中间件 存储 数据库
RocketMQ 流存储解析:面向流场景的关键特性与典型案例
RocketMQ 流存储解析:面向流场景的关键特性与典型案例
88360 0
|
2天前
|
Rust 安全 程序员
Rust vs Go:解析两者的独特特性和适用场景
在讨论 Rust 与 Go 两种编程语言哪种更优秀时,我们将探讨它们在性能、简易性、安全性、功能、规模和并发处理等方面的比较。同时,我们看看它们有什么共同点和根本的差异。现在就来看看这个友好而公平的对比。
|
16天前
|
JavaScript API UED
Vue3.0新特性解析与实战:Composition API、Teleport与Suspense
【4月更文挑战第6天】Vue3.0引入了颠覆性的Composition API,通过函数式方法提升代码可读性和复用性,例如`setup()`、`ref`等,便于逻辑模块化。实战中,自定义的`useUser`函数可在多个组件中共享用户信息逻辑。另外,Teleport允许组件渲染到DOM特定位置,解决模态框等场景的上下文问题。再者,Suspense提供异步组件加载的延迟渲染,使用fallback内容改善用户体验。这些新特性显著优化了开发和性能,适应现代Web需求。
19 0
|
22天前
|
测试技术 PHP 开发者
PHP 7.4新特性深度解析
【4月更文挑战第4天】 本文将深入探讨PHP 7.4的新特性,包括预加载,数组解构,扩展的箭头函数等。我们将详细解释这些新特性的作用,以及如何在项目中使用它们来提高代码的效率和可读性。
|
1月前
|
PHP 开发者
PHP 8.1 新特性解析:提升开发效率与性能的利器
本文将深入探讨PHP 8.1的新特性,包括联合方法调用、never返回类型、str_contains函数等,展示这些更新如何提升开发者的工作效率和代码性能。
15 1
|
1月前
|
存储 安全 Linux
C++文件格式深度解析:从底层结构到关键特性
C++文件格式深度解析:从底层结构到关键特性
250 3
C++文件格式深度解析:从底层结构到关键特性
|
1月前
|
编译器 PHP 开发者
PHP 8 新特性解析:提升性能与安全性
随着技术的不断进步,PHP 8作为一种流行的服务器端脚本语言,在性能和安全性方面有了许多值得关注的新特性。本文将深入探讨PHP 8的一些重要更新,包括Just In Time编译器、Union Types、Named Arguments等,帮助开发者更好地利用这些新功能提升应用程序的性能和安全性。
|
1月前
|
编译器 C++
【C/C++ 构造函数 详解】深入解析C++ 构造函数:C++ 11 中的新特性与实践
【C/C++ 构造函数 详解】深入解析C++ 构造函数:C++ 11 中的新特性与实践
106 0
|
1月前
|
编译器 开发工具 Android开发
Android 12 新特性深度解析
【2月更文挑战第15天】 随着移动操作系统的不断进化,Android 12带来了一系列创新功能与性能提升。本文将深入剖析Android 12的核心新特性,包括隐私仪表盘、通知管理、设备控制以及性能优化等方面,为开发者和用户提供全面的更新指南。
|
1月前
|
JavaScript Java 程序员
Java 8新特性解析:Lambda表达式与函数式编程
【2月更文挑战第12天】 本文深入探讨Java 8引入的两大革命性特性:Lambda表达式和函数式编程接口,旨在为Java开发者提供一个清晰的指南,帮助他们理解和应用这些新特性以提升代码的简洁性和效率。通过对Lambda表达式的基本概念、语法及其与函数式接口的结合使用进行详细分析,本文展示了如何利用这些新特性来编写更加简洁、易读且易于维护的代码。同时,文章还将通过实例探讨Lambda表达式在实际开发中的应用,包括在集合处理、事件监听和并发编程等方面的具体使用场景,以期让读者能够充分理解并有效利用Java 8的这些新工具,从而在日常开发工作中提高效率。
25 3

相关产品

  • 云数据库 RDS PostgreSQL 版
  • 推荐镜像

    更多