2025年9月25日: PostgreSQL 18 发布!
支持的版本:当前 (18) / 17 / 16 / 15 / 14 / 13
开发版本:devel
不支持的版本:12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2

9.16。JSON 函数和运算符 #

本节描述

  • 处理和创建 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 类型的详细信息,请参阅 第 8.14 节

9.16.1。处理和创建 JSON 数据 #

表 9.47 显示了可用于 JSON 数据类型的运算符(请参阅 第 8.14 节)。此外,表 9.1 中所示的常规比较运算符可用于 jsonb,但不适用于 json。比较运算符遵循 第 8.14.4 节 中概述的 B-树操作的排序规则。另请参阅 第 9.21 节,了解将记录值聚合为 JSON 的聚合函数 json_agg,将值对聚合为 JSON 对象的聚合函数 json_object_agg,以及它们对应的 jsonb 等价函数 jsonb_aggjsonb_object_agg

表 9.47。jsonjsonb 运算符

运算符

描述

示例

json -> integerjson

jsonb -> integerjsonb

提取 JSON 数组的第 n 个元素(数组元素从零开始索引,但负整数从末尾开始计数)。

'[{"a":"foo"},{"b":"bar"},{"c":"baz"}]'::json -> 2{"c":"baz"}

'[{"a":"foo"},{"b":"bar"},{"c":"baz"}]'::json -> -3{"a":"foo"}

json -> textjson

jsonb -> textjsonb

提取具有给定键的 JSON 对象字段。

'{"a": {"b":"foo"}}'::json -> 'a'{"b":"foo"}

json ->> integertext

jsonb ->> integertext

提取 JSON 数组的第 n 个元素,作为 text

'[1,2,3]'::json ->> 23

json ->> texttext

jsonb ->> texttext

提取具有给定键的 JSON 对象字段,作为 text

'{"a":1,"b":2}'::json ->> 'b'2

json #> text[]json

jsonb #> text[]jsonb

在指定路径提取 JSON 子对象,其中路径元素可以是字段键或数组索引。

'{"a": {"b": ["foo","bar"]}}'::json #> '{a,b,1}'"bar"

json #>> text[]text

jsonb #>> text[]text

将指定路径的 JSON 子对象提取为 text

'{"a": {"b": ["foo","bar"]}}'::json #>> '{a,b,1}'bar


注意

如果 JSON 输入不具有与请求匹配的正确结构,例如不存在此类键或数组元素,则字段/元素/路径提取运算符返回 NULL,而不是失败。

表 9.48 所示,还有一些运算符仅适用于 jsonb第 8.14.4 节 描述了如何使用这些运算符有效地搜索索引的 jsonb 数据。

表 9.48。附加 jsonb 运算符

运算符

描述

示例

jsonb @> jsonbboolean

第一个 JSON 值是否包含第二个?(有关包含的详细信息,请参阅 第 8.14.3 节。)

'{"a":1, "b":2}'::jsonb @> '{"b":2}'::jsonbt

jsonb <@ jsonbboolean

第一个 JSON 值是否包含在第二个中?

'{"b":2}'::jsonb <@ '{"a":1, "b":2}'::jsonbt

jsonb ? textboolean

文本字符串作为顶层键或数组元素是否存在于 JSON 值中?

'{"a":1, "b":2}'::jsonb ? 'b't

'["a", "b", "c"]'::jsonb ? 'b't

jsonb ?| text[]boolean

文本数组中的任何字符串作为顶层键或数组元素是否存在?

'{"a":1, "b":2, "c":3}'::jsonb ?| array['b', 'd']t

jsonb ?& text[]boolean

文本数组中的所有字符串作为顶层键或数组元素是否存在?

'["a", "b", "c"]'::jsonb ?& array['a', 'b']t

jsonb || jsonbjsonb

连接两个 jsonb 值。连接两个数组生成一个包含每个输入所有元素的数组。连接两个对象生成一个包含它们键的并集的对象,在存在重复键时取第二个对象的值。所有其他情况通过将非数组输入转换为单个元素数组来处理,然后像两个数组一样继续。不递归操作:只合并顶层数组或对象结构。

'["a", "b"]'::jsonb || '["a", "d"]'::jsonb["a", "b", "a", "d"]

'{"a": "b"}'::jsonb || '{"c": "d"}'::jsonb{"a": "b", "c": "d"}

'[1, 2]'::jsonb || '3'::jsonb[1, 2, 3]

'{"a": "b"}'::jsonb || '42'::jsonb[{"a": "b"}, 42]

要将一个数组作为单个条目附加到另一个数组,请将其包装在额外的数组层中,例如

'[1, 2]'::jsonb || jsonb_build_array('[3, 4]'::jsonb)[1, 2, [3, 4]]

jsonb - textjsonb

从 JSON 对象中删除一个键(及其值),或从 JSON 数组中删除匹配的字符串值。

'{"a": "b", "c": "d"}'::jsonb - 'a'{"c": "d"}

'["a", "b", "c", "b"]'::jsonb - 'b'["a", "c"]

jsonb - text[]jsonb

从左操作数中删除所有匹配的键或数组元素。

'{"a": "b", "c": "d"}'::jsonb - '{a,c}'::text[]{}

jsonb - integerjsonb

删除具有指定索引的数组元素(负整数从末尾开始计数)。如果 JSON 值不是数组,则抛出错误。

'["a", "b"]'::jsonb - 1["a"]

jsonb #- text[]jsonb

删除指定路径处的字段或数组元素,其中路径元素可以是字段键或数组索引。

'["a", {"b":1}]'::jsonb #- '{1,b}'["a", {}]

jsonb @? jsonpathboolean

JSON 路径是否为指定的 JSON 值返回任何项?(这仅对 SQL 标准 JSON 路径表达式有用,对谓词检查表达式无效,因为它们总是返回一个值。)

'{"a":[1,2,3,4,5]}'::jsonb @? '$.a[*] ? (@ > 2)'t

jsonb @@ jsonpathboolean

返回指定 JSON 值的 JSON 路径谓词检查结果。(这仅对谓词检查表达式有用,对 SQL 标准 JSON 路径表达式无效,因为如果路径结果不是单个布尔值,它将返回 NULL。)

'{"a":[1,2,3,4,5]}'::jsonb @@ '$.a[*] > 2't


注意

jsonpath 运算符 @?@@ 抑制以下错误:缺少对象字段或数组元素、意外的 JSON 项类型、日期时间或数字错误。下面描述的 jsonpath 相关函数也可以被告知抑制这些类型的错误。这种行为在搜索结构不同的 JSON 文档集合时可能会有所帮助。

表 9.49 显示了可用于构造 jsonjsonb 值的函数。此表中的某些函数具有 RETURNING 子句,该子句指定返回的数据类型。它必须是 jsonjsonbbytea、字符字符串类型(textcharvarchar),或可以转换为 json 的类型之一。默认情况下,返回 json 类型。

表 9.49。JSON 创建函数

函数

描述

示例

to_json ( anyelement ) → json

to_jsonb ( anyelement ) → jsonb

