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

本文涉及的产品
阿里云百炼推荐规格 ADB PostgreSQL,4核16GB 100GB 1个月
RDS PostgreSQL Serverless,0.5-4RCU 50GB 3个月
推荐场景:
对影评进行热评分析
云数据库 RDS PostgreSQL,高可用版 2核4GB 50GB
简介: 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 (版权不明,互联网上可找到的发表最早的一篇)

相关实践学习
AnalyticDB MySQL海量数据秒级分析体验
快速上手AnalyticDB MySQL,玩转SQL开发等功能!本教程介绍如何在AnalyticDB MySQL中,一键加载内置数据集,并基于自动生成的查询脚本,运行复杂查询语句,秒级生成查询结果。
阿里云云原生数据仓库AnalyticDB MySQL版 使用教程
云原生数据仓库AnalyticDB MySQL版是一种支持高并发低延时查询的新一代云原生数据仓库,高度兼容MySQL协议以及SQL:92、SQL:99、SQL:2003标准,可以对海量数据进行即时的多维分析透视和业务探索,快速构建企业云上数据仓库。 了解产品 https://www.aliyun.com/product/ApsaraDB/ads
相关文章
|
2天前
|
设计模式 安全 PHP
PHP 7新特性深度解析与应用实践
【6月更文挑战第29天】在PHP 7的发布中,开发者社区迎来了一场性能与功能的盛宴。本文将深入挖掘PHP 7的新特性,从类型声明的强化到异常处理的改进,再到匿名类的引入,我们将一一探讨这些变化如何影响日常编码实践。通过实际代码示例,我们将展示如何有效利用这些新特性来编写更加清晰、高效和安全的PHP代码。
22 11
|
1天前
|
缓存 安全 编译器
PHP 8新特性解析与性能优化实践
PHP 8的发布带来了一系列新特性和性能改进,本文将深入探讨PHP 8的关键特性,如JIT编译器、类型系统的增强和语言语法的优化,以及如何利用这些特性优化现有代码和提升应用性能。
6 1
|
1天前
|
安全 算法 编译器
PHP 8新特性深度解析与实践应用
【7月更文挑战第2天】本文深入探讨了PHP 8带来的革新性特性,包括JIT编译器的引入、联合类型和属性的声明等。文章不仅剖析了这些新特性背后的技术原理,还通过实例展示了如何在现实项目中有效利用它们来提升代码质量和执行效率。读者将获得对PHP 8新特性的全面认识以及如何在实际开发中灵活运用它们的实用指南。
7 1
|
5天前
|
JavaScript 前端开发 Java
Java11 新特性深度解析
Java11 新特性深度解析
|
6天前
|
关系型数据库 MySQL 测试技术
深入探索MySQL 8:隐藏索引与降序索引的新特性
深入探索MySQL 8:隐藏索引与降序索引的新特性
|
15天前
|
JavaScript 前端开发 Java
【JavaScript】ECMAS6(ES6)新特性概览(二):解构赋值、扩展与收集、class类全面解析
【JavaScript】ECMAS6(ES6)新特性概览(二):解构赋值、扩展与收集、class类全面解析
19 2
|
15天前
|
SQL 自然语言处理 前端开发
【JavaScript】ECMAS6(ES6)新特性概览(一):变量声明let与const、箭头函数、模板字面量全面解析
【JavaScript】ECMAS6(ES6)新特性概览(一):变量声明let与const、箭头函数、模板字面量全面解析
14 2
|
21天前
|
编译器 PHP 开发者
PHP 8.0 新特性解析与实践
PHP 8.0的发布带来了许多令人激动的新特性和改进,本文将深入解析PHP 8.0的一些重要特性,包括JIT编译器、命名参数、属性声明的初始化等,并结合实际案例进行详细讨论,帮助开发者更好地理解和应用这些新特性。
25 5
|
19天前
|
安全 Java API
深入解析 Java 8 新特性:LocalDate 的强大功能与实用技巧
深入解析 Java 8 新特性:LocalDate 的强大功能与实用技巧
19 1
|
19天前
|
SQL JSON 关系型数据库
MySQL 8.0新特性?
【6月更文挑战第14天】MySQL 8.0新特性?
18 1

相关产品

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

    更多