【PostgreSQL】 JSON数组 提取根据索引提取对象和字段

在 PostgreSQL 中处理 JSON 数组:按索引提取对象和字段

在现代应用程序中,JSON 数据格式因其灵活性和可读性广泛应用。PostgreSQL 作为一个强大的关系型数据库管理系统,提供了强大的 JSON 数据类型和函数支持,使得在数据库中存储和操作 JSON 数据变得方便高效。在这篇博客中,我们将探讨如何在 PostgreSQL 中从 JSON 数组中按索引提取对象,提取对象中的字段,并使用 LATERAL 子查询和 ROW_NUMBER() 进行进一步处理。

场景描述

假设我们有一张名为 dfv_visiting_plan 的表,其中的 JSON 字段 schedule 存储了多个包含随访任务的 JSON 对象。我们希望能够从这个 JSON 数组中提取第一个对象的 commenceDate 字段,并根据这个字段进行筛选和统计。

示例数据

首先,让我们创建一个示例表并插入一些数据:

CREATE TABLE dfv_visiting_plan (id serial PRIMARY KEY,schedule jsonb
);INSERT INTO dfv_visiting_plan (schedule) VALUES
('[{"orderNo": 0,"name": "出院三个月后第一次随访任务","planDate": "2024-04-24T08:00:00.000Z","commenceDate": "2024-04-17T08:00:00.000Z","deadline": "2024-05-01T08:00:00.000Z","visitWay": 1,"offset": 7,"offsetUnit": "day","duration": 3,"durationUnit": "month","forms": ["stroke-recovery-beijin"],"status": 0},{"orderNo": 1,"name": "出院三个月后第二次随访任务","planDate": "2024-05-24T08:00:00.000Z","commenceDate": "2024-05-17T08:00:00.000Z","deadline": "2024-06-01T08:00:00.000Z","visitWay": 1,"offset": 7,"offsetUnit": "day","duration": 3,"durationUnit": "month","forms": ["stroke-recovery-beijin"],"status": 0}
]');

方法一:LATERAL 和 json_array_elements 函数

是使用 json_array_elements 将 JSON 数组展开为多行,然后仅选择第一个对象。我们可以借助 ROW_NUMBER() 窗口函数实现这一点。

1.首先找到单条数据的json字段
2.使用json_array_elements 函数将该字段 展开


SELECT(elem->>'commenceDate')::timestamp AS commenceDate
FROMdfv_visiting_plan plan,LATERAL (SELECTelem,ROW_NUMBER() OVER () AS rnFROMjson_array_elements(plan.schedule) AS elem) AS subquery
WHEREplan.id = '72e823b2b6224c7985752806efb513e2'AND subquery.rn = 2;

3.使用CTE/或者LATERAL

第一中使用 CTE

WITH expanded AS (SELECTplan.id,(elem->>'commenceDate')::timestamp AS commenceDate,ROW_NUMBER() OVER (PARTITION BY plan.id ORDER BY plan.id) AS rnFROMdfv_visiting_plan plan,LATERAL json_array_elements(plan.schedule) AS elem
)SELECTplan.id,expanded.commenceDate
FROMdfv_visiting_plan plan
JOINexpanded ON plan.id = expanded.id
WHEREexpanded.rn = 1AND expanded.commenceDate >= '2023-01-28';

当然,以下是对该 SQL 查询中的各个部分及其涉及的语法的详细讲解:

1. 公共表表达式 (CTE)

WITH expanded AS (SELECTplan.id,(elem->>'commenceDate')::timestamp AS commenceDate,ROW_NUMBER() OVER (PARTITION BY plan.id ORDER BY plan.id) AS rnFROMdfv_visiting_plan plan,LATERAL json_array_elements(plan.schedule) AS elem
)
a. WITH expanded AS (...)

WITH 语句定义了一个公共表表达式 (CTE),其名称为 expanded。CTE 是一个临时结果集,供随后查询中的主查询使用。

