PostgreSQL LATERAL 的工作原理

LATERAL 的工作原理

  1. 外部查询生成一行结果:LATERAL 子查询会对每一行外部查询结果进行评估。
  2. LATERAL 子查询执行:对于每一行,LATERAL 子查询会使用该行的列值来执行自己的查询。
  3. 结果合并:子查询的结果与外部查询的行合并,形成最终的结果集。

以下是一个简单的例子来说明 LATERAL 的工作原理:

示例表和数据

假设我们有一个名为 employees 的表:

sql 复制代码
CREATE TABLE employees (
    id SERIAL PRIMARY KEY,
    name TEXT,
    tasks JSONB
);

INSERT INTO employees (name, tasks) VALUES
('Alice', '[{"task": "task1"}, {"task": "task2"}]'),
('Bob', '[{"task": "task3"}, {"task": "task4"}]');

使用 LATERAL 的查询示例

我们想要展开每个员工的任务,并显示员工的名字和每个任务的名字。

sql 复制代码
SELECT
    e.name,
    t.task
FROM
    employees e,
    LATERAL jsonb_array_elements(e.tasks) AS t(task);

查询解释

  1. 外部查询生成每一行结果

    • 第一行:('Alice', '[{"task": "task1"}, {"task": "task2"}]')
    • 第二行:('Bob', '[{"task": "task3"}, {"task": "task4"}]')
  2. LATERAL 子查询执行

    • 对于第一行,jsonb_array_elements('[{"task": "task1"}, {"task": "task2"}]') 展开成两行:
      • {"task": "task1"}
      • {"task": "task2"}
    • 对于第二行,jsonb_array_elements('[{"task": "task3"}, {"task": "task4"}]') 展开成两行:
      • {"task": "task3"}
      • {"task": "task4"}
  3. 结果合并

    • 第一行展开结果合并:
      • ('Alice', 'task1')
      • ('Alice', 'task2')
    • 第二行展开结果合并:
      • ('Bob', 'task3')
      • ('Bob', 'task4')

结果集

plaintext 复制代码
 name  | task
-------|-------
 Alice | task1
 Alice | task2
 Bob   | task3
 Bob   | task4

使用 LATERAL 和 WHERE 子句

假设我们只想筛选出特定的任务,可以在 LATERAL 子查询之后使用 WHERE 子句。

例如,筛选出任务为 task1task3 的记录:

sql 复制代码
SELECT
    e.name,
    t.task
FROM
    employees e,
    LATERAL jsonb_array_elements(e.tasks) AS t(task)
WHERE
    t.task->>'task' IN ('task1', 'task3');

结果集

plaintext 复制代码
 name  | task
-------|-------
 Alice | task1
 Bob   | task3

总结

  • LATERAL 的作用:LATERAL 允许子查询访问外部查询的列,且会对外部查询的每一行进行评估。
  • 子查询与外部查询的关系:子查询可以使用外部查询中的列值来生成结果,并将其合并到最终的结果集中。
  • 临时结果集:LATERAL 子查询的结果会与外部查询的每一行合并,从而形成最终的结果集。

通过使用 LATERAL,我们能够在子查询中引用外部查询的列值,并根据这些列值进行进一步的数据处理和筛选。

sql 复制代码
是的,你可以使用相同的方法来处理具有多个字段的 JSON 数组。假设每个 JSON 对象都有两个字段 `task` 和 `priority`,你可以像这样修改查询来处理它:

```sql
SELECT
    e.name,
    t.task,
    t.priority
FROM
    employees e,
    LATERAL (
        SELECT (elem->>'task') AS task,
               (elem->>'priority') AS priority
        FROM jsonb_array_elements(e.tasks) AS elem
    ) AS t
WHERE
    t.task IN ('task1', 'task3');

在这个查询中,jsonb_array_elements(e.tasks) 将每个 JSON 对象展开成一行,并在子查询中使用 ->> 运算符来提取每个对象的 taskpriority 字段。然后,通过别名 t 引用这些字段。

这样,你就可以处理具有多个字段的 JSON 数组了。

在这个查询中,`LATERAL jsonb_array_elements(e.tasks) AS t(task)` 这一部分是一个 LATERAL JOIN 语句,用于将 JSON 数组 `e.tasks` 中的每个元素展开成一行。让我们来解释一下其中的各个部分:

- `LATERAL`: 这是一个关键字,它告诉数据库引擎在执行查询时按顺序处理 FROM 子句中的表达式,确保对每一行计算一次。在这里,它确保对 `jsonb_array_elements(e.tasks)` 返回的每个元素都进行处理。

- `jsonb_array_elements(e.tasks)`: 这是一个 JSON 函数,它将 JSON 数组 `e.tasks` 中的每个元素作为一个行返回。例如,如果 `e.tasks` 是 `["task1", "task2", "task3"]`,那么这个函数将返回三行,每行一个任务。

- `AS t(task)`: 这部分给返回的行定义了一个别名 `t`,并指定了一个列别名 `task`。在查询的后续部分,你可以使用 `t.task` 来引用 `jsonb_array_elements(e.tasks)` 返回的每一行中的 `task` 列。

所以,整个 LATERAL JOIN 的作用是将 JSON 数组 `e.tasks` 中的每个元素拆分成一行,并将每个元素赋值给名为 `t` 的临时表,该表只有一个名为 `task` 的列。


**可以选择第几个对象**
```sql
SELECT
    e.name,
    t.task,
    t.priority
FROM
    employees e,
    LATERAL (
        SELECT (elem->>'task') AS task,
               (elem->>'priority') AS priority,
							 ROW_NUMBER() OVER () AS rn
        FROM jsonb_array_elements(e.tasks) AS elem
    ) AS t
		left join 
WHERE
t.rn = 1;
相关推荐
PGCCC25 分钟前
【PGCCC】Postgresql 存储设计
数据库·postgresql
PcVue China2 小时前
PcVue + SQL Grid : 释放数据的无限潜力
大数据·服务器·数据库·sql·科技·安全·oracle
魔道不误砍柴功4 小时前
简单叙述 Spring Boot 启动过程
java·数据库·spring boot
锐策4 小时前
〔 MySQL 〕数据库基础
数据库·mysql
远歌已逝5 小时前
管理Oracle实例(二)
数据库·oracle
日月星宿~5 小时前
【MySQL】summary
数据库·mysql
爱吃土豆的程序员5 小时前
在oracle官网下载资源显示400 Bad Request Request Header Or Cookie Too Large 解决办法
java·数据库·oracle·cookie
睿思达DBA_WGX6 小时前
Oracle 11g rac 集群节点的修复过程
数据库·oracle
尘浮生6 小时前
Java项目实战II基于微信小程序的移动学习平台的设计与实现(开发文档+数据库+源码)
java·开发语言·数据库·spring boot·学习·微信小程序·小程序
Leo.yuan7 小时前
数据量大Excel卡顿严重?选对报表工具提高10倍效率
数据库·数据分析·数据可视化·powerbi