Redrock Postgres 搜索 英文
版本: 9.3 / 9.4 / 9.5 / 9.6 / 10 / 11 / 12 / 13 / 14 / 15 / 16 / 17

9.16. JSON 函数和运算符 #

9.16.1. 处理和创建 JSON 数据
9.16.2. SQL/JSON 路径语言
9.16.3. SQL/JSON 查询函数
9.16.4. JSON_TABLE

本节的描述包括

为在 SQL 环境中以本机方式支持 JSON 数据类型,PostgreSQL 实现SQL/JSON 数据模型。该模型由项序列组成。每个项都可以保存 SQL 标量值、一个额外的 SQL/JSON null 值以及使用 JSON 数组和对象的复合数据结构。该模型是 JSON 规范 RFC 7159 中的隐含数据模型的形式化。

SQL/JSON允许您处理类似于常规SQL数据的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 个元素,作为 文本

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

json ->> 文本文本

jsonb ->> 文本文本

将指定的键值提取 JSON 对象字段作为 文本

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

json #> 文本数组json

jsonb #> 文本数组jsonb

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

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

json #>> 文本数组文本

jsonb #>> 文本数组文本

将指定路径中的 JSON 子对象提取为 文本

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


注意

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

一些其他运算符仅存在于 jsonb 中,如 表 9.46 所示。 第 8.14.4 节 描述了如何使用这些运算符有效地搜索已编入索引的 jsonb 数据。

表 9.46. 其他 jsonb 运算符

运算符

描述

示例

jsonb @> jsonb布尔值

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

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

jsonb <@ jsonb布尔值

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

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

jsonb ? 文本布尔值

文本字符串是否作为 JSON 值内的顶级键或数组元素存在?

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

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

jsonb ?| 文本数组布尔值

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

'{"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 的结果(必须是返回单个列的 SELECT 查询)构建一个 JSON 数组。如果指定了 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 的标量表达式key,转换为 text 类型。它不能是 NULL,也不能属于可以转换为 json 类型的类型。如果指定了 WITH UNIQUE KEYS,则不能有任何重复的 key_expression。如果指定了 ABSENT ON NULL,则对于 value_expression 评估为 NULL 的任何对从输出中省略;如果指定了 NULL ON NULL 或省略该子句,则 key 包含在值 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,则SQLnull 值返回。如果指定了 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 编码的 bytea。在 RETURNING 中使用的返回类型可以是任何字符字符串类型或 bytea。默认为 text

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

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


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

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

将指定路径处的 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 null 值都转换为 SQL null。

  • 如果输出列的类型为 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。

创建 myrowtype 类型为 (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

使用 new_value 替换 path 指定的项目,或在 create_if_missing 为 true(这是默认值)且 path 指定的项目不存在时添加 new_value,返回 target。路径中的所有较早步骤必须存在,否则 target 将保持不变。与面向路径的 operator 一样,出现在 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,则 new_value 将在此元素之后插入。如果由 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 值中删除所有具有空值的字段,不对空值(非对象字段)进行修改。

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

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 值(content item),请在路径表达式中使用 $ 变量。路径的第一个元素必须始终为 $。它后面可以跟一个或多个 访问器运算符,这些访问器运算符逐层向下遍历 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]

每个路径评估步骤的结果都可以由 jsonpath 运算符处理,其中一种或多种运算符列于 第 9.16.2.3 节 所示。每个方法名称前面必须带有一个圆点。例如,您可以获取数组的大小

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

以下在 第 9.16.2.3 节 中列出了在路径表达式中使用 jsonpath 运算符和方法的更多示例。

路径还可能包含类似于 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. 布尔谓词检查表达式 #

作为一个对产品标准的扩展,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

+ 数字数字

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

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]

. 字符串()字符串

从 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"

. 双精度()数字

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

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

number . 向上取整()number

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

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

number . 向下取整()number

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

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

number . 绝对值()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"

字符串 . timestamp()无时区的timestamp

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

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

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

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

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

字符串 . timestamp_tz()带时区的timestamp

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

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

字符串 . timestamp_tz(精度)带时区的timestamp

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

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(模板)方法的结果类型可以是datetimetztimetimestamptztimestamp。这两种方法都动态地确定它们的结果类型。

方法 datetime() 顺序尝试将其输入字符串与 datetimetztimetimestamptz 以及 timestamp 的 ISO 格式匹配。会在第一个匹配的格式上停止,并发出对应的数据类型。

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

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

如果需要比较不同的日期/时间类型,则会应用隐式强制转换。可以将 date 值强制转换为 timestamptimestamptz,可以将 timestamp 强制转换为 timestamptz,可以将 time 强制转换为 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 < valuebool

小于比较

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

value <= valuebool

小于或等于比较

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

value > valuebool

大于比较

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

value >= valuebool

大于或等于比较

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

truebool

JSON 常量 true

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

falsebool

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"

bool && boolbool

布尔 AND

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

bool || boolbool

布尔 OR

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

booleanboolean

布尔非

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 运算符的正则表达式定义,而该运算符又使用了 XQuery 标准。PostgreSQL 目前不支持 LIKE_REGEX 运算符。因此, like_regex 筛选器是使用 POSIX 正则表达式引擎实现的,该引擎在 第 9.7.3 节 中进行了说明。这导致了与标准 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。

  • 如果在评估path_expression时发生错误,ON ERROR子句会指定行为。指定ERROR将使错误连同相应消息一起被抛出。其他选项包括返回布尔FALSETRUE或值UNKNOWN,它实际上是一个SQL NULL。如果未指定ON ERROR子句,则默认情况下将返回布尔FALSE

示例

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类型的value,但是RETURNING子句可以用来作为可以成功强制转换到另一个类型的value返回。

  • 如果路径表达式可能会返回多个值,则可能有必要使用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


注意

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

注意

如果 path_expression 返回 JSON 的 null 值,JSON_VALUE() 将返回 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 [, ...] )

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

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 子句。在此子句中,您可以指定要填充的每列,方法是针对行模式应用 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 对象。只有在 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 ]

取出通过向行模式应用 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 子句,则使用路径表达 $.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 为 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“同级连接”,还显示在 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)