b. SELECT ...
SELECTplan.id,(elem->>'commenceDate')::timestamp AS commenceDate,ROW_NUMBER() OVER (PARTITION BY plan.id ORDER BY plan.id) AS rn
  • plan.id: 从表 dfv_visiting_plan 中选择 id 列。
  • (elem->>'commenceDate')::timestamp AS commenceDate: 使用 ->> 运算符从 JSON 对象中提取 commenceDate 字段,将其转换为文本,然后将其转换为 timestamp 类型。
  • ROW_NUMBER() OVER (PARTITION BY plan.id ORDER BY plan.id) AS rn: 使用窗口函数 ROW_NUMBER() 为每组 plan.id 分配一个唯一的行号,按 plan.id 排序。PARTITION BY 子句将数据按 plan.id 分区,ORDER BY 子句指定排序顺序。
c. FROM dfv_visiting_plan plan, LATERAL json_array_elements(plan.schedule) AS elem
  • dfv_visiting_plan plan: 查询表 dfv_visiting_plan,别名为 plan
  • LATERAL json_array_elements(plan.schedule) AS elem: LATERAL 允许子查询中的每一行都能引用外部查询中的列。json_array_elements(plan.schedule) 将 JSON 数组展开为多个 JSON 对象,每个对象作为一行,别名为 elem

2. 主查询

SELECTplan.id,expanded.commenceDate
FROMdfv_visiting_plan plan
JOINexpanded ON plan.id = expanded.id
WHEREexpanded.rn = 1AND expanded.commenceDate >= '2023-01-28';
a. SELECT plan.id, expanded.commenceDate

选择 dfv_visiting_plan 表中的 id 列和 CTE expanded 中的 commenceDate 列。

b. FROM dfv_visiting_plan plan JOIN expanded ON plan.id = expanded.id

dfv_visiting_plan 表和 CTE expanded 中进行连接:

  • JOIN: 使用内部连接将两个结果集结合起来。
  • ON plan.id = expanded.id: 指定连接条件,基于 id 列匹配。
c. WHERE expanded.rn = 1 AND expanded.commenceDate >= '2023-01-28'
  • expanded.rn = 1: 过滤条件,选择行号为 1 的记录,即 JSON 数组中的第一个对象。
  • expanded.commenceDate >= '2023-01-28': 过滤条件,选择 commenceDate 字段大于或等于 2023-01-28 的记录。

整个查询流程总结

  1. CTE 定义 (expanded): 将 dfv_visiting_plan 表中的 schedule 列展开为 JSON 数组中的各个对象,并提取每个对象的 commenceDate 字段。使用窗口函数 ROW_NUMBER() 为每个 plan.id 分配行号。
  2. 主查询: 从 dfv_visiting_plan 表和 CTE expanded 中选择数据,进行内部连接,并应用过滤条件来筛选所需的记录。

具体

WITH expanded_schedule AS (SELECTpa.tenant_code,plan.status,schedule_elem->>'commenceDate' AS commence_date,ROW_NUMBER() OVER (PARTITION BY pa.tenant_code ORDER BY (schedule_elem->>'commenceDate')::timestamp) AS rnFROMdfv_visiting_plan planLEFT JOIN dfv_patient_case pcase ON pcase.ID = plan.idLEFT JOIN dfv_patient pa ON pa.ID = pcase.patient_id,LATERAL jsonb_array_elements(plan.schedule) AS schedule_elem
)
SELECTtenant_code,COUNT(*) FILTER (WHERE 1 = 1) AS,COUNT(*) FILTER (WHERE status IN (2, 3)) AS,commence_date
FROMexpanded_schedule
WHERErn = 1AND commence_date::timestamp >= '2023-01-28'::timestamp
GROUP BYtenant_code, commence_date;

好的,让我们更详细地讲解这三个知识点:

1. 使用 ->> 运算符提取 JSON 字段并转换类型

