postgresql-通用表表达式

news/2024/7/9 20:15:45 标签: postgresql, 数据库

postgresql-通用表表达式

  • 简介
  • 简单 CTE
  • 递归 CTE
    • 案例1
    • 案例2
  • DML 语句与 CTE

简介

通用表表达式(Common Table Expression、CTE)是一个临时的查询结果或者临时表,可以
在其他 SELECT、INSERT、UPDATE 以及 DELETE 语句中使用。通用表表达式只在当前语句中
有效,类似于子查询。
使用 CTE 的主要好处包括:

  • 提高复杂查询的可读性。CTE 可以将复杂查询模块化,组织成容易理解的结构
  • 支持递归查询。CTE 通过引用自身实现递归,可以方便地处理层次结构数据和图数据。

简单 CTE

通用表表达式的定义如下:

WITH cte_name (col1, col2, ...) AS (
 cte_query_definition
)
sql_statement;

在这里插入图片描述
PostgreSQL 中的 CTE 通常用于简化复杂的连接查询或子查询。例如:

with department_avg(department_id, avg_salary) as (
 select department_id,
 avg(salary) as avg_salary
 from employees
 group by department_id
)
select d.department_name,
 da.avg_salary
 from departments d
 join department_avg da
 on (d.department_id = da.department_id)
order by d.department_name;

在这里插入图片描述
首先,我们定义了一个名为 department_avg 的 CTE,表示每个部门的平均月薪;然后和
departments 表进行连接查询。虽然用其他方式也可以实现相同的功能,但是 CTE 让代码显得更
加清晰易懂。

一个 WITH 关键字可以定义多个 CTE,而且后面的 CTE 可以引用前面的 CTE。例如:

with cte1(n) as (
 select 1
),
cte2(m) as (
 select n+1 from cte1
)
select *
 from cte1, cte2;

在这里插入图片描述
以上示例中定义了两个 CTE,其中 cte2 引用了 cte1。最后的查询使用两者进行连接查询。

递归 CTE

递归 CTE 允许在它的定义中进行自引用,理论上来说可以实现任何复杂的计算功能,最常
用的场景就是遍历层次结构的数据和图结构数据。

WITH RECURSIVE cte_name AS(
 cte_query_initial -- 初始化部分
 UNION [ALL]
 cte_query_iterative -- 递归部分
) SELECT * FROM cte_name;

RECURSIVE 表示递归;
cte_query_initial 是初始化查询,用于创建初始结果集
cte_query_iterative 是递归部分,可以引用 cte_name
如果递归查询无法从上一次迭代中返回更多的数据,将会终止递归并返回结果。

案例1

一个经典的递归 CTE 案例就是生成数字序列:

with recursive t(n) as (
 values (1)
 union all
 select n+1 from t where n < 10
)
select n from t;

在这里插入图片描述
以上语句执行过程如下:

  • 执行 CTE 中的初始化查询,生成一行数据(1)
  • 第一次执行递归查询,判断 n < 10 成立,生成一行数据 2 (n+1);
  • 重复执行递归查询,生成更多的数据;直到 n = 10 终止;此时临时表 t 中包含 10 条数据
  • 执行主查询,返回所有的数据

如果没有指定终止条件,上面的查询将会进入死循环

案例2

递归 CTE 遍历组织结构

with recursive employee_path (employee_id, employee_name, path) as
(
 select employee_id, concat(first_name, ',', last_name), concat(first_name,
',', last_name) as path
 from employees
 where manager_id is null
 union all
 select e.employee_id, concat(e.first_name, ',', e.last_name),
concat(ep.path, '->', e.first_name, ',', e.last_name)
 from employee_path ep
 join employees e on ep.employee_id = e.manager_id
)
select employee_name, path
 from employee_path
order by employee_id;

在这里插入图片描述
其中,初始化查询语句返回了公司最高层的领导(manager_id IS NULL),也就是
“Steven,King”;递归查询将员工表的 manager_id 与已有结果集中的 employee_id 关联,获取每个
员工的下一级员工,直到无法找到新的数据;path 字段存储了每个员工从上至下的管理路径

更多关于递归 CTE 的实际应用场景,可以参考这篇文章

DML 语句与 CTE

除了 SELECT 语句之外,INSERT、UPDATE 或者 DELETE 语句也可以与 CTE 一起使用。
我们可以在 CTE 中使用 DML 语句,也可以将 CTE 用于 DML 语句。
如果在 CTE 中使用 DML 语句,我们可以将数据修改操作影响的结果作为一个临时表,然
后在其他语句中使用。例如:

-- 创建一个员工历史表
create table employees_history
as select * from employees where 1 = 0;
with deletes as (
 delete from employees
 where employee_id = 206
 returning *
)
insert into employees_history
select * from deletes;

SELECT employee_id, first_name, last_name
FROM employees_history;