将任何 SQL 值转换为 jsonjsonb。数组和复合类型递归地转换为 JSON 中的数组和对象(多维数组成为 JSON 中的数组的数组)。否则,如果存在从 SQL 数据类型到 json 的转换,则将使用转换函数执行转换;[a] 否则,将生成一个标量 JSON 值。对于除数字、布尔值或空值之外的任何标量,将使用文本表示形式,并根据需要进行转义以使其成为有效的 JSON 字符串值。

to_json('Fred said "Hi."'::text)"Fred said \"Hi.\""

to_jsonb(row(42, 'Fred said "Hi."'::text)){"f1": 42, "f2": "Fred said \"Hi.\""}

array_to_json ( anyarray [, boolean ] ) → json

将 SQL 数组转换为 JSON 数组。行为与 to_json 相同,不同之处在于,如果可选的布尔参数为 true,则会在顶级数组元素之间添加换行符。

array_to_json('{{1,5},{99,100}}'::int[])[[1,5],[99,100]]

json_array ( [ { value_expression [ FORMAT JSON ] } [, ...] ] [ { NULL | ABSENT } ON NULL ] [ RETURNING data_type [ FORMAT JSON [ ENCODING UTF8 ] ] ])

json_array ( [ query_expression ] [ RETURNING data_type [ FORMAT JSON [ ENCODING UTF8 ] ] ])

从一系列 value_expression 参数或 query_expression 的结果构造一个 JSON 数组,后者必须是返回单个列的 SELECT 查询。如果指定了 ABSENT ON NULL,则忽略 NULL 值。如果使用 query_expression,则始终如此。

json_array(1,true,json '{"a":null}')[1, true, {"a":null}]

json_array(SELECT * FROM (VALUES(1),(2)) t)[1, 2]

row_to_json ( record [, boolean ] ) → json

将 SQL 复合值转换为 JSON 对象。行为与 to_json 相同,不同之处在于,如果可选的布尔参数为 true,则会在顶级元素之间添加换行符。

row_to_json(row(1,'foo')){"f1":1,"f2":"foo"}

json_build_array ( VARIADIC "any" ) → json

jsonb_build_array ( VARIADIC "any" ) → jsonb

从可变参数列表构建一个可能异构类型的 JSON 数组。每个参数都按照 to_jsonto_jsonb 进行转换。

json_build_array(1, 2, 'foo', 4, 5)[1, 2, "foo", 4, 5]

json_build_object ( VARIADIC "any" ) → json

jsonb_build_object ( VARIADIC "any" ) → jsonb

从可变参数列表构建一个 JSON 对象。按照惯例,参数列表由交替的键和值组成。键参数被强制转换为文本;值参数按照 to_jsonto_jsonb 进行转换。

json_build_object('foo', 1, 2, row(3,'bar')){"foo" : 1, "2" : {"f1":3,"f2":"bar"}}

json_object ( [ { key_expression { VALUE | ':' } value_expression [ FORMAT JSON [ ENCODING UTF8 ] ] }[, ...] ] [ { NULL | ABSENT } ON NULL ] [ { WITH | WITHOUT } UNIQUE [ KEYS ] ] [ RETURNING data_type [ FORMAT JSON [ ENCODING UTF8 ] ] ])

构造一个包含所有给定键/值对的 JSON 对象,如果没有给定则为空对象。key_expression 是一个标量表达式,定义JSON键,它被转换为 text 类型。它不能为 NULL,也不能属于具有到 json 类型转换的类型。如果指定了 WITH UNIQUE KEYS,则不能有任何重复的 key_expression。如果指定了 ABSENT ON NULL,则 value_expression 求值为 NULL 的任何对都将从输出中省略;如果指定了 NULL ON NULL 或省略了该子句,则键将包含值 NULL

json_object('code' VALUE 'P123', 'title': 'Jaws'){"code" : "P123", "title" : "Jaws"}

json_object ( text[] ) → json

jsonb_object ( text[] ) → jsonb

从文本数组构建 JSON 对象。数组必须具有恰好一个维度且成员数为偶数,在这种情况下,它们被视为交替的键/值对,或者两个维度,使得每个内部数组恰好有两个元素,它们被视为键/值对。所有值都转换为 JSON 字符串。

json_object('{a, 1, b, "def", c, 3.5}'){"a" : "1", "b" : "def", "c" : "3.5"}

json_object('{{a, 1}, {b, "def"}, {c, 3.5}}'){"a" : "1", "b" : "def", "c" : "3.5"}

json_object ( keys text[], values text[] ) → json

jsonb_object ( keys text[], values text[] ) → jsonb

此形式的 json_object 从单独的文本数组中成对地获取键和值。否则,它与单参数形式相同。

json_object('{a,b}', '{1,2}'){"a": "1", "b": "2"}

json ( expression [ FORMAT JSON [ ENCODING UTF8 ]] [ { WITH | WITHOUT } UNIQUE [ KEYS ]] ) → json

将指定为 textbytea 字符串(以 UTF8 编码)的给定表达式转换为 JSON 值。如果 expression 为 NULL,则返回一个SQL空值。如果指定了 WITH UNIQUE,则 expression 不得包含任何重复的对象键。

json('{"a":123, "b":[true,"foo"], "a":"bar"}'){"a":123, "b":[true,"foo"], "a":"bar"}

json_scalar ( expression )

将给定的 SQL 标量值转换为 JSON 标量值。如果输入为 NULL,则返回一个SQLnull。如果输入是数字或布尔值,则返回相应的 JSON 数字或布尔值。对于任何其他值,返回 JSON 字符串。

json_scalar(123.45)123.45

json_scalar(CURRENT_TIMESTAMP)"2022-05-10T10:51:04.62128-04:00"

json_serialize ( expression [ FORMAT JSON [ ENCODING UTF8 ] ] [ RETURNING data_type [ FORMAT JSON [ ENCODING UTF8 ] ] ] )

将 SQL/JSON 表达式转换为字符或二进制字符串。expression 可以是任何 JSON 类型、任何字符字符串类型或 UTF8 编码的 byteaRETURNING 中使用的返回类型可以是任何字符字符串类型或 bytea。默认是 text

json_serialize('{ "a" : 1 } ' RETURNING bytea)\x7b20226122203a2031207d20

[a] 例如,hstore 扩展具有从 hstorejson 的转换,因此通过 JSON 创建函数转换的 hstore 值将表示为 JSON 对象,而不是原始字符串值。


表 9.50 详细介绍了用于测试 JSON 的 SQL/JSON 工具。

表 9.50。SQL/JSON 测试函数

函数签名

描述

示例

expression IS [ NOT ] JSON [ { VALUE | SCALAR | ARRAY | OBJECT } ] [ { WITH | WITHOUT } UNIQUE [ KEYS ] ]

此谓词测试 expression 是否可以解析为 JSON,可能为指定类型。如果指定了 SCALARARRAYOBJECT,则测试 JSON 是否为该特定类型。如果指定了 WITH UNIQUE KEYS,则还会测试 expression 中的任何对象是否具有重复键。

