支持的版本: 当前 (17) / 16 / 15 / 14 / 13
开发版本: 开发版
不支持的版本: 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.45 显示了可用于 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.45. 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,而不是失败。

某些其他操作符仅适用于 jsonb,如表 9.46所示。第 8.14.4 节描述了如何使用这些操作符有效地搜索索引的 jsonb 数据。

表 9.46. 其他 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.47 显示了可用于构造 jsonjsonb 值的函数。此表中的某些函数具有 RETURNING 子句,该子句指定返回的数据类型。它必须是 jsonjsonbbytea、字符字符串类型(textcharvarchar)或可以强制转换为 json 的类型。默认情况下,返回 json 类型。

表 9.47. JSON 创建函数

函数

描述

示例

to_json ( anyelement ) → json

to_jsonb ( anyelement ) → jsonb

将任何 SQL 值转换为 jsonjsonb。数组和复合类型会递归转换为数组和对象(多维数组在 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。如果 value_expression 的计算结果为 NULL,并且指定了 ABSENT ON 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 ( 表达式 [ FORMAT JSON [ ENCODING UTF8 ] ] [ RETURNING 数据类型 [ FORMAT JSON [ ENCODING UTF8 ] ] ] )

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

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

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


表 9.48 详细介绍了用于测试 JSON 的 SQL/JSON 功能。

表 9.48. SQL/JSON 测试函数

函数签名

描述

示例

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

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

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.49 显示了可用于处理 jsonjsonb 值的函数。

表 9.49. 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

text 形式提取指定路径上的 JSON 子对象。(这在功能上等效于 #>> 运算符。)

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,这意味着任何与任何对象字段不匹配的输出列都将填充为空值。但是,如果 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 ( json ) → json

jsonb_strip_nulls ( jsonb ) → jsonb

从给定的 JSON 值中递归删除所有具有 null 值的对象字段。不是对象字段的 Null 值保持不变。

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

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 标准 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 设置。由于这种依赖性,这些函数被标记为 stable,这意味着这些函数不能在索引中使用。它们的对应函数是 immutable 的,因此可以在索引中使用;但如果要求它们进行此类比较,则会抛出错误。

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.51 中列出。在筛选表达式中,@ 变量表示正在考虑的值(即,上一个路径步骤的一个结果)。可以在 @ 之后编写访问器运算符以检索组件项。

例如,假设您要检索所有高于 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 值的相关元素,而谓词检查表达式返回谓词的单个三值结果:truefalseunknown。例如,我们可以编写这个 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.50 显示了 jsonpath 中可用的运算符和方法。请注意,虽然一元运算符和方法可以应用于先前路径步骤产生的多个值,但二元运算符(加法等)只能应用于单个值。在宽松模式下,应用于数组的方法将为数组中的每个值执行。例外情况是 .type().size(),它们应用于数组本身。

表 9.50. jsonpath 运算符和方法

运算符/方法

描述

示例

number + numbernumber

加法

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

+ numbernumber

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

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

number - numbernumber

减法

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

- numbernumber

取负;与减法不同,它可以迭代多个值

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

number * numbernumber

乘法

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

number / numbernumber

除法

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

number % numbernumber

取模(余数)

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

value . type()string

JSON 项的类型(参见 json_typeof

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

value . size()number

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

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

value . boolean()boolean

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

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

value . string()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"

value . double()number

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

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

number . ceiling()number

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

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

number . floor()number

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

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

number . abs()number

给定数字的绝对值

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

value . bigint()bigint

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

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

value . decimal( [ precision [ , scale ] ] )decimal

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

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

value . integer()integer

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

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

value . number()numeric

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

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

string . datetime()datetime_type (参见注释)

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

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

string . datetime(template)datetime_type (见注释)

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

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

string . date()date

从字符串转换而来的日期值

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

string . time()time without time zone

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

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

string . time(precision)time without time zone

从字符串转换而来的不带时区的时间值,小数秒调整为给定的精度

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

string . time_tz()time with time zone

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

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

string . time_tz(precision)time with time zone

从字符串转换而来的带时区的时间值,小数秒调整为给定的精度

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

string . timestamp()timestamp without time zone

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

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

string . timestamp(precision)timestamp without time zone

从字符串转换而来的不带时区的时间戳值,小数秒调整为给定的精度

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

string . timestamp_tz()timestamp with time zone

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

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

string . timestamp_tz(precision)timestamp with time zone

从字符串转换而来的带时区的时间戳值,小数秒调整为给定的精度

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

object . keyvalue()array

对象的键值对,表示为包含三个字段的对象数组:"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.51 显示了可用的过滤器表达式元素。

表 9.51. jsonpath 过滤器表达式元素

谓词/值

描述

示例

value == valueboolean

相等比较(这个和其他比较运算符适用于所有 JSON 标量值)

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

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

value != valueboolean

value <> valueboolean

不相等比较

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

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

value < valueboolean

小于比较

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

value <= valueboolean

小于等于比较

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

value > valueboolean

大于比较

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

value >= valueboolean

大于等于比较

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

trueboolean

JSON 常量 true

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

falseboolean

JSON 常量 false

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

nullvalue

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

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

boolean && booleanboolean

布尔 AND

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

boolean || booleanboolean

布尔 OR

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

! booleanboolean

布尔 NOT

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

boolean is unknownboolean

测试布尔条件是否为 unknown

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

string like_regex string [ flag string ] → boolean

测试第一个操作数是否与第二个操作数给定的正则表达式匹配,可以选择使用由 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"]

string starts with stringboolean

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

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

exists ( path_expression )boolean

测试路径表达式是否匹配至少一个 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 运算符借用其正则表达式的定义,而 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.52 中描述的 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.52. 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 类型时,或者在 path_expression 求值返回空集时评估 ON EMPTY 表达式时发生错误时的行为。

  • 对于 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 的结果。

  • 仅当提取的值预计为单个值时才使用 JSON_VALUE()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 表进行访问。您可以在 SELECTUPDATEDELETE 语句的 FROM 子句中使用 JSON_TABLE,并在 MERGE 语句中将其用作数据源。

JSON_TABLE 接收 JSON 数据作为输入,使用 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 [, ...] )

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

context_item, path_expression [ AS json_path_name ] [ PASSING { value AS varname } [, ...]]

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

COLUMNS ( json_table_column [, ...] )

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

name FOR ORDINALITY

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

name type [FORMAT JSON [ENCODING UTF8]] [ PATH path_expression ]

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

指定 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 子句,则后者。

name type EXISTS [ PATH path_expression ]

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

该值对应于对行模式应用 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 子句,则使用路径表达式 $.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, 'Vertigo' AS filter2
     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)

提交更正

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