在这里插入图片描述
我们首先创建了一个记录员工历史信息的 employees_history 表;然后使用 DELETE 语句定
义了一个 CTE,RETURNING *返回了被删除的数据,构成了结果集 deletes;然后使用 INSERT
语句记录被删除的员工信息

接下来我们将该员工添加回员工表:

WITH inserts AS (
 INSERT INTO employees
 VALUES
(206,'William','Gietz','WGIETZ','515.123.8181','2002-06-07','AC_ACCOUNT',8800.00,NULL,205,110)
 RETURNING *
)
INSERT INTO employees_history
SELECT * FROM inserts;

除了插入数据到 employees 表之外,我们还利用 CTE 在表 employees_history 中增加了一条
历史记录,现在该表中有两条数据
在这里插入图片描述
在这里插入图片描述
CTE 中的 UPDATE 语句有些不同,因为更新的数据分为更新之前的状态和更新之后的状态。
例如:

DELETE FROM employees_history;-- 清除历史记录
with updates as (
 update employees
 set salary = salary + 500
 where employee_id = 206
 returning *
)
insert into employees_history
select * from employees where employee_id = 206;
select employee_id, salary from employees_history;

在这里插入图片描述
returning 在 CTE 中,UPDATE 语句修改了一个员工的月薪;但是为了记录修改之前的数据,
我们插入 employees_history 的数据仍然来自 employees 表。因为在一个语句中,所有的操作都在
一个事务中,所以主查询中的 employees 是修改之前的状态。
如果想要获取更新之后的数据,直接使用 updates 即可:

with updates as (
 update employees
 set salary = salary - 500
 where employee_id = 206
 returning *
)
select employee_id,first_name, last_name, salary
from updates;

在这里插入图片描述


http://www.niftyadmin.cn/n/4985966.html

相关文章

Springboot整合MyBatisPlus框架操作MySQL

1、MyBatis-Plus概述 MyBatis-Plus (opens new window)&#xff08;简称 MP&#xff09;是一个 MyBatis (opens new window)的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。 【技术储备】 拥有 Java 开发环境以及相应 IDE…

Milk-V Duo开发板实战——基于MobileNetV2的的图像分类

Milk-V Duo开发板实战——基于MobileNetV2的的图像分类 本教程介绍使用TPU-MLIR工具链对MobileNet-Caffe模型进行转换&#xff0c;生成MLIR以及MLIR量化成INT8模型&#xff0c;并在Milk-V Duo开发板上进行部署测试&#xff0c;完成图像分类任务&#xff0c;涉及以下步骤&#…

Java 动态规划 Leetcode 740. 删除并获得点数

题目 对于该题的题目分析&#xff0c;已经代码分析都一并写入到了代码注释中 代码 class Solution {public int deleteAndEarn(int[] nums) {//核心思路&#xff1a;//由于我们获得 nums[i] 的点数之后&#xff0c;就必须删除所有等于 nums[i] - 1 和 nums[i] 1 的元素//假设…

L1-046 整除光棍(Python实现) 测试点全过

题目 这里所谓的“光棍”&#xff0c;并不是指单身汪啦~ 说的是全部由1组成的数字&#xff0c;比如1、11、111、1111等。传说任何一个光棍都能被一个不以5结尾的奇数整除。比如&#xff0c;111111就可以被13整除。 现在&#xff0c;你的程序要读入一个整数x&#xff0c;这个整…

后端开发基础概念

后端开发基础概念 目前处于项目上手阶段&#xff0c;在学习项目过程中&#xff0c;有一些一知半解或者不明白含义的专业名词或者缩写&#xff0c;在此汇总。里面的内容很多都是基于个人理解&#xff0c;水平有限如果有出错的地方还请各位大佬批评指正。 2023年8月31日00:34:22…

SpringBoot—日志

目录 日志使用日志日志级别设置日志级别设置分组指定日志文件路径日志切割归档使用第三方日志框架log4j2配置文件【分级存储】logback配置文件【分级存储】 实例代码 日志 使用日志 给controller添加日志信息 要给controller类上添加Slf4j注解&#xff0c;然后使用log.info(…

MindsDB为许多不支持内置机器学习的数据库带来了机器学习功能

选择平台的首要原则是“靠近数据”,让代码靠近数据是保持低延迟的必要条件。 机器学习,特别是深度学习往往会多次遍历所有数据(遍历一次被称为一个epoch)。对于非常大的数据集来说,理想的情况是在存储数据的地方建立模型,这样就不需要大量的数据传输。目前已经有部分数据…

【运维】hadoop 集群安装(三)hdfs、yarn集群配置、nodemanager健康管理讲解

文章目录 一. 配置说明1. hadoop各进程环境配置2. hadoop各进程配置2.1. etc/hadoop/core-site.xml2.2. etc/hadoop/hdfs-site.xml2.2.1. NameNode2.2.2. datanode 2.3. etc/hadoop/yarn-site.xml2.3.1. ResourceManager and NodeManager2.3.2. ResourceManager2.3.3. NodeMana…