SELECT js,
  js IS JSON "json?",
  js IS JSON SCALAR "scalar?",
  js IS JSON OBJECT "object?",
  js IS JSON ARRAY "array?"
FROM (VALUES
      ('123'), ('"abc"'), ('{"a": "b"}'), ('[1,2]'),('abc')) foo(js);
     js     | json? | scalar? | object? | array?
------------+-------+---------+---------+--------
 123        | t     | t       | f       | f
 "abc"      | t     | t       | f       | f
 {"a": "b"} | t     | f       | t       | f
 [1,2]      | t     | f       | f       | t
 abc        | f     | f       | f       | f
SELECT js,
  js IS JSON OBJECT "object?",
  js IS JSON ARRAY "array?",
  js IS JSON ARRAY WITH UNIQUE KEYS "array w. UK?",
  js IS JSON ARRAY WITHOUT UNIQUE KEYS "array w/o UK?"
FROM (VALUES ('[{"a":"1"},
 {"b":"2","b":"3"}]')) foo(js);
-[ RECORD 1 ]-+--------------------
js            | [{"a":"1"},        +
              |  {"b":"2","b":"3"}]
object?       | f
array?        | t
array w. UK?  | f
array w/o UK? | t

表 9.51 显示了可用于处理 jsonjsonb 值的函数。

表 9.51。JSON 处理函数

函数

描述

示例

json_array_elements ( json ) → setof json

jsonb_array_elements ( jsonb ) → setof jsonb

将顶级 JSON 数组扩展为一组 JSON 值。

select * from json_array_elements('[1,true, [2,false]]')

   value
-----------
 1
 true
 [2,false]

json_array_elements_text ( json ) → setof text

jsonb_array_elements_text ( jsonb ) → setof text

将顶级 JSON 数组扩展为一组 text 值。

select * from json_array_elements_text('["foo", "bar"]')

   value
-----------
 foo
 bar

json_array_length ( json ) → integer

jsonb_array_length ( jsonb ) → integer

返回顶级 JSON 数组中的元素数量。

json_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]')5

jsonb_array_length('[]')0

json_each ( json ) → setof record ( key text, value json )

jsonb_each ( jsonb ) → setof record ( key text, value jsonb )

将顶级 JSON 对象扩展为一组键/值对。

select * from json_each('{"a":"foo", "b":"bar"}')

 key | value
-----+-------
 a   | "foo"
 b   | "bar"

json_each_text ( json ) → setof record ( key text, value text )

jsonb_each_text ( jsonb ) → setof record ( key text, value text )

将顶级 JSON 对象扩展为一组键/值对。返回的 value 将是 text 类型。

select * from json_each_text('{"a":"foo", "b":"bar"}')

 key | value
-----+-------
 a   | foo
 b   | bar

json_extract_path ( from_json json, VARIADIC path_elems text[] ) → json

jsonb_extract_path ( from_json jsonb, VARIADIC path_elems text[] ) → jsonb

在指定路径提取 JSON 子对象。(这在功能上等同于 #> 运算符,但在某些情况下,将路径写成可变列表可能更方便。)

json_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}', 'f4', 'f6')"foo"

json_extract_path_text ( from_json json, VARIADIC path_elems text[] ) → text

jsonb_extract_path_text ( from_json jsonb, VARIADIC path_elems text[] ) → text

在指定路径提取 JSON 子对象作为 text。(这在功能上等同于 #>> 运算符。)

json_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}', 'f4', 'f6')foo

json_object_keys ( json ) → setof text

jsonb_object_keys ( jsonb ) → setof text

返回顶级 JSON 对象中的键集。

select * from json_object_keys('{"f1":"abc","f2":{"f3":"a", "f4":"b"}}')

 json_object_keys
------------------
 f1
 f2

json_populate_record ( base anyelement, from_json json ) → anyelement

jsonb_populate_record ( base anyelement, from_json jsonb ) → anyelement

将顶级 JSON 对象扩展为具有 base 参数的复合类型的行。扫描 JSON 对象以查找名称与输出行类型的列名匹配的字段,并将其值插入输出的这些列中。(不对应任何输出列名的字段将被忽略。)在典型使用中,base 的值只是 NULL,这意味着任何不匹配任何对象字段的输出列都将填充 NULL。但是,如果 base 不是 NULL,则其中包含的值将用于不匹配的列。

要将 JSON 值转换为输出列的 SQL 类型,按顺序应用以下规则

  • JSON 空值在所有情况下都转换为 SQL 空值。

  • 如果输出列的类型为 jsonjsonb,则 JSON 值会完全再现。

  • 如果输出列是复合(行)类型,并且 JSON 值是 JSON 对象,则通过递归应用这些规则,将对象的字段转换为输出行类型的列。

  • 同样,如果输出列是数组类型,并且 JSON 值是 JSON 数组,则通过递归应用这些规则,将 JSON 数组的元素转换为输出数组的元素。

  • 否则,如果 JSON 值是字符串,则字符串的内容将提供给该列数据类型的输入转换函数。

  • 否则,JSON 值的普通文本表示形式将提供给该列数据类型的输入转换函数。

虽然下面的示例使用常量 JSON 值,但典型用法是横向引用查询的 FROM 子句中另一个表中的 jsonjsonb 列。在 FROM 子句中编写 json_populate_record 是一个好习惯,因为所有提取的列都可以在不重复函数调用的情况下使用。

create type subrowtype as (d int, e text); create type myrowtype as (a int, b text[], c subrowtype);

select * from json_populate_record(null::myrowtype, '{"a": 1, "b": ["2", "a b"], "c": {"d": 4, "e": "a b c"}, "x": "foo"}')

 a |   b       |      c
---+-----------+-------------
 1 | {2,"a b"} | (4,"a b c")

jsonb_populate_record_valid ( base anyelement, from_json json ) → boolean

用于测试 jsonb_populate_record 的函数。如果给定输入 JSON 对象,输入 jsonb_populate_record 将在没有错误的情况下完成,则返回 true;也就是说,它是有效输入,否则返回 false

create type jsb_char2 as (a char(2));

select jsonb_populate_record_valid(NULL::jsb_char2, '{"a": "aaa"}');

 jsonb_populate_record_valid
-----------------------------
 f
(1 row)

select * from jsonb_populate_record(NULL::jsb_char2, '{"a": "aaa"}') q;

ERROR:  value too long for type character(2)

select jsonb_populate_record_valid(NULL::jsb_char2, '{"a": "aa"}');

 jsonb_populate_record_valid
-----------------------------
 t
(1 row)

select * from jsonb_populate_record(NULL::jsb_char2, '{"a": "aa"}') q;

 a
----
 aa
(1 row)

json_populate_recordset ( base anyelement, from_json json ) → setof anyelement

jsonb_populate_recordset ( base anyelement, from_json jsonb ) → setof anyelement

将顶级 JSON 对象数组扩展为具有 base 参数的复合类型的一组行。JSON 数组的每个元素都按照上述 json[b]_populate_record 的描述进行处理。