在 PostgreSQL 中,JSON 数据类型支持各种操作符和函数来访问和操作 JSON 数据。其中,->> 运算符用于从 JSON 对象中提取字段值并将其转换为文本。

(elem->>'commenceDate')::timestamp AS commenceDate
a. ->> 运算符
  • elem->>'commenceDate':从 JSON 对象 elem 中提取 commenceDate 字段的值并将其转换为文本。这是因为 ->> 运算符返回的是 text 类型,而 -> 运算符返回的是 jsonb 类型。
b. 类型转换
  • ::timestamp:将文本类型的 commenceDate 转换为 timestamp 类型。这样可以在 SQL 查询中对日期和时间进行比较和操作。

2. 窗口函数 ROW_NUMBER()

窗口函数 ROW_NUMBER() 为结果集中的每一行分配唯一的行号。窗口函数在 OVER 子句的控制下执行,能够根据特定的排序和分区规则对行进行编号。

ROW_NUMBER() OVER (PARTITION BY plan.id ORDER BY plan.id) AS rn
a. ROW_NUMBER()
  • ROW_NUMBER():窗口函数,为结果集中的每一行生成一个唯一的行号,起始值为1。
b. OVER 子句
  • OVER (PARTITION BY plan.id ORDER BY plan.id):定义了窗口函数的分区和排序规则。
    • PARTITION BY plan.id:将结果集按 plan.id 分区。每个分区内的行号从1开始重新编号。
    • ORDER BY plan.id:指定分区内行的排序顺序。按照 plan.id 排序,如果不指定排序顺序,默认是升序。

3. LATERALjson_array_elements 函数

a. LATERAL

LATERAL 允许子查询中的每一行引用外部查询中的列。它使得子查询能够依赖于外部查询的上下文。

LATERAL (SELECTelem,ROW_NUMBER() OVER (PARTITION BY plan.id ORDER BY plan.id) AS rnFROMjson_array_elements(plan.schedule) AS elem
)
  • LATERAL 子查询:在 FROM 子句中使用 LATERAL,使子查询能够访问外部查询中的列。在这种情况下,子查询可以引用 plan.schedule 列。
b. json_array_elements 函数

json_array_elements 是一个 PostgreSQL 函数,用于将 JSON 数组展开为多个 JSON 值,每个值作为一行输出。

  • json_array_elements(plan.schedule) AS elem:将 plan.schedule 中的 JSON 数组展开为多个 JSON 对象,每个对象作为一行,别名为 elem

具体示例讲解

假设 dfv_visiting_plan 表中有一行数据,schedule 列包含以下 JSON 数组:

[{"commenceDate": "2024-03-03T06:34:08.811Z"},{"commenceDate": "2024-04-14T06:34:08.811Z"}
]

通过以下查询,我们可以提取数组中的第一个 commenceDate 字段并将其转换为 timestamp 类型:

WITH expanded AS (SELECTplan.id,(elem->>'commenceDate')::timestamp AS commenceDate,ROW_NUMBER() OVER (PARTITION BY plan.id ORDER BY plan.id) AS rnFROMdfv_visiting_plan plan,LATERAL json_array_elements(plan.schedule) AS elem
)
SELECTplan.id,expanded.commenceDate
FROMdfv_visiting_plan plan
JOINexpanded ON plan.id = expanded.id
WHEREexpanded.rn = 1AND expanded.commenceDate >= '2023-01-28';

查询流程

  1. CTE 定义 (expanded)

    • 使用 LATERALjson_array_elements 展开 plan.schedule 中的 JSON 数组,每个元素成为一行。
    • 使用 ->> 运算符从 JSON 对象中提取 commenceDate 字段,转换为文本,再转换为 timestamp
    • 使用 ROW_NUMBER() 窗口函数按 plan.id 分区和排序,为每组分配一个行号。
  2. 主查询

    • dfv_visiting_plan 表和 expanded CTE 中选择数据。
    • 使用内部连接 JOIN,基于 id 进行匹配。
    • WHERE 子句中过滤出 rn = 1(第一个 JSON 对象)且 commenceDate >= '2023-01-28' 的记录。

