PostgreSQL-常用函数和操作符-2

0. WITH

在 PL/pgSQL 中,WITH 子句通常用于创建一个临时结果集,这个结果集在执行 SQL 查询时使用。这个临时结果集通常被称为一个公共表表达式(Common Table Expression, CTE)。CTE 允许您在查询中引用它,就像引用一个表一样。

基本语法:

sql 复制代码
WITH [RECURSIVE] cte_name AS (
  [query]
)
-- 使用
SELECT * FROM cte_name;
  • cte_name:您为 CTE 指定的名称。
  • RECURSIVE\]:可选的,用于标记递归 CTE。

案例:

  • 下面是一个使用 WITH 语句的简单具体案例。假设我们有一个名为orders的表,它记录了订单的信息,包括订单ID、客户ID、订单日期和订单金额。我们想要找出每个客户的总订单金额,并找出订单金额超过1000的客户。

  • 不使用 WITH 语句的查询可能会像这样:

sql 复制代码
SELECT customer_id, SUM(order_amount) AS total_amount  
FROM orders  
GROUP BY customer_id  
HAVING SUM(order_amount) > 1000;

要求:

  • 如果我们想要进一步筛选这些客户,比如只选择那些在某个日期之后下过订单的客户,我们可以使用 WITH 语句来使查询更清晰:
sql 复制代码
WITH customer_totals AS (  
    SELECT customer_id, SUM(order_amount) AS total_amount  
    FROM orders  
    GROUP BY customer_id  
)  
SELECT ct.customer_id, ct.total_amount  
FROM customer_totals ct  
JOIN orders o ON ct.customer_id = o.customer_id  
WHERE o.order_date > '2023-01-01' -- 假设我们只关心2023年1月1日之后的订单  
AND ct.total_amount > 1000; -- 订单金额超过1000

解析:

  • 在这个例子中,我们首先使用 WITH 语句创建了一个名为customer_totals的CTE,它包含了每个客户的总订单金额。然后,在主查询中,我们从这个CTE中选择数据,并与orders表进行连接,以找出在指定日期之后下过订单且总订单金额超过1000的客户。
  • 使用 WITH 语句可以使查询更加模块化,并提高可读性,特别是当查询变得复杂时。

1. WITH RECURSIVE - 递归结果集

具体案例:with recursive

  • 问题:

    我们有一个简单的员工表employees,其中包含了员工的ID、姓名、职位和经理ID(表示上级经理的ID)。

    我们想要构建一个CTE来找到某个员工的所有上级经理,直到CEO(假设CEO没有上级经理)。

  • 实现:

    表的结构可能如下:

sql 复制代码
employees table:
+----+-------+---------+----------+
| id | name  | title   | manager_id |
+----+-------+---------+----------+
| 1  | Alice | CEO     | NULL     |
| 2  | Bob   | Manager | 1        |
| 3  | Carol | Staff   | 2        |
| 4  | Dave  | Staff   | 2        |
| 5  | Eve   | Manager | 1        |
| 6  | Frank | Staff   | 5        |
+----+-------+---------+----------+

我们想要找到员工Carol的所有上级经理。我们可以使用WITH RECURSIVE语句和UNION来构建一个CTE,如下所示:

sql 复制代码
-- 创建查询过程
WITH RECURSIVE managers_chain AS (
    -- 非递归部分:找到Carol的直接上级
    SELECT manager_id
    FROM employees
    WHERE id = 3 -- 假设Carol的ID是3

    UNION

    -- 递归部分:找到上级经理的上级,直到CEO
    SELECT e.manager_id
    FROM employees e
    INNER JOIN managers_chain mc ON e.id = mc.manager_id
    WHERE e.manager_id IS NOT NULL
)

-- 查询
SELECT * FROM managers_chain;
  • 解析:
    这个CTE的工作原理如下:
    非递归部分(种子查询)选择了Carol的直接上级的ID。
    递归部分通过UNION与非递归部分合并,它会反复执行,每次都使用前一次查询结果中的manager_id来找到下一级的上级经理,直到达到CEO(即manager_id为NULL)。
    最终,CTE的结果将是一个包含Carol所有上级经理ID的列表。当我们执行SELECT * FROM managers_chain;时,我们将得到以下结果:
sql 复制代码
manager_id
-----------
2
1

这表示Carol的直接上级是Bob(ID为2),Bob的上级是Alice(ID为1),即CEO。

2. UNION

UNION 是 SQL(结构化查询语言)中的一个操作符,用于合并两个或多个 SELECT 语句的结果集。这些 SELECT 语句必须选择相同数量的列,并且这些列的数据类型也必须相似或兼容。UNION 会自动去除重复的行,而如果你想要包含重复的行,可以使用 UNION ALL。

基本语法:

sql 复制代码
SELECT column_name(s) FROM table1  
UNION  
SELECT column_name(s) FROM table2;

案例:

假设我们有两个表,employees_ny 和 employees_sf,分别存储纽约和旧金山的员工信息。这两个表的结构是相同的,都有 id、name 和 salary 三个字段。

  • 表结构

employees_ny 表:

sql 复制代码
id	name	salary
1	Alice	5000
2	Bob	6000
3	Carol	5500

employees_sf 表:

sql 复制代码
id	name	salary
4	Dave	7000
5	Eve	5000
6	Frank	6500
  • 问题:
    如果我们想要查询纽约和旧金山所有工资超过 5000 的员工,并合并结果,可以使用 UNION:
sql 复制代码
SELECT name, salary FROM employees_ny WHERE salary >= 5000  
UNION  
SELECT name, salary FROM employees_sf WHERE salary >= 5000;

注:

结果集可能如下(注意结果中的 "Alice" 和 "Eve" 工资都是 5000,但由于 UNION 会去除重复行,所以 "Alice" 只会出现一次):

  • 结果:
sql 复制代码
name	salary
Bob	6000
Carol	5500
Dave	7000
Eve	5000
Frank	6500

如果你想要保留所有行,包括重复的行,可以使用 UNION ALL:

sql 复制代码
SELECT name, salary FROM employees_ny WHERE salary >= 5000  
UNION ALL  
SELECT name, salary FROM employees_sf WHERE salary >= 5000;

这样,"Alice" 将会在两次结果中都出现,因为 UNION ALL 不会去除重复的行。

3. 持续更新中...

相关推荐
百***92022 分钟前
【MySQL】MySQL库的操作
android·数据库·mysql
q***76663 分钟前
Spring Boot 从 2.7.x 升级到 3.3注意事项
数据库·hive·spring boot
信仰_27399324316 分钟前
Redis红锁
数据库·redis·缓存
人间打气筒(Ada)24 分钟前
Centos7 搭建hadoop2.7.2、hbase伪分布式集群
数据库·分布式·hbase
心灵宝贝31 分钟前
如何在 Mac 上安装 MySQL 8.0.20.dmg(从下载到使用全流程)
数据库·mysql·macos
奋斗的牛马1 小时前
OFDM理解
网络·数据库·单片机·嵌入式硬件·fpga开发·信息与通信
忧郁的橙子.2 小时前
一、Rabbit MQ 初级
服务器·网络·数据库
杰杰7982 小时前
SQL 实战:用户访问 → 下单 → 支付全流程转化率分析
数据库·sql
爬山算法2 小时前
Redis(120)Redis的常见错误如何处理?
数据库·redis·缓存
野生技术架构师3 小时前
盘一盘Redis的底层数据结构
数据结构·数据库·redis