9.16. JSON 函数和运算符#
本节介绍
用于处理和创建 JSON 数据的函数和运算符
SQL/JSON 路径语言
为了在 SQL 环境中提供对 JSON 数据类型的本机支持,PostgreSQL实现了SQL/JSON 数据模型。此模型包含一系列项。每项可以保存 SQL 标量值,以及一个额外的 SQL/JSON null 值,以及使用 JSON 数组和对象的复合数据结构。该模型是对 JSON 规范RFC 7159中隐含数据模型的规范化。
SQL/JSON 允许您处理 JSON 数据以及常规 SQL 数据,并提供事务支持,包括
将 JSON 数据上传到数据库并将其作为字符或二进制字符串存储在常规 SQL 列中。
从关系数据生成 JSON 对象和数组。
使用 SQL/JSON 查询函数和 SQL/JSON 路径语言表达式查询 JSON 数据。
要了解有关 SQL/JSON 标准的更多信息,请参见[sqltr-19075-6]。有关PostgreSQL中支持的 JSON 类型的详细信息,请参见第 8.14 节。
9.16.1. 处理和创建 JSON 数据#
表 9.45显示了可用于 JSON 数据类型的运算符(参见第 8.14 节)。此外,表 9.1中显示的常用比较运算符可用于jsonb
,但不能用于json
。比较运算符遵循第 8.14.4 节中概述的 B 树操作排序规则。另请参见第 9.21 节,了解聚合函数json_agg
(将记录值聚合为 JSON)、聚合函数json_object_agg
(将值对聚合为 JSON 对象)及其jsonb
等效项jsonb_agg
和jsonb_object_agg
。
表 9.45.json
和jsonb
运算符
运算符 说明 示例 |
---|
提取 JSON 数组的第
|
使用给定的键提取 JSON 对象字段。
|
提取 JSON 数组的第
|
提取 JSON 对象字段,作为
|
提取指定路径处的 JSON 子对象,其中路径元素可以是字段键或数组索引。
|
提取指定路径处的 JSON 子对象,作为
|
注意
如果 JSON 输入没有与请求匹配的正确结构,则字段/元素/路径提取运算符返回 NULL,而不是失败;例如,如果不存在此类键或数组元素。
某些其他运算符仅适用于jsonb
,如表 9.46所示。第 8.14.4 节介绍了如何使用这些运算符有效搜索已编制索引的jsonb
数据。
表 9.46. 其他jsonb
运算符
运算符 说明 示例 |
---|
第一个 JSON 值是否包含第二个值?(有关包含的详细信息,请参见 第 8.14.3 节。)
|
第一个 JSON 值是否包含在第二个值中?
|
文本字符串是否存在于 JSON 值内的顶级键或数组元素中?
|
文本数组中的任何字符串是否存在于顶级键或数组元素中?
|
文本数组中的所有字符串是否存在于顶级键或数组元素中?
|
连接两个
要将数组作为单个条目附加到另一个数组,请将其包装在额外的数组层中,例如
|
从 JSON 对象中删除一个键(及其值),或从 JSON 数组中删除匹配的字符串值。
|
从左操作数中删除所有匹配的键或数组元素。
|
删除具有指定索引的数组元素(负整数从末尾开始计数)。如果 JSON 值不是数组,则会引发错误。
|
删除指定路径处的字段或数组元素,其中路径元素可以是字段键或数组索引。
|
JSON 路径是否为指定 JSON 值返回任何项?
|
返回指定 JSON 值的 JSON 路径谓词检查结果。仅考虑结果的第一项。如果结果不是布尔值,则返回
|
注意
jsonpath
运算符@?
和@@
抑制以下错误:缺少对象字段或数组元素、意外的 JSON 项类型、日期时间和数字错误。下面描述的jsonpath
相关函数也可以抑制这些类型的错误。在搜索结构不同的 JSON 文档集合时,此行为可能会有所帮助。
表 9.47显示了可用于构造json
和jsonb
值的函数。此表中的一些函数具有RETURNING
子句,该子句指定返回的数据类型。它必须是json
、jsonb
、bytea
、字符字符串类型(text
、char
、varchar
或nchar
)或存在从json
到该类型的强制转换的类型。默认情况下,返回json
类型。
表 9.47. JSON 创建函数
函数 说明 示例 |
---|
将任何 SQL 值转换为
|
将 SQL 数组转换为 JSON 数组。行为与
|
从一系列
|
将 SQL 复合值转换为 JSON 对象。行为与
|
从可变参数列表中构建一个可能具有异构类型的 JSON 数组。每个参数都将按照
|
根据可变参数列表构建一个 JSON 对象。根据惯例,参数列表由交替的键和值组成。键参数强制转换为文本;值参数按照
|
构建一个 JSON 对象,包含给定所有键/值对,如果未给出,则构建一个空对象。
|
使用文本数组构建 JSON 对象。该数组必须具有一个维度,且成员数为偶数,在这种情况下,它们被视为交替的键/值对,或者具有两个维度,使得每个内部数组恰好具有两个元素,它们被视为键/值对。所有值都转换为 JSON 字符串。
|
此形式的
|
表 9.48详细介绍了用于测试 JSON 的 SQL/JSON 工具。
表 9.48. SQL/JSON 测试函数
表 9.49显示了可用于处理json
和jsonb
值的函数。
表 9.49。JSON 处理函数
函数 说明 示例 |
---|
将顶级 JSON 数组扩展为一组 JSON 值。
value ----------- 1 true [2,false] |
将顶级 JSON 数组扩展为一组
value ----------- foo bar |
返回顶级 JSON 数组中的元素数。
|
将顶级 JSON 对象扩展为一组键/值对。
key | value -----+------- a | "foo" b | "bar" |
将顶级 JSON 对象扩展为一组键/值对。返回的
key | value -----+------- a | foo b | bar |
提取指定路径处的 JSON 子对象。(这在功能上等同于
|
将指定路径处的 JSON 子对象提取为
|
返回顶级 JSON 对象中的键集。
json_object_keys ------------------ f1 f2 |
将顶级 JSON 对象扩展为具有 要将 JSON 值转换为输出列的 SQL 类型,将按顺序应用以下规则
虽然下面的示例使用常量 JSON 值,但典型用法是引用查询的
a | b | c ---+-----------+------------- 1 | {2,"a b"} | (4,"a b c") |
将对象的最顶层 JSON 数组扩展到一组行,这些行具有
a | b ---+--- 1 | 2 3 | 4 |
将顶级 JSON 对象扩展到具有
a | b | c | d | r ---+---------+---------+---+--------------- 1 | [1,2,3] | {1,2,3} | | (123,"a b c") |
将顶级 JSON 对象数组扩展到具有
a | b ---+----- 1 | foo 2 | |
返回
|
如果
|
返回插入了
|
从给定的 JSON 值中递归删除所有值为 null 的对象字段。未作为对象字段的 null 值保持不变。
|
检查 JSON 路径是否为指定的 JSON 值返回任何项目。如果指定了
|
返回指定 JSON 值的 JSON 路径谓词检查结果。仅考虑结果的第一项。如果结果不是布尔值,则返回
|
返回指定 JSON 值的 JSON 路径返回的所有 JSON 项。可选
jsonb_path_query ------------------ 2 3 4 |
将指定 JSON 值的 JSON 路径返回的所有 JSON 项作为 JSON 数组返回。可选
|
返回 JSON 路径针对指定 JSON 值返回的第一个 JSON 项。如果没有任何结果,则返回
|
这些函数的行为与其上面描述的带有
|
将给定的 JSON 值转换为漂亮的缩进文本。
[ { "f1": 1, "f2": null }, 2 ] |
以文本字符串的形式返回顶级 JSON 值的类型。可能类型包括
|
9.16.2. SQL/JSON 路径语言#
SQL/JSON 路径表达式指定要从 JSON 数据中检索的项目,类似于用于 XML 的 SQL 访问的 XPath 表达式。在PostgreSQL中,路径表达式作为jsonpath
数据类型实现,并且可以使用第 8.14.7 节中描述的任何元素。
JSON 查询函数和运算符将提供的路径表达式传递给路径引擎进行评估。如果表达式与查询的 JSON 数据匹配,则将返回相应的 JSON 项目或项目集。路径表达式使用 SQL/JSON 路径语言编写,并且可以包括算术表达式和函数。
路径表达式由jsonpath
数据类型允许的一系列元素组成。路径表达式通常从左到右进行评估,但可以使用括号来更改运算顺序。如果评估成功,将生成一系列 JSON 项目,并将评估结果返回给完成指定计算的 JSON 查询函数。
要引用正在查询的 JSON 值(上下文项),请在路径表达式中使用$
变量。它后面可以跟一个或多个访问器运算符,这些运算符逐层深入 JSON 结构以检索上下文项的子项。每个后续运算符都处理前一个评估步骤的结果。
例如,假设您有一些来自 GPS 跟踪器的 JSON 数据,您希望对其进行解析,例如
{
"track": {
"segments": [
{
"location": [ 47.763, 13.4034 ],
"start time": "2018-10-14 10:05:14",
"HR": 73
},
{
"location": [ 47.706, 13.2635 ],
"start time": "2018-10-14 10:39:21",
"HR": 135
}
]
}
}
要检索可用的轨迹段,您需要使用.*
key*
访问器运算符来遍历周围的 JSON 对象
$.track.segments
要检索数组的内容,您通常使用[*]
运算符。例如,以下路径将返回所有可用轨迹段的位置坐标
$.track.segments[*].location
要仅返回第一段的坐标,您可以在[]
访问器运算符中指定相应的下标。请记住,JSON 数组索引从 0 开始
$.track.segments[0].location
每个路径评估步骤的结果都可以由第 9.16.2.2 节中列出的一个或多个jsonpath
运算符和方法处理。每个方法名称前面必须加一个点。例如,您可以获取数组的大小
$.track.segments.size()
在路径表达式中使用jsonpath
运算符和方法的更多示例如下所示:第 9.16.2.2 节。
在定义路径时,您还可以使用一个或多个筛选表达式,其工作方式类似于 SQL 中的WHERE
子句。筛选表达式以问号开头,并在括号中提供条件
? (condition)
筛选表达式必须紧跟它们要应用到的路径评估步骤之后编写。该步骤的结果将经过筛选,仅包括满足所提供条件的那些项。SQL/JSON 定义了三值逻辑,因此条件可以是true
、false
或unknown
。``unknown值扮演着与 SQL
NULL相同的角色,并且可以用
is unknown谓词对其进行测试。后续路径评估步骤仅使用筛选表达式返回
true的那些项。
可以在筛选表达式中使用的函数和运算符列在[表 9.51](functions-json.html#FUNCTIONS-SQLJSON-FILTER-EX-TABLE)中。在筛选表达式内,``@
变量表示正在筛选的值(即,前一个路径步骤的一个结果)。可以在@
后面编写访问器运算符以检索组件项。`
`例如,假设您想检索所有高于 130 的心率值。您可以使用以下表达式实现此目的
$.track.segments[*].HR ? (@ > 130)
要获取具有此类值的区段的开始时间,您必须在返回开始时间之前筛选掉无关的区段,因此筛选表达式应用于前一个步骤,并且条件中使用的路径不同
$.track.segments[*] ? (@.HR > 130)."start time"
如果需要,您可以按顺序使用多个筛选表达式。例如,以下表达式选择包含具有相关坐标和高心率值的位置的所有区段的开始时间
$.track.segments[*] ? (@.location[1] < 13.4) ? (@.HR > 130)."start time"
还允许在不同的嵌套级别使用筛选表达式。以下示例首先按位置筛选所有区段,然后为这些区段(如果可用)返回高心率值
$.track.segments[*] ? (@.location[1] < 13.4).HR ? (@ > 130)
您还可以在彼此内部嵌套筛选表达式
$.track ? (exists(@.segments[*] ? (@.HR > 130))).segments.size()
此表达式返回轨道的大小(如果它包含具有高心率值的任何区段),否则返回一个空序列。
PostgreSQL对 SQL/JSON 路径语言的实现与 SQL/JSON 标准有以下偏差
路径表达式可以是布尔谓词,尽管 SQL/JSON 标准仅允许在筛选器中使用谓词。这是实现
@@
运算符所必需的。例如,以下jsonpath
表达式在 PostgreSQL 中有效$.track.segments[*].HR < 70
在
like_regex
筛选器中使用的正则表达式模式的解释中存在细微差别,如 第 9.16.2.3 节 中所述。
9.16.2.1。严格模式和宽松模式#
当您查询 JSON 数据时,路径表达式可能与实际 JSON 数据结构不匹配。尝试访问不存在的对象成员或数组元素会导致结构错误。SQL/JSON 路径表达式有两种处理结构错误的模式
lax(默认)——路径引擎隐式地将查询的数据调整为指定路径。任何剩余的结构错误都会被抑制并转换为空的 SQL/JSON 序列。
strict——如果发生结构错误,则会引发错误。
lax 模式有助于匹配 JSON 文档结构和路径表达式(如果 JSON 数据不符合预期的架构)。如果操作数不符合特定操作的要求,则可以在执行此操作之前将其自动包装为 SQL/JSON 数组或通过将其元素转换为 SQL/JSON 序列来解包。此外,比较运算符在 lax 模式下会自动解包其操作数,因此您可以直接比较 SQL/JSON 数组。大小为 1 的数组被视为等于其唯一元素。仅在以下情况下才不执行自动解包
路径表达式包含
type()
或size()
方法,它们分别返回数组中的类型和元素数。查询的 JSON 数据包含嵌套数组。在这种情况下,仅解包最外层的数组,而所有内部数组保持不变。因此,隐式解包在每个路径评估步骤中只能向下进行一级。
例如,在查询上面列出的 GPS 数据时,在使用 lax 模式时,您可以抽象出它存储一个片段数组这一事实
lax $.track.segments.location
在 strict 模式下,指定的路径必须与查询的 JSON 文档的结构完全匹配才能返回 SQL/JSON 项,因此使用此路径表达式将导致错误。要获得与 lax 模式相同的结果,您必须显式解包segments
数组
strict $.track.segments[*].location
在使用 lax 模式时,.**
访问器可能导致令人惊讶的结果。例如,以下查询选择每个HR
值两次
lax $.**.HR
这是因为.**
访问器同时选择segments
数组及其每个元素,而.HR
访问器在使用 lax 模式时会自动解包数组。为避免出现令人惊讶的结果,我们建议仅在 strict 模式下使用.**
访问器。以下查询仅选择每个HR
值一次
strict $.**.HR
9.16.2.2. SQL/JSON 路径运算符和方法#
表 9.50显示了jsonpath
中可用的运算符和方法。请注意,虽然一元运算符和方法可以应用于前一个路径步骤产生的多个值,但二元运算符(加法等)只能应用于单个值。
表 9.50.jsonpath
运算符和方法
运算符/方法 说明 示例 |
---|
加法
|
一元加法(无操作);与加法不同,它可以迭代多个值
|
减法
|
取反;与减法不同,它可以迭代多个值
|
乘法
|
除法
|
模运算(余数)
|
JSON 项的类型(参见
|
JSON 项的大小(数组元素的数量,如果不是数组则为 1)
|
从 JSON 数字或字符串转换的近似浮点数
|
大于或等于给定数字的最近整数
|
小于或等于给定数字的最近整数
|
给定数字的绝对值
|
从字符串转换的日期/时间值
|
使用指定的
|
对象的键值对,表示为一个数组,其中包含三个字段的对象:
|
注意
datetime()
和datetime(*
template*)
方法的结果类型可以是date
、timetz
、time
、timestamptz
或timestamp
。这两种方法都动态地确定其结果类型。datetime()
方法按顺序尝试将输入字符串与date
、timetz
、time
、timestamptz
和timestamp
的 ISO 格式进行匹配。它在第一个匹配的格式上停止并发出相应的数据类型。datetime(*
template*)
方法根据提供的模板字符串中使用的字段来确定结果类型。datetime()
和datetime(*`template`*)
方法使用与to_timestamp
SQL 函数相同的解析规则(请参阅第 9.8 节),但有三个例外。首先,这些方法不允许不匹配的模板模式。其次,模板字符串中只允许使用以下分隔符:减号、句号、斜杠、逗号、撇号、分号、冒号和空格。第三,模板字符串中的分隔符必须与输入字符串完全匹配。如果需要比较不同的日期/时间类型,则会应用隐式转换。date
值可以转换为timestamp
或timestamptz
,timestamp
可以转换为timestamptz
,time
可以转换为timetz
。但是,除了第一个转换之外,所有转换都取决于当前时区设置,因此只能在支持时区的jsonpath
函数中执行。
表 9.51显示了可用的筛选器表达式元素。
表 9.51.jsonpath
筛选器表达式元素
谓词/值 说明 示例 |
---|
相等比较(此比较运算符以及其他比较运算符适用于所有 JSON 标量值)
|
不相等比较
|
小于比较
|
小于或等于比较
|
大于比较
|
大于或等于比较
|
JSON 常量
|
JSON 常量
|
JSON 常量
|
布尔 AND
|
布尔 OR
|
布尔 NOT
|
测试布尔条件是否为
|
测试第一个操作数是否与第二个操作数给出的正则表达式匹配,还可以选择使用
|
测试第二个操作数是否是第一个操作数的初始子字符串。
|
测试路径表达式是否与至少一个 SQL/JSON 项匹配。如果路径表达式会导致错误,则返回
|
9.16.2.3. SQL/JSON 正则表达式#
SQL/JSON 路径表达式允许使用like_regex
过滤器将文本与正则表达式进行匹配。例如,以下 SQL/JSON 路径查询将不区分大小写地匹配数组中以英语元音开头的所有字符串
$[*] ? (@ like_regex "^[aeiou]" flag "i")
可选的flag
字符串可能包含一个或多个字符i
用于不区分大小写的匹配,m
允许^
和$
在换行符处匹配,s
允许.
匹配换行符,q
对整个模式进行引用(将行为简化为简单的子字符串匹配)。
SQL/JSON 标准从LIKE_REGEX
运算符借用了其正则表达式的定义,而后者又使用了 XQuery 标准。PostgreSQL 目前不支持LIKE_REGEX
运算符。因此,like_regex
过滤器使用第 9.7.3 节中描述的 POSIX 正则表达式引擎实现。这导致了与标准 SQL/JSON 行为的各种细微差异,这些差异已编入目录,请参见第 9.7.3.8 节。但是,请注意,此处描述的标志字母不兼容性不适用于 SQL/JSON,因为它将 XQuery 标志字母转换为与 POSIX 引擎期望的内容相匹配。
请记住,like_regex
的模式参数是 JSON 路径字符串文字,根据第 8.14.7 节中给出的规则编写。这意味着,你希望在正则表达式中使用的任何反斜杠都必须加倍。例如,要匹配仅包含数字的根文档的字符串值
$.* ? (@ like_regex "^\\d+$")