通过上述步骤,最终我们可以提取并筛选出 JSON 数组中指定的 commenceDate 字段。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/18967.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SD-WAN:跨国公司的组网方式

在经济全球化的时代,跨国企业需要与国外客户、供应商和合作伙伴进行快速的互动和沟通,并且会在国外建立办公指点,传统的WAN(大面积网络)架构已不能满足快速、可靠、安全的网络通信需求,可以采用SD-WAN的方式…

怎么将3D模型转换立面图---模大狮模型网

在建筑设计、室内设计以及产品建模等领域,经常需要将3D模型转换为立面图以进行展示、分析或交流。立面图能够清晰地呈现物体的外观和结构,是设计和施工中不可或缺的一部分。 一、导出3D模型 首先,需要将3D模型导出为CAD软件能够识别的格式。…

第十九节:带你梳理Vue2: 父组件向子组件传参(props传参)

1. 组件嵌套 1.1 组件的嵌套使用 之前有说过,Vue组件跟Vue实例是一样的,因此在Vue中一个组件中也可以定义并使用自己的局部组件,这就是组件的嵌套使用 例如:示例代码如下: <div id"app"><!-- 3. 使用组件 --><my-component></my-component&…

29-ESP32-S3-WIFI_Driver-00 STA模式扫描全部 AP

ESP32-S3 WIFI_Driver 引言 ESP32-S3是一款集成了Wi-Fi和蓝牙功能的芯片。关于WIFI的部分&#xff0c;其实内容比我想象的要多得多。所以通常来说&#xff0c;如果你想要编写自己的Wi-Fi应用程序&#xff0c;最快捷的方法就是先找一个类似的示例应用&#xff0c;然后将它的相…

VSCODE常用插件记录

重点提名&#xff1a; back & ForthBookmarksC/ChighlightSSH FS //SSH插件

JVM之垃圾判断的详细解析

垃圾判断 垃圾介绍 垃圾&#xff1a;如果一个或多个对象没有任何的引用指向它了&#xff0c;那么这个对象现在就是垃圾 作用&#xff1a;释放没用的对象&#xff0c;清除内存里的记录碎片&#xff0c;碎片整理将所占用的堆内存移到堆的一端&#xff0c;以便 JVM 将整理出的内…

三大主流框架

Web前端开发领域中&#xff0c;三大主流框架通常指的是&#xff1a; React&#xff1a;由Facebook开发的一个用于构建用户界面的JavaScript库。React以其组件化、声明式编程和虚拟DOM等特点而广受欢迎&#xff0c;能够高效地更新和渲染大型应用。 Vue.js&#xff1a;由尤雨溪创…

第3章 数据链路层

王道学习 考纲内容 &#xff08;一&#xff09;数据链路层的功能 &#xff08;二&#xff09;组帧 &#xff08;三&#xff09;差错控制 检错编码&#xff1b;纠错编码 &#xff08;四&#xff09;流量控制与可靠传输机制 流量控制、可靠传输与滑动窗口…

AcWing 1600:完全二叉树

【题目来源】https://www.acwing.com/problem/content/1602/【题目描述】 给定一个树&#xff0c;请你判断它是否是完全二叉树。【输入格式】 第一行包含整数 N&#xff0c;表示树的结点个数。 树的结点编号为 0∼N−1。 接下来 N 行&#xff0c;每行对应一个结点&#xff0c;并…

使用 Spring Boot 实现邮件发送功能

推荐一个AI网站&#xff0c;免费使用豆包AI模型&#xff0c;快去白嫖&#x1f449;海鲸AI SpringBoot——发送邮件 在现代应用程序中&#xff0c;发送邮件是一个常见的需求。本文将介绍如何使用 Spring Boot 发送邮件。我们将从新建一个 Spring Boot 项目开始&#xff0c;逐步…

3dmax渲染经常卡主?关掉光追即可流畅渲染

