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;
相关推荐
容器( ु⁎ᴗ_ᴗ⁎)ु.。oO20 分钟前
MySQL事务
数据库·mysql
cyt涛2 小时前
MyBatis 学习总结
数据库·sql·学习·mysql·mybatis·jdbc·lombok
Rookie也要加油3 小时前
01_SQLite
数据库·sqlite
liuxin334455663 小时前
教育技术革新:SpringBoot在线教育系统开发
数据库·spring boot·后端
看山还是山,看水还是。3 小时前
MySQL 管理
数据库·笔记·mysql·adb
fishmemory7sec3 小时前
Koa2项目实战2(路由管理、项目结构优化)
数据库·mongodb·koa
momo小菜pa4 小时前
【MySQL 09】表的内外连接
数据库·mysql
Jasonakeke4 小时前
【重学 MySQL】四十九、阿里 MySQL 命名规范及 MySQL8 DDL 的原子化
数据库·mysql
程序猿小D4 小时前
第二百六十九节 JPA教程 - JPA查询OrderBy两个属性示例
java·开发语言·数据库·windows·jpa
小宇成长录4 小时前
Mysql:数据库和表增删查改基本语句
数据库·mysql·数据库备份