create type twoints as (a int, b int);

select * from json_populate_recordset(null::twoints, '[{"a":1,"b":2}, {"a":3,"b":4}]')

 a | b
---+---
 1 | 2
 3 | 4

json_to_record ( json ) → record

jsonb_to_record ( jsonb ) → record

将顶级 JSON 对象扩展为具有 AS 子句定义的复合类型的行。(与所有返回 record 的函数一样,调用查询必须使用 AS 子句明确定义记录的结构。)输出记录从 JSON 对象的字段填充,其方式与上述 json[b]_populate_record 相同。由于没有输入记录值,因此不匹配的列始终填充 NULL。

create type myrowtype as (a int, b text);

select * from json_to_record('{"a":1,"b":[1,2,3],"c":[1,2,3],"e":"bar","r": {"a": 123, "b": "a b c"}}') as x(a int, b text, c int[], d text, r myrowtype)

 a |    b    |    c    | d |       r
---+---------+---------+---+---------------
 1 | [1,2,3] | {1,2,3} |   | (123,"a b c")

json_to_recordset ( json ) → setof record

jsonb_to_recordset ( jsonb ) → setof record

将顶级 JSON 对象数组扩展为具有 AS 子句定义的复合类型的一组行。(与所有返回 record 的函数一样,调用查询必须使用 AS 子句明确定义记录的结构。)JSON 数组的每个元素都按照上述 json[b]_populate_record 的描述进行处理。

select * from json_to_recordset('[{"a":1,"b":"foo"}, {"a":"2","c":"bar"}]') as x(a int, b text)

 a |  b
---+-----
 1 | foo
 2 |

jsonb_set ( target jsonb, path text[], new_value jsonb [, create_if_missing boolean ] ) → jsonb

返回 target,其中由 path 指定的项被 new_value 替换,如果 create_if_missing 为 true(默认值)并且由 path 指定的项不存在,则添加 new_value。路径中所有较早的步骤必须存在,否则返回 target 不变。与路径定向运算符一样,path 中出现的负整数从 JSON 数组的末尾开始计数。如果最后一个路径步骤是超出范围的数组索引,并且 create_if_missing 为 true,则新值将添加到数组的开头(如果索引为负),或添加到数组的末尾(如果索引为正)。

jsonb_set('[{"f1":1,"f2":null},2,null,3]', '{0,f1}', '[2,3,4]', false)[{"f1": [2, 3, 4], "f2": null}, 2, null, 3]

jsonb_set('[{"f1":1,"f2":null},2]', '{0,f3}', '[2,3,4]')[{"f1": 1, "f2": null, "f3": [2, 3, 4]}, 2]

jsonb_set_lax ( target jsonb, path text[], new_value jsonb [, create_if_missing boolean [, null_value_treatment text ]] ) → jsonb

如果 new_value 不是 NULL,则行为与 jsonb_set 完全相同。否则,行为取决于 null_value_treatment 的值,该值必须是 'raise_exception''use_json_null''delete_key''return_target' 之一。默认值为 'use_json_null'

jsonb_set_lax('[{"f1":1,"f2":null},2,null,3]', '{0,f1}', null)[{"f1": null, "f2": null}, 2, null, 3]

jsonb_set_lax('[{"f1":99,"f2":null},2]', '{0,f3}', null, true, 'return_target')[{"f1": 99, "f2": null}, 2]

jsonb_insert ( target jsonb, path text[], new_value jsonb [, insert_after boolean ] ) → jsonb

返回插入 new_valuetarget。如果由 path 指定的项是数组元素,则如果 insert_after 为 false(默认值),new_value 将插入在该项之前,否则如果 insert_after 为 true,则插入在该项之后。如果由 path 指定的项是对象字段,则仅当对象不包含该键时才插入 new_value。路径中所有较早的步骤都必须存在,否则返回 target 不变。与路径定向运算符一样,path 中出现的负整数从 JSON 数组的末尾开始计数。如果最后一个路径步骤是超出范围的数组索引,则如果索引为负,新值将添加到数组的开头,如果索引为正,则添加到数组的末尾。

jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '"new_value"'){"a": [0, "new_value", 1, 2]}

jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '"new_value"', true){"a": [0, 1, "new_value", 2]}

json_strip_nulls ( target json [,strip_in_arrays boolean ] ) → json

jsonb_strip_nulls ( target jsonb [,strip_in_arrays boolean ] ) → jsonb

递归地从给定 JSON 值中删除所有具有 null 值的对象字段。如果 strip_in_arrays 为 true(默认值为 false),则也会删除空数组元素。否则它们不会被删除。裸 null 值永远不会被删除。

json_strip_nulls('[{"f1":1, "f2":null}, 2, null, 3]')[{"f1":1},2,null,3]

jsonb_strip_nulls('[1,2,null,3,4]', true);[1,2,3,4]

jsonb_path_exists ( target jsonb, path jsonpath [, vars jsonb [, silent boolean ]] ) → boolean

检查 JSON 路径是否为指定的 JSON 值返回任何项。(这仅对 SQL 标准 JSON 路径表达式有用,对谓词检查表达式无效,因为它们总是返回一个值。)如果指定了 vars 参数,它必须是一个 JSON 对象,其字段提供要替换到 jsonpath 表达式中的命名值。如果指定了 silent 参数且为 true,则该函数将抑制与 @?@@ 运算符相同的错误。

jsonb_path_exists('{"a":[1,2,3,4,5]}', '$.a[*] ? (@ >= $min && @ <= $max)', '{"min":2, "max":4}')t

jsonb_path_match ( target jsonb, path jsonpath [, vars jsonb [, silent boolean ]] ) → boolean

返回指定 JSON 值的 JSON 路径谓词检查的 SQL 布尔结果。(这仅对谓词检查表达式有用,对 SQL 标准 JSON 路径表达式无效,因为如果路径结果不是单个布尔值,它将失败或返回 NULL。)可选的 varssilent 参数与 jsonb_path_exists 的作用相同。

jsonb_path_match('{"a":[1,2,3,4,5]}', 'exists($.a[*] ? (@ >= $min && @ <= $max))', '{"min":2, "max":4}')t

jsonb_path_query ( target jsonb, path jsonpath [, vars jsonb [, silent boolean ]] ) → setof jsonb

返回 JSON 路径为指定 JSON 值返回的所有 JSON 项。对于 SQL 标准 JSON 路径表达式,它返回从 target 中选择的 JSON 值。对于谓词检查表达式,它返回谓词检查的结果:truefalsenull。可选的 varssilent 参数与 jsonb_path_exists 的作用相同。

select * from jsonb_path_query('{"a":[1,2,3,4,5]}', '$.a[*] ? (@ >= $min && @ <= $max)', '{"min":2, "max":4}')

 jsonb_path_query
------------------
 2
 3
 4

jsonb_path_query_array ( target jsonb, path jsonpath [, vars jsonb [, silent boolean ]] ) → jsonb

将 JSON 路径为指定 JSON 值返回的所有 JSON 项作为 JSON 数组返回。参数与 jsonb_path_query 相同。