3ds Max是一款广泛应用于三维建模、动画和渲染的软件&#xff0c;它在影视、游戏、建筑可视化等领域具有重要地位。在3ds Max中&#xff0c;渲染技术的选择和应用直接影响到最终图像的质量和渲染效率。 但在实际使用过程中&#xff0c;由于3dsMax中有太多选项&#xff0c;很多…

# 解决 win11 连接共享打印机,报错 0x00000709 问题

解决 win11 连接共享打印机&#xff0c;报错 0x00000709 问题 一、问题描述&#xff1a; 当我们连接一台共享打印机&#xff0c;出现报错 0x00000709 时&#xff0c;这是由于本机注册表本配置 RPC 远程调用&#xff0c;我们需要对自己的电脑进行修改&#xff0c;而不是主机&a…

什么是 ISP 代理?

代理是路由互联网流量的中间服务器&#xff0c;通常分为三类&#xff1a;数据中心、住宅和 ISP。根据定义&#xff0c;ISP 代理隶属于互联网服务提供商&#xff0c;但实际上&#xff0c;更容易将它们视为数据中心和住宅代理的组合。 让我们仔细研究一下 ISP 代理&#xff0c;看…

国产操作系统上部署SVN版本控制系统

原文链接&#xff1a;国产操作系统上部署SVN版本控制系统 | 统信 | 麒麟 | 中科方德 Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇在国产操作系统上部署SVN版本控制系统的文章。SVN&#xff08;Subversion&#xff09;是一款广泛使用的版本控制系统&#xff0c;它…

【一步一步了解Java系列】:类与对象的联系

看到这句话的时候证明&#xff1a;此刻你我都在努力加油陌生人个人主页&#xff1a;Gu Gu Study专栏&#xff1a;一步一步了解Java 喜欢的一句话&#xff1a; 常常会回顾努力的自己&#xff0c;所以要为自己的努力留下足迹 喜欢的话可以点个赞谢谢了。 作者&#xff1a;小闭 对…

真实故障分享,H3C ER3208G3-X路由器-双绞线一闪一停

六类非屏蔽双绞线 网线钳 如上图所示&#xff0c;2号线接到h3c路由器出现网线一闪一停&#xff0c;用对线器测试一到8芯能一一对应&#xff0c;无法上网。2号线接到h3c交换机能正常上网&#xff0c;难道是网线对568A 568B有要求&#xff1f; 解决方式&#xff1a;通过两端568…

一文讲清楚:如何做好建设工程项目管理?

在房地产开发中&#xff0c;作为项目负责人我目前的状况成了一个大管家&#xff0c;还要管理工程质量。上至各部门领导的关系维护&#xff0c;下到工人的吃喝拉撒都要我操心&#xff0c;还要没完没了的处理四邻纠纷和拆迁户的纠纷&#xff0c;每天都搞得很疲惫&#xff0c;如何…

elementUI type=“selection“多选框选中 删除 回显 赋值问题 回显数组改变选中状态未改变

业务需求&#xff1a; 点击查询弹列表框 勾选列表选项保存 可删除可重新查询列表添加 遇到的问题&#xff1a;删除之后查询列表selection回显问题 解决&#xff1a;row-click配合:reserve-selection"true"使用 <el-tableref"refPlanTable":data"…

vue3 + ts 实现IP地址及Mac地址输入框功能

1、组件完成代码 <template><div class"ip-input"><div v-for"(item, index) in ipArr" :key"index" class"ip-input__item-wrap"><input ref"ipInput" v-model"ipArr[index]" type"t…

【Python Cookbook】S01E01 将长度为N的序列分解为N个单独的变量

目录 问题解决方案讨论 问题 将一个包含 N N N 个元素的元组或者序列&#xff0c;现在想将其分解为 N N N 个单独的变量。 解决方案 任何序列都可以通过简单的赋值操作分解为单独的变量&#xff1a; p (4, 5) x, y p print("x", x) print("y", y)唯…