本节描述:
处理和创建 JSON 数据的函数和操作符
SQL/JSON 路径语言
SQL/JSON 查询函数
为了在 SQL 环境中为 JSON 数据类型提供原生支持, PostgreSQL 实现了 SQL/JSON 数据模型。 这个模型由项的序列组成。 每个项可以持有 SQL 标量值 (外加一个额外的 SQL/JSON 空值), 以及使用 JSON 数组和对象的复合数据结构。 这个模型是 JSON 规范 RFC 7159 中隐含数据模型的形式化。
SQL/JSON 允许你把 JSON 数据与常规的 SQL 数据一起处理, 并带有事务支持, 包括:
把 JSON 数据上传到数据库, 并把它作为字符或二进制字符串存储在常规的 SQL 列中。
从关系数据生成 JSON 对象和数组。
使用 SQL/JSON 查询函数和 SQL/JSON 路径语言表达式查询 JSON 数据。
要了解更多关于 SQL/JSON 标准的信息, 请参见 [sqltr-19075-6]。 有关 PostgreSQL 支持的 JSON 类型的细节, 请参见 Section 8.14。
Table 9.47
展示了可用于 JSON
数据类型的操作符
(参见
Section 8.14)。
此外,
Table 9.1
中展示的常规比较操作符可用于
jsonb,
但不适用于
json。
这些比较操作符遵循
Section 8.14.4
中概括的 B-tree
操作排序规则。
另请参见
Section 9.21
中的聚集函数
json_agg,
它把记录值聚集为 JSON;
以及聚集函数
json_object_agg,
它把值对聚集为一个 JSON
对象;
还有它们的
jsonb
等价函数
jsonb_agg 和
jsonb_object_agg。
Table 9.47. json 和 jsonb 操作符
操作符 说明 示例 |
|---|
提取 JSON
数组的第
|
用给定的键提取 JSON 对象字段。
|
以
|
用给定的键以
|
在指定的路径上提取 JSON 子对象, 路径元素可以是字段键或数组索引。
|
以
|
如果 JSON 输入不具备与请求相匹配的正确结构, 例如不存在这样的键或数组元素, 那么字段/元素/路径提取操作符会返回 NULL, 而不是失败。
还有一些操作符只存在于
jsonb,
如
Table 9.48
所示。
Section 8.14.4
描述了如何使用这些操作符来有效地搜索已建立索引的
jsonb
数据。
Table 9.48. 其他 jsonb 操作符
操作符 说明 示例 |
|---|
第一个 JSON 值是否包含第二个? (关于包含关系的细节请参见 Section 8.14.3。)
|
第一个 JSON 值是否被包含在第二个之中?
|
该文本字符串是否作为顶层键或数组元素存在于 JSON 值之中?
|
文本数组中的任何字符串是否作为顶层键或数组元素存在?
|
文本数组中的所有字符串是否都作为顶层键或数组元素存在?
|
连接两个
要把一个数组作为单一条目追加到另一个数组, 请把它再包一层额外的数组, 例如:
|
从一个 JSON 对象中删除一个键 (及其值), 或者从一个 JSON 数组中删除匹配的字符串值。
|
从左侧操作数中删除所有匹配的键或数组元素。
|
删除具有指定索引的数组元素 (负整数从末尾开始计数)。 如果 JSON 值不是一个数组, 则抛出一个错误。
|
删除指定路径上的字段或数组元素, 路径元素可以是字段键或数组索引。
|
JSON 路径是否为指定的 JSON 值返回任何项? (这只对 SQL 标准 JSON 路径表达式有用, 对 谓词检查表达式 没有用处, 因为后者总是返回一个值。)
|
返回对指定的 JSON
值执行 JSON
路径谓词检查所产生的结果。
(这只对
谓词检查表达式
有用,
对 SQL
标准 JSON
路径表达式没有用处,
因为如果路径结果不是单一的布尔值,
它会返回
|
jsonpath
操作符
@? 和
@@
会抑制以下错误:
缺失对象字段或数组元素、
非预期的 JSON
项类型、
日期时间和数字错误。
下文描述的与
jsonpath
相关的函数也可以被要求抑制这些类型的错误。
当搜索结构各异的 JSON
文档集合时,
这种行为可能有所帮助。
Table 9.49
展示了可用于构造
json 和
jsonb
值的函数。
这个表中的一些函数有一个
RETURNING
子句,
它规定要返回的数据类型。
它必须是
json、
jsonb、
bytea、
一个字符串类型
(text、
char 或
varchar),
或一个可以被转换为
json
的类型。
默认返回
json
类型。
Table 9.49. JSON 创建函数
函数 说明 示例 |
|---|
|
把任何 SQL
值转换为
|
把一个 SQL
数组转换为一个 JSON
数组。
行为与
|
用一系列
|
把一个 SQL
组合值转换为一个 JSON
对象。
行为与
|
用一个可变参数列表构造一个可能具有异构类型的 JSON
数组。
每个参数都按照
|
用一个可变参数列表构造一个 JSON
对象。
按照惯例,
参数列表由交替的键和值组成。
键参数被强制转换为文本;
值参数按照
|
用给出的所有键/值对构造一个 JSON
对象,
如果没有给出任何键/值对,
则构造一个空对象。
|
|
用一个文本数组构造一个 JSON 对象。 该数组必须恰好有一个维度且成员数量为偶数, 在那种情况下它们被当作交替的键/值对; 或者有两个维度, 使得每个内部数组恰好有两个元素, 它们被当作一个键/值对。 所有的值都会被转换为 JSON 字符串。
|
这种形式的
|
|
把一个指定为
|
|
把一个给定的 SQL 标量值转换为一个 JSON 标量值。 如果输入为 NULL, 则返回一个 SQL 空。 如果输入是数字或布尔值, 则返回相应的 JSON 数字或布尔值。 对于任何其他值, 则返回一个 JSON 字符串。
|
|
把一个 SQL/JSON
表达式转换为字符或二进制字符串。
|
Table 9.50 详述了用于测试 JSON 的 SQL/JSON 设施。
Table 9.50. SQL/JSON 测试函数
Table 9.51
展示了可用于处理
json 和
jsonb
值的函数。
Table 9.51. 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_object_keys ------------------ f1 f2
|
把顶层的 JSON
对象展开为一行,
该行具有
要把一个 JSON 值转换为一个输出列的 SQL 类型, 会依次应用以下规则:
虽然下面的例子使用的是一个常量 JSON
值,
但典型的用法是在查询的
a | b | c
---+-----------+-------------
1 | {2,"a b"} | (4,"a b c")
|
用于测试
jsonb_populate_record_valid ----------------------------- f (1 row)
ERROR: value too long for type character(2)
jsonb_populate_record_valid ----------------------------- t (1 row)
a ---- aa (1 row)
|
把顶层的由对象组成的 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
值中删除所有具有空值的对象字段。
如果
|
检查 JSON
路径是否为指定的 JSON
值返回任何项。
(这只对 SQL
标准 JSON
路径表达式有用,
对
谓词检查表达式
没有用处,
因为后者总是返回一个值。)
如果指定了
|
返回对指定的 JSON
值执行 JSON
路径谓词检查所产生的 SQL
布尔结果。
(这只对
谓词检查表达式
有用,
对 SQL
标准 JSON
路径表达式没有用处,
因为如果路径结果不是单一的布尔值,
它会失败或返回
|
返回 JSON
路径为指定的 JSON
值返回的所有 JSON
项。
对于 SQL
标准 JSON
路径表达式,
它返回从
jsonb_path_query ------------------ 2 3 4
|
以一个 JSON
数组的形式返回 JSON
路径为指定的 JSON
值返回的所有 JSON
项。
参数与
|
返回 JSON
路径为指定的 JSON
值返回的第一个 JSON
项,
如果没有结果则返回
|
这些函数的行为类似于上文描述的、不带
|
|
把给定的 JSON 值转换为美化打印的、 带缩进的文本。
[
{
"f1": 1,
"f2": null
},
2
]
|
|
以文本字符串的形式返回顶层 JSON
值的类型。
可能的类型是
|
SQL/JSON
路径表达式规定要从 JSON
值中检索的项,
类似于用于访问 XML
内容的 XPath
表达式。
在
PostgreSQL
中,
路径表达式被实现为
jsonpath
数据类型,
并且可以使用
Section 8.14.7
中描述的任何元素。
JSON 查询函数和操作符把提供的路径表达式传递给
路径引擎进行求值。
如果该表达式与被查询的 JSON
数据相匹配,
则返回相应的 JSON
项或项的集合。
如果没有匹配,
结果将是
NULL、
false
或一个错误,
取决于函数。
路径表达式以 SQL/JSON
路径语言书写,
并且可以包括算术表达式和函数。
一个路径表达式由
jsonpath
数据类型所允许的元素序列组成。
路径表达式通常从左到右求值,
但你可以使用圆括号来改变运算的顺序。
如果求值成功,
会产生一个 JSON
项的序列,
并且求值结果会被返回给完成指定计算的 JSON
查询函数。
要引用被查询的 JSON
值
(即
上下文项),
请在路径表达式中使用
$
变量。
路径的第一个元素必须始终是
$。
它后面可以跟一个或多个
访问器操作符,
这些操作符会逐层深入 JSON
结构,
检索上下文项的子项。
每个访问器操作符作用于前一次求值步骤的结果,
并从每个输入项产生零个、一个或多个输出项。
例如, 假设你有一些来自 GPS 追踪器的 JSON 数据想要解析, 例如:
SELECT '{
"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
}
]
}
}' AS json \gset
(上面的例子可以被复制并粘贴到
psql
中,
为后面的例子做好准备。
然后
psql
会把
:'json'
展开为一个包含该 JSON
值的、带有适当引号的字符串常量。)
要检索可用的追踪段,
你需要使用
.
访问器操作符穿过外围的 JSON
对象逐层下降,
例如:
key
=>select jsonb_path_query(:'json', '$.track.segments');jsonb_path_query ------------------------------------------------------------------------------------------------------------------------------------------------------------------- [{"HR": 73, "location": [47.763, 13.4034], "start time": "2018-10-14 10:05:14"}, {"HR": 135, "location": [47.706, 13.2635], "start time": "2018-10-14 10:39:21"}]
要检索一个数组的内容,
你通常使用
[*]
操作符。
下面的例子会返回所有可用追踪段的位置坐标:
=>select jsonb_path_query(:'json', '$.track.segments[*].location');jsonb_path_query ------------------- [47.763, 13.4034] [47.706, 13.2635]
这里我们从完整的 JSON
输入值
($)
开始,
然后
.track
访问器选中与
"track"
对象键关联的 JSON
对象,
接着
.segments
访问器选中那个对象内与
"segments"
键关联的 JSON
数组,
然后
[*]
访问器选中该数组的每个元素
(产生一系列项),
接着
.location
访问器选中每个这样的对象内与
"location"
键关联的 JSON
数组。
在这个例子中,
每个对象都有
"location"
键;
但如果其中任何一个没有,
.location
访问器对该输入项就只会简单地不产生任何输出。
要只返回第一段的坐标,
你可以在
[]
访问器操作符中指定相应的下标。
回顾一下,
JSON
数组索引是从 0
开始的:
=>select jsonb_path_query(:'json', '$.track.segments[0].location');jsonb_path_query ------------------- [47.763, 13.4034]
每次路径求值步骤的结果都可以由
Section 9.16.2.3
中列出的一个或多个
jsonpath
操作符和方法处理。
每个方法名之前必须有一个点。
例如,
你可以获得一个数组的尺寸:
=>select jsonb_path_query(:'json', '$.track.segments.size()');jsonb_path_query ------------------ 2
在路径表达式中使用
jsonpath
操作符和方法的更多例子,
将在下文的
Section 9.16.2.3
中给出。
路径中还可以包含
过滤表达式,
它们的运作方式类似于 SQL
中的
WHERE
子句。
过滤表达式以一个问号开始,
并在圆括号中提供一个条件:
? (condition)
过滤表达式必须写在它们应当应用的那个路径求值步骤之后。
那一步的结果会被过滤,
只包含满足所提供条件的项。
SQL/JSON
定义了三值逻辑,
因此条件可以产生
true、
false
或
unknown。
unknown
值扮演的角色与 SQL 的
NULL
相同,
并且可以用
is unknown
谓词来测试它。
进一步的路径求值步骤只会使用过滤表达式为其返回了
true
的那些项。
可以在过滤表达式中使用的函数和操作符列在
Table 9.53
中。
在过滤表达式内,
@
变量表示正在被考虑的值
(即前一个路径步骤的一个结果)。
你可以在
@
之后书写访问器操作符来检索分量项。
例如, 假设你想检索所有高于 130 的心率值。 你可以这样实现:
=>select jsonb_path_query(:'json', '$.track.segments[*].HR ? (@ > 130)');jsonb_path_query ------------------ 135
要得到具有这样的值的段的开始时间, 你必须在选择开始时间之前先过滤掉不相关的段, 因此过滤表达式被应用在前一步上, 并且条件中使用的路径有所不同:
=>select jsonb_path_query(:'json', '$.track.segments[*] ? (@.HR > 130)."start time"');jsonb_path_query ----------------------- "2018-10-14 10:39:21"
如果需要, 你可以按顺序使用多个过滤表达式。 下面的例子选择所有包含坐标相关且心率值高的位置的段的开始时间:
=>select jsonb_path_query(:'json', '$.track.segments[*] ? (@.location[1] < 13.4) ? (@.HR > 130)."start time"');jsonb_path_query ----------------------- "2018-10-14 10:39:21"
在不同的嵌套层级上使用过滤表达式也是允许的。 下面的例子先按位置过滤所有段, 然后为这些段返回高心率值 (如果有的话):
=>select jsonb_path_query(:'json', '$.track.segments[*] ? (@.location[1] < 13.4).HR ? (@ > 130)');jsonb_path_query ------------------ 135
你还可以把过滤表达式彼此嵌套。 这个例子在轨迹包含任何高心率值的段时返回该轨迹的尺寸, 否则返回一个空序列:
=>select jsonb_path_query(:'json', '$.track ? (exists(@.segments[*] ? (@.HR > 130))).segments.size()');jsonb_path_query ------------------ 2
PostgreSQL 对 SQL/JSON 路径语言的实现与 SQL/JSON 标准有以下偏离。
作为对 SQL
标准的扩展,
PostgreSQL
的路径表达式可以是一个布尔谓词,
而 SQL
标准只允许在过滤器内使用谓词。
SQL 标准的路径表达式返回被查询 JSON
值的相关元素,
而谓词检查表达式返回该谓词的单一三值
jsonb
结果:
true、
false
或
null。
例如,
我们可以写这条符合 SQL
标准的过滤表达式:
=>select jsonb_path_query(:'json', '$.track.segments ?(@[*].HR > 130)');jsonb_path_query --------------------------------------------------------------------------------- {"HR": 135, "location": [47.706, 13.2635], "start time": "2018-10-14 10:39:21"}
类似的谓词检查表达式则简单地返回
true,
表示存在一个匹配:
=>select jsonb_path_query(:'json', '$.track.segments[*].HR > 130');jsonb_path_query ------------------ true
在
@@
操作符
(以及
jsonb_path_match
函数)
中要求使用谓词检查表达式,
而不应当把它们与
@?
操作符
(或
jsonb_path_exists
函数)
一起使用。
在
like_regex
过滤器中使用的正则表达式模式的解释存在微小的差异,
如
Section 9.16.2.4
中所描述。
当你查询 JSON 数据时, 路径表达式可能与实际的 JSON 数据结构不匹配。 访问对象的一个不存在的成员或数组的一个元素的尝试被定义为 结构错误。 SQL/JSON 路径表达式有两种处理结构错误的模式:
宽松模式 (默认) — 路径引擎隐式地把被查询的数据适配到指定的路径。 任何无法按下文所述方式修复的结构错误都会被抑制, 产生空匹配。
严格模式 — 如果发生结构错误, 会抛出一个错误。
宽松模式便于在 JSON 数据不符合预期模式时让 JSON 文档与路径表达式相匹配。 如果一个操作数不满足某个特定操作的要求, 它可以被自动包装为 SQL/JSON 数组, 或者通过把它的元素转换为 SQL/JSON 序列来解包, 然后再执行该操作。 此外, 比较操作符在宽松模式下会自动解包它们的操作数, 因此你可以开箱即用地比较 SQL/JSON 数组。 一个尺寸为 1 的数组被视为与其唯一元素相等。 在以下情况下不执行自动解包:
路径表达式包含
type() 或
size()
方法,
它们分别返回数组的类型和元素的数量。
被查询的 JSON 数据包含嵌套数组。 在这种情况下, 只有最外层的数组被解包, 而所有内部的数组保持不变。 因此, 隐式解包在每个路径求值步骤内只能向下进行一层。
例如, 在查询上面列出的 GPS 数据时, 使用宽松模式时你可以不必在意它存储的是一个段的数组:
=>select jsonb_path_query(:'json', 'lax $.track.segments.location');jsonb_path_query ------------------- [47.763, 13.4034] [47.706, 13.2635]
在严格模式下, 指定的路径必须与被查询 JSON 文档的结构精确匹配, 因此使用这个路径表达式会导致错误:
=>select jsonb_path_query(:'json', 'strict $.track.segments.location');ERROR: jsonpath member accessor can only be applied to an object
要得到与宽松模式下相同的结果,
你必须显式地解包
segments
数组:
=>select jsonb_path_query(:'json', 'strict $.track.segments[*].location');jsonb_path_query ------------------- [47.763, 13.4034] [47.706, 13.2635]
宽松模式的解包行为可能导致令人意外的结果。
例如,
下面这个使用
.**
访问器的查询会把每个
HR
值选中两次:
=>select jsonb_path_query(:'json', 'lax $.**.HR');jsonb_path_query ------------------ 73 135 73 135
之所以如此,
是因为
.**
访问器既选中
segments
数组也选中它的每个元素,
而在使用宽松模式时
.HR
访问器会自动解包数组。
为了避免令人意外的结果,
我们建议只在严格模式下使用
.**
访问器。
下面的查询会把每个
HR
值只选中一次:
=>select jsonb_path_query(:'json', 'strict $.**.HR');jsonb_path_query ------------------ 73 135
数组的解包也可能导致意外的结果。
考虑这个选择全部
location
数组的例子:
=>select jsonb_path_query(:'json', 'lax $.track.segments[*].location');jsonb_path_query ------------------- [47.763, 13.4034] [47.706, 13.2635] (2 rows)
正如预期的那样, 它返回完整的数组。 但应用一个过滤表达式会导致数组被解包以对每一项求值, 只返回与表达式相匹配的项:
=>select jsonb_path_query(:'json', 'lax $.track.segments[*].location ?(@[*] > 15)');jsonb_path_query ------------------ 47.763 47.706 (2 rows)
尽管路径表达式选择的是完整的数组, 情况仍然如此。 使用严格模式可以恢复选择数组:
=>select jsonb_path_query(:'json', 'strict $.track.segments[*].location ?(@[*] > 15)');jsonb_path_query ------------------- [47.763, 13.4034] [47.706, 13.2635] (2 rows)
Table 9.52
展示了
jsonpath
中可用的操作符和方法。
注意,虽然一元操作符和方法可以应用于前一个路径步骤产生的多个值,
但二元操作符
(加法等)
只能应用于单值。
在宽松模式下,
应用到一个数组上的方法会对该数组中的每个值执行。
例外的是
.type() 和
.size(),
它们作用于数组本身。
Table 9.52. jsonpath 操作符和方法
操作符/方法 说明 示例 |
|---|
加法
|
一元加号 (不做任何操作); 与加法不同, 它可以迭代多个值
|
减法
|
取负; 与减法不同, 它可以迭代多个值
|
乘法
|
除法
|
取模(余数)
|
JSON 项的类型
(参见
|
JSON 项的尺寸 (数组元素的数量, 如果不是数组则为 1)
|
从一个 JSON 布尔值、数字或字符串转换得到的布尔值
|
从一个 JSON 布尔值、数字、 字符串或日期时间转换得到的字符串值
|
从一个 JSON 数字或字符串转换得到的近似浮点数
|
大于等于给定数字的最接近整数
|
小于等于给定数字的最接近整数
|
给定数字的绝对值
|
从一个 JSON 数字或字符串转换得到的大整数值
|
从一个 JSON
数字或字符串转换得到的舍入十进制值
(
|
从一个 JSON 数字或字符串转换得到的整数值
|
从一个 JSON 数字或字符串转换得到的数值
|
从一个字符串转换得到的日期/时间值
|
使用指定的
|
从一个字符串转换得到的日期值
|
从一个字符串转换得到的不带时区的时间值
|
从一个字符串转换得到的不带时区的时间值, 小数秒被调整到给定的精度
|
从一个字符串转换得到的带时区的时间值
|
从一个字符串转换得到的带时区的时间值, 小数秒被调整到给定的精度
|
从一个字符串转换得到的不带时区的时间戳值
|
从一个字符串转换得到的不带时区的时间戳值, 小数秒被调整到给定的精度
|
从一个字符串转换得到的带时区的时间戳值
|
从一个字符串转换得到的带时区的时间戳值, 小数秒被调整到给定的精度
|
该对象的键值对,
表示为一个由对象组成的数组,
每个对象包含三个字段:
|
datetime() 和
datetime(
方法的结果类型可以是
template)date、
timetz、
time、
timestamptz 或
timestamp。
两种方法都会动态地确定它们的结果类型。
datetime()
方法会依次尝试把它的输入字符串与
date、
timetz、
time、
timestamptz 和
timestamp
的 ISO
格式相匹配。
它在第一个匹配的格式上停止,
并发出相应的数据类型。
datetime(
方法根据所提供模板字符串中使用的字段来确定结果类型。
template)
datetime() 和
datetime(
方法使用的解析规则与
template)to_timestamp
SQL 函数相同
(参见
Section 9.8),
有三个例外。
首先,
这些方法不允许不匹配的模板模式。
其次,
模板字符串中只允许以下分隔符:
减号、句点、斜杠
(solidus)、逗号、单引号、
分号、冒号和空格。
第三,
模板字符串中的分隔符必须与输入字符串精确匹配。
如果需要比较不同的日期/时间类型,
会应用一次隐式转换。
一个
date
值可以被转换为
timestamp 或
timestamptz,
timestamp
可以被转换为
timestamptz,
time
可以被转换为
timetz。
然而,
这些转换中除第一个之外,
其他都依赖于当前的
TimeZone
设置,
因此只能在时区感知的
jsonpath
函数内执行。
类似地,
其他把字符串转换为日期/时间类型的与日期/时间相关的方法也会做这样的转换,
这可能涉及当前的
TimeZone
设置。
因此,
这些转换同样只能在时区感知的
jsonpath
函数内执行。
Table 9.53 展示了可用的过滤表达式元素。
Table 9.53. jsonpath 过滤表达式元素
谓词/值 说明 示例 |
|---|
相等比较 (它以及其他比较操作符对所有 JSON 标量值都有效)
|
不等比较
|
小于比较
|
小于等于比较
|
大于比较
|
大于等于比较
|
JSON 常量
|
JSON 常量
|
JSON 常量
|
布尔与
|
布尔或
|
布尔非
|
测试一个布尔条件是否为
|
测试第一个操作数是否匹配由第二个操作数给定的正则表达式,
可以带由一个
|
测试第二个操作数是否为第一个操作数的初始子串。
|
测试一个路径表达式是否匹配至少一个 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
过滤器是使用
Section 9.7.3
中描述的 POSIX
正则表达式引擎实现的。
这导致与标准 SQL/JSON
行为的各种细微差异,
这些差异被编目在
Section 9.7.3.8
中。
不过请注意,
那里描述的标志字母不兼容之处不适用于 SQL/JSON,
因为它会把 XQuery
的标志字母翻译为 POSIX
引擎期望的形式。
请记住,
like_regex
的模式参数是一个 JSON
路径字符串字面量,
它按照
Section 8.14.7
中给出的规则书写。
这特别意味着,
你想在正则表达式中使用的任何反斜杠都必须写成双份。
例如,
要匹配根文档中只包含数字的字符串值:
$.* ? (@ like_regex "^\\d+$")
Table 9.54
中描述的 SQL/JSON 函数
JSON_EXISTS()、
JSON_QUERY() 和
JSON_VALUE()
可用于查询 JSON
文档。
这些函数中的每一个都会把一个
path_expression
(一条 SQL/JSON
路径查询)
应用到一个
context_item
(即文档)上。
关于
path_expression
可以包含什么内容的更多细节,
请参见
Section 9.16.2。
path_expression
还可以引用变量,
变量的值由每个函数所支持的
PASSING
子句中以它们各自的名字指定。
context_item
可以是一个
jsonb
值,
或者一个可以被成功转换为
jsonb
的字符串。
Table 9.54. SQL/JSON 查询函数
函数签名 说明 示例 |
|---|
Examples:
ERROR: jsonpath array subscript is out of bounds
|
Examples:
ERROR: malformed array literal: "[1, 2]" DETAIL: Missing "]" after array dimensions.
|
Examples:
|
如果
context_item
表达式还不是
jsonb
类型,
它会通过一次隐式转换被转换为
jsonb。
不过请注意,
该转换期间发生的任何解析错误都会被无条件地抛出,
也就是说,
它们不会按照
(指定的或隐式的)
ON ERROR
子句处理。
如果
path_expression
返回一个 JSON
null,
JSON_VALUE()
会返回一个 SQL NULL,
而
JSON_QUERY()
会原样返回那个 JSON
null。
JSON_TABLE
是一个 SQL/JSON 函数,
它查询
JSON
数据,
并把结果呈现为一个关系视图,
该视图可以作为一张普通的 SQL
表来访问。
你可以在
SELECT、
UPDATE 或
DELETE
的
FROM
子句中,
以及在
MERGE
语句中作为数据源使用
JSON_TABLE。
JSON_TABLE
以 JSON
数据为输入,
它使用一个 JSON
路径表达式来提取所提供数据的一部分,
作为所构造视图的
行模式。
行模式给出的每个 SQL/JSON
值都作为所构造视图中一个单独行的来源。
为了把行模式拆分成列,
JSON_TABLE
提供了
COLUMNS
子句,
它定义所创建视图的模式。
对于每一列,
可以指定一个单独的 JSON
路径表达式,
针对行模式求值,
以得到一个 SQL/JSON
值,
它将成为给定输出行中指定列的值。
存储在行模式嵌套层级上的 JSON
数据可以用
NESTED PATH
子句来提取。
每个
NESTED PATH
子句都可以利用行模式嵌套层级上的数据生成一列或多列。
这些列可以用一个
COLUMNS
子句指定,
它看起来与顶层的 COLUMNS
子句类似。
由 NESTED COLUMNS
构造的行被称为
子行,
它们与由父
COLUMNS
子句中指定的列构造的行相连接,
以得到最终视图中的行。
子列自身也可以包含一个
NESTED PATH
说明,
从而允许提取位于任意嵌套层级上的数据。
由同一层级上的多个
NESTED PATH
产生的列被认为是彼此的
兄弟,
它们与父行连接之后的行会用 UNION
合并。
JSON_TABLE
产生的行会横向连接到产生它们的行,
因此你不必显式地把所构造的视图与持有
JSON
数据的原始表连接。
The syntax is:
JSON_TABLE (
context_item, path_expression [ AS json_path_name ] [ PASSING { value AS varname } [, ...] ]
COLUMNS ( json_table_column [, ...] )
[ { ERROR | EMPTY [ARRAY]} ON ERROR ]
)
where json_table_column is:
name FOR ORDINALITY
| name type
[ FORMAT JSON [ENCODING UTF8]]
[ PATH path_expression ]
[ { WITHOUT | WITH { CONDITIONAL | [UNCONDITIONAL] } } [ ARRAY ] WRAPPER ]
[ { KEEP | OMIT } QUOTES [ ON SCALAR STRING ] ]
[ { ERROR | NULL | EMPTY { [ARRAY] | OBJECT } | DEFAULT expression } ON EMPTY ]
[ { ERROR | NULL | EMPTY { [ARRAY] | OBJECT } | DEFAULT expression } ON ERROR ]
| name type EXISTS [ PATH path_expression ]
[ { ERROR | TRUE | FALSE | UNKNOWN } ON ERROR ]
| NESTED [ PATH ] path_expression [ AS json_path_name ] COLUMNS ( json_table_column [, ...] )
下面会更详细地描述每个语法元素。
context_item, path_expression [ AS json_path_name ] [ PASSING { value AS varname } [, ...]]
context_item
指定要查询的输入文档,
path_expression
是定义查询的一条 SQL/JSON
路径表达式,
而
json_path_name
是该
path_expression
的一个可选名字。
可选的
PASSING
子句为
path_expression
中提到的变量提供数据值。
用上述元素对输入数据求值的结果被称为
行模式,
它被用作所构造视图中行值的来源。
COLUMNS ( json_table_column [, ...] )
定义所构造视图模式的
COLUMNS
子句。
在这个子句中,
你可以指定每一列都填充一个通过把 JSON
路径表达式应用于行模式而得到的 SQL/JSON
值。
json_table_column
有以下这些变体:
name FOR ORDINALITY
添加一个序号列,
它提供从 1
开始的顺序行编号。
每个
NESTED PATH
(见下文)
都会为任何嵌套的序号列准备它自己的计数器。
name type
[FORMAT JSON [ENCODING UTF8]]
[ PATH path_expression ]
把通过针对行模式应用
path_expression
得到的 SQL/JSON
值,
在把它转换为指定的
type
之后插入视图的输出行。
指定
FORMAT JSON
显式表明你期望该值是一个有效的
json
对象。
只有当
type
是
bpchar、
bytea、
character varying、
name、
json、
jsonb、
text,
或这些类型之上的一个域时,
指定
FORMAT JSON
才有意义。
可选地,
你可以指定
WRAPPER 和
QUOTES
子句来格式化输出。
注意,
如果同时指定了
OMIT QUOTES,
它会覆盖
FORMAT JSON,
因为不带引号的字面量不构成有效的
json
值。
可选地,
你可以使用
ON EMPTY 和
ON ERROR
子句,
分别规定当 JSON
路径求值的结果为空时、
以及在 JSON
路径求值期间或把 SQL/JSON
值转换为指定类型时发生错误时,
是抛出错误还是返回指定的值。
两者的默认行为都是返回一个
NULL
值。
这个子句在内部被转换并且具有与
JSON_VALUE 或
JSON_QUERY
相同的语义。
当指定的类型不是一个标量类型,
或者存在
FORMAT JSON、
WRAPPER 或
QUOTES
子句之一时,
用的是后者。
name type
EXISTS [ PATH path_expression ]
把通过针对行模式应用
path_expression
得到的布尔值,
在把它转换为指定的
type
之后插入视图的输出行。
该值对应于把
PATH
表达式应用到行模式上是否会产生任何值。
指定的
type
应当有一个从
boolean
类型出发的转换。
可选地,
你可以使用
ON ERROR
来规定当 JSON
路径求值期间或把 SQL/JSON
值转换为指定类型时发生错误时,
是抛出错误还是返回指定的值。
默认行为是返回布尔值
FALSE。
这个子句在内部被转换并且具有与
JSON_EXISTS
相同的语义。
NESTED [ PATH ] path_expression [ AS json_path_name ]
COLUMNS ( json_table_column [, ...] )
从行模式的嵌套层级中提取 SQL/JSON
值,
按照
COLUMNS
子子句的定义生成一列或多列,
并把提取到的 SQL/JSON
值插入那些列。
COLUMNS
子子句中的
json_table_column
表达式使用与父
COLUMNS
子句中相同的语法。
NESTED PATH
语法是递归的,
因此你可以通过在彼此之内指定多个
NESTED PATH
子子句来深入多个嵌套层级。
它允许在单次函数调用中解开 JSON
对象和数组的层级结构,
而不是在一条 SQL
语句中串起多个
JSON_TABLE
表达式。
在上文描述的
json_table_column
的每个变体中,
如果省略了
PATH
子句,
则使用路径表达式
$.,
其中
namename
是所提供的列名。
AS json_path_name
可选的
json_path_name
用作所提供的
path_expression
的标识符。
该名字必须是唯一的,
并且与列名不同。
ERROR | EMPTY } ON ERROR
可选的
ON ERROR
可用于规定在对顶层
path_expression
求值时如何处理错误。
如果你希望抛出错误,
请使用
ERROR;
如果希望返回一个空表
(即包含 0
行的表),
请使用
EMPTY。
注意,
这个子句不影响对列求值时发生的错误,
那种错误的行为取决于是否针对给定的列指定了
ON ERROR
子句。
示例
在接下来的例子中, 将会使用下面这张包含 JSON 数据的表:
CREATE TABLE my_films ( js jsonb );
INSERT INTO my_films VALUES (
'{ "favorites" : [
{ "kind" : "comedy", "films" : [
{ "title" : "Bananas",
"director" : "Woody Allen"},
{ "title" : "The Dinner Game",
"director" : "Francis Veber" } ] },
{ "kind" : "horror", "films" : [
{ "title" : "Psycho",
"director" : "Alfred Hitchcock" } ] },
{ "kind" : "thriller", "films" : [
{ "title" : "Vertigo",
"director" : "Alfred Hitchcock" } ] },
{ "kind" : "drama", "films" : [
{ "title" : "Yojimbo",
"director" : "Akira Kurosawa" } ] }
] }');
下面的查询展示了如何使用
JSON_TABLE
把
my_films
表中的 JSON
对象转换为一个视图,
该视图包含原始 JSON
中键
kind、
title 和
director
所对应的列,
外加一个序号列:
SELECT jt.* FROM my_films, JSON_TABLE (js, '$.favorites[*]' COLUMNS ( id FOR ORDINALITY, kind text PATH '$.kind', title text PATH '$.films[*].title' WITH WRAPPER, director text PATH '$.films[*].director' WITH WRAPPER)) AS jt;
id | kind | title | director ----+----------+--------------------------------+---------------------------------- 1 | comedy | ["Bananas", "The Dinner Game"] | ["Woody Allen", "Francis Veber"] 2 | horror | ["Psycho"] | ["Alfred Hitchcock"] 3 | thriller | ["Vertigo"] | ["Alfred Hitchcock"] 4 | drama | ["Yojimbo"] | ["Akira Kurosawa"] (4 rows)
下面是上面查询的一个修改版本,
用于展示在顶层 JSON
路径表达式中指定的过滤器里使用
PASSING
参数,
以及各个列的各种选项:
SELECT jt.* FROM
my_films,
JSON_TABLE (js, '$.favorites[*] ? (@.films[*].director == $filter)'
PASSING 'Alfred Hitchcock' AS filter
COLUMNS (
id FOR ORDINALITY,
kind text PATH '$.kind',
title text FORMAT JSON PATH '$.films[*].title' OMIT QUOTES,
director text PATH '$.films[*].director' KEEP QUOTES)) AS jt;
id | kind | title | director ----+----------+---------+-------------------- 1 | horror | Psycho | "Alfred Hitchcock" 2 | thriller | Vertigo | "Alfred Hitchcock" (2 rows)
下面是上面查询的一个修改版本,
用于展示使用
NESTED PATH
来填充 title 和 director
列,
并说明它们是如何与父列 id 和 kind
相连接的:
SELECT jt.* FROM
my_films,
JSON_TABLE ( js, '$.favorites[*] ? (@.films[*].director == $filter)'
PASSING 'Alfred Hitchcock' AS filter
COLUMNS (
id FOR ORDINALITY,
kind text PATH '$.kind',
NESTED PATH '$.films[*]' COLUMNS (
title text FORMAT JSON PATH '$.title' OMIT QUOTES,
director text PATH '$.director' KEEP QUOTES))) AS jt;
id | kind | title | director ----+----------+---------+-------------------- 1 | horror | Psycho | "Alfred Hitchcock" 2 | thriller | Vertigo | "Alfred Hitchcock" (2 rows)
下面是同一条查询, 但根路径中没有过滤器:
SELECT jt.* FROM
my_films,
JSON_TABLE ( js, '$.favorites[*]'
COLUMNS (
id FOR ORDINALITY,
kind text PATH '$.kind',
NESTED PATH '$.films[*]' COLUMNS (
title text FORMAT JSON PATH '$.title' OMIT QUOTES,
director text PATH '$.director' KEEP QUOTES))) AS jt;
id | kind | title | director ----+----------+-----------------+-------------------- 1 | comedy | Bananas | "Woody Allen" 1 | comedy | The Dinner Game | "Francis Veber" 2 | horror | Psycho | "Alfred Hitchcock" 3 | thriller | Vertigo | "Alfred Hitchcock" 4 | drama | Yojimbo | "Akira Kurosawa" (5 rows)
下面展示了另一个查询,
它使用一个不同的
JSON
对象作为输入。
它展示了
NESTED
路径
$.movies[*] 和
$.books[*]
之间的 UNION
“兄弟连接”,
以及
FOR ORDINALITY
列在
NESTED
层级上的使用
(列
movie_id、
book_id
和
author_id):
SELECT * FROM JSON_TABLE (
'{"favorites":
[{"movies":
[{"name": "One", "director": "John Doe"},
{"name": "Two", "director": "Don Joe"}],
"books":
[{"name": "Mystery", "authors": [{"name": "Brown Dan"}]},
{"name": "Wonder", "authors": [{"name": "Jun Murakami"}, {"name":"Craig Doe"}]}]
}]}'::json, '$.favorites[*]'
COLUMNS (
user_id FOR ORDINALITY,
NESTED '$.movies[*]'
COLUMNS (
movie_id FOR ORDINALITY,
mname text PATH '$.name',
director text),
NESTED '$.books[*]'
COLUMNS (
book_id FOR ORDINALITY,
bname text PATH '$.name',
NESTED '$.authors[*]'
COLUMNS (
author_id FOR ORDINALITY,
author_name text PATH '$.name'))));
user_id | movie_id | mname | director | book_id | bname | author_id | author_name
---------+----------+-------+----------+---------+---------+-----------+--------------
1 | 1 | One | John Doe | | | |
1 | 2 | Two | Don Joe | | | |
1 | | | | 1 | Mystery | 1 | Brown Dan
1 | | | | 2 | Wonder | 1 | Jun Murakami
1 | | | | 2 | Wonder | 2 | Craig Doe
(5 rows)