jsonb_path_query_array('{"a":[1,2,3,4,5]}', '$.a[*] ? (@ >= $min && @ <= $max)', '{"min":2, "max":4}')[2, 3, 4]

jsonb_path_query_first ( target jsonb, path jsonpath [, vars jsonb [, silent boolean ]] ) → jsonb

返回 JSON 路径为指定 JSON 值返回的第一个 JSON 项,如果没有结果则返回 NULL。参数与 jsonb_path_query 相同。

jsonb_path_query_first('{"a":[1,2,3,4,5]}', '$.a[*] ? (@ >= $min && @ <= $max)', '{"min":2, "max":4}')2

jsonb_path_exists_tz ( target jsonb, path jsonpath [, vars jsonb [, silent boolean ]] ) → boolean

jsonb_path_match_tz ( target jsonb, path jsonpath [, vars jsonb [, silent boolean ]] ) → boolean

jsonb_path_query_tz ( target jsonb, path jsonpath [, vars jsonb [, silent boolean ]] ) → setof jsonb

jsonb_path_query_array_tz ( target jsonb, path jsonpath [, vars jsonb [, silent boolean ]] ) → jsonb

jsonb_path_query_first_tz ( target jsonb, path jsonpath [, vars jsonb [, silent boolean ]] ) → jsonb

这些函数的作用与上面描述的没有 _tz 后缀的函数类似,不同之处在于这些函数支持需要时区感知转换的日期/时间值比较。下面的示例需要将仅日期值 2015-08-02 解释为带时区的时间戳,因此结果取决于当前的 TimeZone 设置。由于此依赖关系,这些函数被标记为稳定,这意味着这些函数不能用于索引。它们的对应函数是不可变的,因此可以用于索引;但如果要求它们进行此类比较,它们将抛出错误。

jsonb_path_exists_tz('["2015-08-01 12:00:00-05"]', '$[*] ? (@.datetime() < "2015-08-02".datetime())')t

jsonb_pretty ( jsonb ) → text

将给定 JSON 值转换为美观的、带缩进的文本。

jsonb_pretty('[{"f1":1,"f2":null}, 2]')

[
    {
        "f1": 1,
        "f2": null
    },
    2
]

json_typeof ( json ) → text

jsonb_typeof ( jsonb ) → text

将顶级 JSON 值的类型作为文本字符串返回。可能的类型有 objectarraystringnumberbooleannull。(null 结果不应与 SQL NULL 混淆;请参阅示例。)

json_typeof('-123.4')number

json_typeof('null'::json)null

json_typeof(NULL::json) IS NULLt


9.16.2。SQL/JSON 路径语言 #

SQL/JSON 路径表达式指定从 JSON 值中检索的项,类似于用于访问 XML 内容的 XPath 表达式。在 PostgreSQL 中,路径表达式作为 jsonpath 数据类型实现,并且可以使用 第 8.14.7 节 中描述的任何元素。

JSON 查询函数和运算符将提供的路径表达式传递给路径引擎进行评估。如果表达式匹配查询的 JSON 数据,则返回相应的 JSON 项或项集。如果没有匹配,结果将是 NULLfalse 或错误,具体取决于函数。路径表达式以 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 值的适当引用的字符串常量。)

要检索可用的轨迹段,您需要使用 .key 访问器运算符来向下遍历周围的 JSON 对象,例如

=> 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]

每个路径评估步骤的结果都可以通过 第 9.16.2.3 节 中列出的一个或多个 jsonpath 运算符和方法进行处理。每个方法名称前面必须有句点。例如,您可以获取数组的大小

=> select jsonb_path_query(:'json', '$.track.segments.size()');
 jsonb_path_query
------------------
 2

有关在路径表达式中使用 jsonpath 运算符和方法的更多示例,请参见下面的 第 9.16.2.3 节

路径还可以包含筛选表达式,其工作方式类似于 SQL 中的 WHERE 子句。筛选表达式以问号开头,并在括号中提供条件

? (condition)

筛选表达式必须紧接在其应应用的路径评估步骤之后编写。该步骤的结果将经过筛选,只包含满足所提供条件的项。SQL/JSON 定义了三值逻辑,因此条件可以生成 truefalseunknownunknown 值的作用与 SQL NULL 相同,可以使用 is unknown 谓词进行测试。进一步的路径评估步骤只使用筛选表达式返回 true 的那些项。

可在筛选表达式中使用的函数和运算符列在 表 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

9.16.2.1. 与 SQL 标准的偏差 #

PostgreSQL 的 SQL/JSON 路径语言实现与 SQL/JSON 标准存在以下偏差。

9.16.2.1.1. 布尔谓词检查表达式 #

作为 SQL 标准的扩展,PostgreSQL 路径表达式可以是布尔谓词,而 SQL 标准只允许谓词出现在筛选器中。SQL 标准路径表达式返回查询 JSON 值中的相关元素,而谓词检查表达式返回谓词的单个三值 jsonb 结果:truefalsenull。例如,我们可以这样编写 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 函数)一起使用。

9.16.2.1.2. 正则表达式解释 #

like_regex 筛选器中使用的正则表达式模式的解释存在细微差别,如 第 9.16.2.4 节中所述。

9.16.2.2. 宽松模式和严格模式 #

当您查询 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)

9.16.2.3. SQL/JSON 路径运算符和方法 #

表 9.52 显示了 jsonpath 中可用的运算符和方法。请注意,虽然一元运算符和方法可以应用于前一个路径步骤产生的多个值,但二元运算符(加法等)只能应用于单个值。在宽松模式下,应用于数组的方法将对数组中的每个值执行。例外是 .type().size(),它们应用于数组本身。

表 9.52. jsonpath 运算符和方法

运算符/方法

描述

示例

数字 + 数字数字

加法。

jsonb_path_query('[2]', '$[0] + 3')5

+ 数字数字

一元加(无操作);与加法不同,这可以迭代多个值

jsonb_path_query_array('{"x": [2,3,4]}', '+ $.x')[2, 3, 4]

数字 - 数字数字

减法。

jsonb_path_query('[2]', '7 - $[0]')5

- 数字数字

取反;与减法不同,这可以迭代多个值

jsonb_path_query_array('{"x": [2,3,4]}', '- $.x')[-2, -3, -4]

数字 * 数字数字

乘法。

jsonb_path_query('[4]', '2 * $[0]')8

数字 / 数字数字

除法

jsonb_path_query('[8.5]', '$[0] / 2')4.2500000000000000

数字 % 数字数字

模数(余数)

jsonb_path_query('[32]', '$[0] % 10')2

. type()字符串

JSON 项的类型(参见 json_typeof

jsonb_path_query_array('[1, "2", {}]', '$[*].type()')["number", "string", "object"]

. size()数字

JSON 项的大小(数组元素的数量,如果不是数组则为 1)

jsonb_path_query('{"m": [11, 15]}', '$.m.size()')2

. boolean()布尔值

从 JSON 布尔值、数字或字符串转换的布尔值

jsonb_path_query_array('[1, "yes", false]', '$[*].boolean()')[true, true, false]

. string()字符串

从 JSON 布尔值、数字、字符串或日期时间转换的字符串值

jsonb_path_query_array('[1.23, "xyz", false]', '$[*].string()')["1.23", "xyz", "false"]

jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp().string()')"2023-08-15T12:34:56"

. double()数字

从 JSON 数字或字符串转换的近似浮点数

jsonb_path_query('{"len": "1.9"}', '$.len.double() * 2')3.8

数字 . ceiling()数字

大于或等于给定数字的最近整数

jsonb_path_query('{"h": 1.3}', '$.h.ceiling()')2

数字 . floor()数字

小于或等于给定数字的最近整数

jsonb_path_query('{"h": 1.7}', '$.h.floor()')1

数字 . abs()数字

给定数字的绝对值

jsonb_path_query('{"z": -0.3}', '$.z.abs()')0.3

. bigint()大整数

从 JSON 数字或字符串转换的大整数值

jsonb_path_query('{"len": "9876543219"}', '$.len.bigint()')9876543219

. decimal( [ 精度 [ , 标度 ] ] )十进制

从 JSON 数字或字符串转换的舍入十进制值(precisionscale 必须是整数值)

jsonb_path_query('1234.5678', '$.decimal(6, 2)')1234.57

. integer()整数

从 JSON 数字或字符串转换的整数值

jsonb_path_query('{"len": "12345"}', '$.len.integer()')12345

. number()数字型

从 JSON 数字或字符串转换的数字值

jsonb_path_query('{"len": "123.45"}', '$.len.number()')123.45

字符串 . datetime()日期时间类型 (参见注释)

从字符串转换的日期/时间值

jsonb_path_query('["2015-8-1", "2015-08-12"]', '$[*] ? (@.datetime() < "2015-08-2".datetime())')"2015-8-1"

字符串 . datetime(模板)日期时间类型 (参见注释)

使用指定的 to_timestamp 模板从字符串转换的日期/时间值

jsonb_path_query_array('["12:30", "18:40"]', '$[*].datetime("HH24:MI")')["12:30:00", "18:40:00"]

字符串 . date()日期

从字符串转换的日期值

jsonb_path_query('"2023-08-15"', '$.date()')"2023-08-15"

字符串 . time()无时区时间

从字符串转换的无时区时间值

jsonb_path_query('"12:34:56"', '$.time()')"12:34:56"

字符串 . time(精度)无时区时间

从字符串转换的无时区时间值,分数秒调整到给定精度

jsonb_path_query('"12:34:56.789"', '$.time(2)')"12:34:56.79"

字符串 . time_tz()带时区时间

从字符串转换的带时区时间值

jsonb_path_query('"12:34:56 +05:30"', '$.time_tz()')"12:34:56+05:30"

字符串 . time_tz(精度)带时区时间

从字符串转换的带时区时间值,分数秒调整到给定精度

jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(2)')"12:34:56.79+05:30"

字符串 . timestamp()无时区时间戳

从字符串转换的无时区时间戳值

jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp()')"2023-08-15T12:34:56"

字符串 . timestamp(精度)无时区时间戳

从字符串转换的无时区时间戳值,分数秒调整到给定精度

jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(2)')"2023-08-15T12:34:56.79"

字符串 . timestamp_tz()带时区时间戳

从字符串转换的带时区时间戳值

jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()')"2023-08-15T12:34:56+05:30"

字符串 . timestamp_tz(精度)带时区时间戳

从字符串转换的带时区时间戳值,分数秒调整到给定精度

jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(2)')"2023-08-15T12:34:56.79+05:30"

对象 . keyvalue()数组

对象的键值对,表示为包含三个字段的对象数组:"key""value""id""id" 是键值对所属对象的唯一标识符

jsonb_path_query_array('{"x": "20", "y": 32}', '$.keyvalue()')[{"id": 0, "key": "x", "value": "20"}, {"id": 0, "key": "y", "value": 32}]


注意

datetime()datetime(template) 方法的结果类型可以是 datetimetztimetimestamptztimestamp。这两种方法都会动态确定其结果类型。

datetime() 方法依次尝试将其输入字符串与 datetimetztimetimestamptztimestamp 的 ISO 格式匹配。它在第一个匹配的格式处停止并发出相应的数据类型。

datetime(template) 方法根据所提供的模板字符串中使用的字段确定结果类型。

datetime()datetime(template) 方法使用与 to_timestamp SQL 函数相同的解析规则(参见 第 9.8 节),但有三个例外。首先,这些方法不允许不匹配的模板模式。其次,模板字符串中只允许以下分隔符:连字符、句号、斜线、逗号、撇号、分号、冒号和空格。第三,模板字符串中的分隔符必须与输入字符串精确匹配。

如果需要比较不同的日期/时间类型,则会应用隐式转换。date 值可以转换为 timestamptimestamptztimestamp 可以转换为 timestamptztime 可以转换为 timetz。然而,除了第一个转换之外,所有这些转换都取决于当前的 TimeZone 设置,因此只能在支持时区的 jsonpath 函数中执行。类似地,其他将字符串转换为日期/时间类型的日期/时间相关方法也会执行此转换,其中可能涉及当前的 TimeZone 设置。因此,这些转换也只能在支持时区的 jsonpath 函数中执行。

表 9.53 显示了可用的筛选表达式元素。

表 9.53. jsonpath 筛选表达式元素

谓词/值

描述

示例

== 布尔值

相等比较(此运算符以及其他比较运算符适用于所有 JSON 标量值)

jsonb_path_query_array('[1, "a", 1, 3]', '$[*] ? (@ == 1)')[1, 1]

jsonb_path_query_array('[1, "a", 1, 3]', '$[*] ? (@ == "a")')["a"]

!= 布尔值

<> 布尔值

不相等比较

jsonb_path_query_array('[1, 2, 1, 3]', '$[*] ? (@ != 1)')[2, 3]

jsonb_path_query_array('["a", "b", "c"]', '$[*] ? (@ <> "b")')["a", "c"]

< 布尔值

小于比较

jsonb_path_query_array('[1, 2, 3]', '$[*] ? (@ < 2)')[1]

<= 布尔值

小于或等于比较

jsonb_path_query_array('["a", "b", "c"]', '$[*] ? (@ <= "b")')["a", "b"]

> 布尔值

大于比较

jsonb_path_query_array('[1, 2, 3]', '$[*] ? (@ > 2)')[3]

>= 布尔值

大于或等于比较

jsonb_path_query_array('[1, 2, 3]', '$[*] ? (@ >= 2)')[2, 3]

true布尔值

JSON 常量 true

jsonb_path_query('[{"name": "John", "parent": false}, {"name": "Chris", "parent": true}]', '$[*] ? (@.parent == true)'){"name": "Chris", "parent": true}

false布尔值

JSON 常量 false

jsonb_path_query('[{"name": "John", "parent": false}, {"name": "Chris", "parent": true}]', '$[*] ? (@.parent == false)'){"name": "John", "parent": false}

null

JSON 常量 null(请注意,与 SQL 不同,与 null 的比较正常工作)

jsonb_path_query('[{"name": "Mary", "job": null}, {"name": "Michael", "job": "driver"}]', '$[*] ? (@.job == null) .name')"Mary"

布尔值 && 布尔值布尔值

布尔 AND

jsonb_path_query('[1, 3, 7]', '$[*] ? (@ > 1 && @ < 5)')3

布尔值 || 布尔值布尔值

布尔 OR

jsonb_path_query('[1, 3, 7]', '$[*] ? (@ < 1 || @ > 5)')7

! 布尔值布尔值

布尔 NOT

jsonb_path_query('[1, 3, 7]', '$[*] ? (!(@ < 5))')7

布尔值 is unknown布尔值

测试布尔条件是否为 unknown

jsonb_path_query('[-1, 2, 7, "foo"]', '$[*] ? ((@ > 0) is unknown)')"foo"

字符串 like_regex 字符串 [ flag 字符串 ] → 布尔值

测试第一个操作数是否与第二个操作数给定的正则表达式匹配,可选地通过 flag 字符字符串描述的修改(参见 第 9.16.2.4 节)。

jsonb_path_query_array('["abc", "abd", "aBdC", "abdacb", "babc"]', '$[*] ? (@ like_regex "^ab.*c")')["abc", "abdacb"]

jsonb_path_query_array('["abc", "abd", "aBdC", "abdacb", "babc"]', '$[*] ? (@ like_regex "^ab.*c" flag "i")')["abc", "aBdC", "abdacb"]

字符串 starts with 字符串布尔值

测试第二个操作数是否为第一个操作数的初始子字符串。

jsonb_path_query('["John Smith", "Mary Stone", "Bob Johnson"]', '$[*] ? (@ starts with "John")')"John Smith"

exists ( 路径表达式 )布尔值

测试路径表达式是否匹配至少一个 SQL/JSON 项。如果路径表达式会导致错误,则返回 unknown;第二个示例使用此方法在严格模式下避免“无此类键”错误。

jsonb_path_query('{"x": [1, 2], "y": [2, 4]}', 'strict $.* ? (exists (@ ? (@[*] > 2)))')[2, 4]

jsonb_path_query_array('{"value": 41}', 'strict $ ? (exists (@.name)) .name')[]


9.16.2.4. 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+$")

9.16.3. SQL/JSON 查询函数 #

表 9.54 中描述的 SQL/JSON 函数 JSON_EXISTS()JSON_QUERY()JSON_VALUE() 可用于查询 JSON 文档。每个函数都将 path_expression(一个 SQL/JSON 路径查询)应用于 context_item(文档)。有关 path_expression 可以包含哪些内容的更多详细信息,请参见 第 9.16.2 节path_expression 还可以引用变量,这些变量的值在每个函数支持的 PASSING 子句中指定其各自的名称。context_item 可以是 jsonb 值或可以成功转换为 jsonb 的字符串。

表 9.54. SQL/JSON 查询函数

函数签名

描述

示例

JSON_EXISTS (
context_item, path_expression
[ PASSING { value AS varname } [, ...]]
[{ TRUE | FALSE | UNKNOWN | ERROR } ON ERROR ]) → boolean

  • 如果应用于 context_item 的 SQL/JSON path_expression 产生任何项,则返回 true,否则返回 false。

  • ON ERROR 子句指定在 path_expression 评估期间发生错误时的行为。指定 ERROR 将导致抛出带有适当消息的错误。其他选项包括返回 booleanFALSETRUE,或值 UNKNOWN(实际上是 SQL NULL)。如果未指定 ON ERROR 子句,默认返回 booleanFALSE

示例

JSON_EXISTS(jsonb '{"key1": [1,2,3]}', 'strict $.key1[*] ? (@ > $x)' PASSING 2 AS x)t

JSON_EXISTS(jsonb '{"a": [1,2,3]}', 'lax $.a[5]' ERROR ON ERROR)f

JSON_EXISTS(jsonb '{"a": [1,2,3]}', 'strict $.a[5]' ERROR ON ERROR)

ERROR:  jsonpath array subscript is out of bounds

JSON_QUERY (
context_item, path_expression
[ PASSING { value AS varname } [, ...]]
[ RETURNING data_type [ FORMAT JSON [ ENCODING UTF8 ] ] ]
[ { 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 ]) → jsonb

  • 返回将 SQL/JSON path_expression 应用于 context_item 的结果。

  • 默认情况下,结果以 jsonb 类型的值返回,尽管可以使用 RETURNING 子句将其返回为可以成功强制转换的其他类型。

  • 如果路径表达式可能返回多个值,则可能需要使用 WITH WRAPPER 子句包装这些值以使其成为有效的 JSON 字符串,因为默认行为是不包装它们,就像指定了 WITHOUT WRAPPER 一样。WITH WRAPPER 子句默认被理解为 WITH UNCONDITIONAL WRAPPER,这意味着即使是单个结果值也将被包装。要仅在存在多个值时应用包装器,请指定 WITH CONDITIONAL WRAPPER。如果指定了 WITHOUT WRAPPER,则结果中获取多个值将被视为错误。

  • 如果结果是标量字符串,默认情况下,返回值将用引号括起来,使其成为有效的 JSON 值。可以通过指定 KEEP QUOTES 来明确。相反,可以通过指定 OMIT QUOTES 来省略引号。为确保结果是有效的 JSON 值,当同时指定 WITH WRAPPER 时,不能指定 OMIT QUOTES

  • ON EMPTY 子句指定如果评估 path_expression 产生空集时的行为。ON ERROR 子句指定在评估 path_expression、将结果值强制转换为 RETURNING 类型或评估 ON EMPTY 表达式(如果 path_expression 评估返回空集)时发生错误时的行为。

  • 对于 ON EMPTYON ERROR,指定 ERROR 将导致抛出带有适当消息的错误。其他选项包括返回 SQL NULL、空数组(EMPTY [ARRAY])、空对象(EMPTY OBJECT)或用户指定的表达式(DEFAULT expression),该表达式可以强制转换为 jsonb 或 RETURNING 中指定的类型。如果未指定 ON EMPTYON ERROR,默认返回 SQL NULL 值。

示例

JSON_QUERY(jsonb '[1,[2,3],null]', 'lax $[*][$off]' PASSING 1 AS off WITH CONDITIONAL WRAPPER)3

JSON_QUERY(jsonb '{"a": "[1, 2]"}', 'lax $.a' OMIT QUOTES)[1, 2]

JSON_QUERY(jsonb '{"a": "[1, 2]"}', 'lax $.a' RETURNING int[] OMIT QUOTES ERROR ON ERROR)

ERROR:  malformed array literal: "[1, 2]"
DETAIL:  Missing "]" after array dimensions.

JSON_VALUE (
context_item, path_expression
[ PASSING { value AS varname } [, ...]]
[ RETURNING data_type ]
[ { ERROR | NULL | DEFAULT expression } ON EMPTY ]
[ { ERROR | NULL | DEFAULT expression } ON ERROR ]) → text

  • 返回将 SQL/JSON path_expression 应用于 context_item 的结果。

  • 仅当期望提取的值是单个 SQL/JSON 标量项时才使用 JSON_VALUE();获取多个值将被视为错误。如果您期望提取的值可能是对象或数组,请改用 JSON_QUERY 函数。SQL/JSON标量项;获取多个值将被视为错误。如果您期望提取的值可能是对象或数组,请改用 JSON_QUERY 函数。

  • 默认情况下,结果(必须是单个标量值)以 text 类型的值返回,尽管可以使用 RETURNING 子句将其返回为可以成功强制转换的其他类型。

  • ON ERRORON EMPTY 子句具有与 JSON_QUERY 描述中提到的类似语义,只是在抛出错误时返回的值集不同。

  • 请注意,JSON_VALUE 返回的标量字符串总是会删除其引号,这等同于在 JSON_QUERY 中指定 OMIT QUOTES

示例

JSON_VALUE(jsonb '"123.45"', '$' RETURNING float)123.45

JSON_VALUE(jsonb '"03:04 2015-02-01"', '$.datetime("HH24:MI YYYY-MM-DD")' RETURNING date)2015-02-01

JSON_VALUE(jsonb '[1,2]', 'strict $[$off]' PASSING 1 as off)2

JSON_VALUE(jsonb '[1,2]', 'strict $[*]' DEFAULT 9 ON ERROR)9


注意

如果 context_item 表达式不是 jsonb 类型,则通过隐式转换将其转换为 jsonb。但是请注意,在此转换过程中发生的任何解析错误都会无条件地抛出,也就是说,不会根据(指定或隐式)ON ERROR 子句进行处理。

注意

JSON_VALUE()path_expression 返回 JSON null 时返回 SQL NULL,而 JSON_QUERY() 直接返回 JSON null

9.16.4. JSON_TABLE #

JSON_TABLE 是一个 SQL/JSON 函数,用于查询JSON数据,并将结果呈现为关系视图,可以像常规 SQL 表一样访问。您可以在 SELECTUPDATEDELETEFROM 子句中使用 JSON_TABLE,并在 MERGE 语句中作为数据源。

以 JSON 数据作为输入,JSON_TABLE 使用 JSON 路径表达式从提供的数据中提取一部分作为构造视图的行模式。行模式给出的每个 SQL/JSON 值都作为构造视图中单独行的源。

为了将行模式拆分为列,JSON_TABLE 提供了 COLUMNS 子句,该子句定义了创建视图的模式。对于每个列,可以指定一个单独的 JSON 路径表达式,该表达式将针对行模式进行评估,以获取将成为给定输出行中指定列的值的 SQL/JSON 值。

可以使用 NESTED PATH 子句提取存储在行模式嵌套级别上的 JSON 数据。每个 NESTED PATH 子句都可以用于使用来自行模式嵌套级别的数据生成一个或多个列。这些列可以使用类似于顶层 COLUMNS 子句的 COLUMNS 子句进行指定。从 NESTED COLUMNS 构造的行称为子行,它们与从父 COLUMNS 子句中指定的列构造的行连接起来,以在最终视图中获取行。子列本身可能包含 NESTED PATH 规范,从而允许提取位于任意嵌套级别的数据。在同一级别由多个 NESTED PATH 生成的列被视为彼此的兄弟,并且它们在与父行连接后的行使用 UNION 合并。

JSON_TABLE 生成的行与生成它们的行横向连接,因此您不必显式地将构造的视图与原始表中保存的JSON数据进行连接。

语法是

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 [, ...] )

每个语法元素都将在下面更详细地描述。

上下文项, 路径表达式 [ AS json_path_name ] [ PASSING { AS 变量名 } [, ...]]

context_item 指定要查询的输入文档,path_expression 是一个定义查询的 SQL/JSON 路径表达式,而 json_path_namepath_expression 的可选名称。可选的 PASSING 子句为 path_expression 中提到的变量提供数据值。使用上述元素评估输入数据的结果称为行模式,它用作构造视图中行值的源。

COLUMNS ( json_table_column [, ...] )

COLUMNS 子句定义了构造视图的模式。在此子句中,您可以指定每个列都将填充一个 SQL/JSON 值,该值是通过将 JSON 路径表达式应用于行模式后获得的。json_table_column 具有以下变体

名称 FOR ORDINALITY

添加一个序号列,提供从 1 开始的顺序行编号。每个 NESTED PATH(参见下文)都会为其任何嵌套序号列获取自己的计数器。

名称 类型 [FORMAT JSON [ENCODING UTF8]] [ PATH 路径表达式 ]

将通过对行模式应用 path_expression 获得的 SQL/JSON 值在强制转换为指定的 type 后插入到视图的输出行中。

指定 FORMAT JSON 明确表示您期望该值是有效的 json 对象。只有当 typebpcharbyteacharacter varyingnamejsonjsonbtext 或这些类型上的域之一时,指定 FORMAT JSON 才有效。

可选地,您可以指定 WRAPPERQUOTES 子句来格式化输出。请注意,如果同时指定了 OMIT QUOTES,它将覆盖 FORMAT JSON,因为未加引号的字面量不构成有效的 json 值。

可选地,您可以使用 ON EMPTYON ERROR 子句指定在 JSON 路径评估结果为空时以及在 JSON 路径评估期间或将 SQL/JSON 值强制转换为指定类型时发生错误时是抛出错误还是返回指定值。两者的默认值都是返回 NULL 值。

注意

此子句在内部转换为 JSON_VALUEJSON_QUERY,并具有相同的语义。如果指定的类型不是标量类型,或者存在 FORMAT JSONWRAPPERQUOTES 子句,则为后者。

名称 类型 EXISTS [ PATH 路径表达式 ]

将通过对行模式应用 path_expression 获得的布尔值在强制转换为指定的 type 后插入到视图的输出行中。

该值对应于将 PATH 表达式应用于行模式是否产生任何值。

指定的 type 应该可以从 boolean 类型进行转换。

可选地,您可以使用 ON ERROR 指定在 JSON 路径评估期间或将 SQL/JSON 值强制转换为指定类型时发生错误时是抛出错误还是返回指定值。默认是返回布尔值 FALSE

注意

此子句在内部转换为 JSON_EXISTS,并具有相同的语义。

NESTED [ PATH ] 路径表达式 [ 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 子句,则使用路径表达式 $.name,其中 name 是提供的列名。

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_TABLEmy_films 表中的 JSON 对象转换为一个视图,其中包含原始 JSON 中包含的 kindtitledirector 键的列,以及一个序号列

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 填充标题和导演列,说明它们如何与父列 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“兄弟连接”,以及在 NESTED 级别使用 FOR ORDINALITY 列(列 movie_idbook_idauthor_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)

提交更正

如果您在文档中发现任何不正确、与您使用特定功能的经验不符或需要进一步澄清的内容,请使用 此表单 报告文档问题。