8.14.JSON类型#
- 8.14.1. JSON 输入和输出语法
- 8.14.2. 设计 JSON 文档
- 8.14.3.
jsonb
包含和存在 - 8.14.4.
jsonb
索引 - 8.14.5.
jsonb
脚本 - 8.14.6. 转换
- 8.14.7. jsonpath 类型
JSON 数据类型用于存储 JSON(JavaScript 对象表示法)数据,如RFC 7159中所述。此类数据也可以存储为text
,但 JSON 数据类型具有一个优势,即它强制执行每个存储的值都符合 JSON 规则。这些数据类型中存储的数据还提供各种特定的 JSON 函数和运算符;请参阅第 9.16 节。
PostgreSQL提供两种类型来存储 JSON 数据:json
和jsonb
。为了为这些数据类型实现高效的查询机制,PostgreSQL还提供了jsonpath
数据类型,如第 8.14.7 节中所述。
json
和jsonb
数据类型接受几乎相同的值集作为输入。主要的实际区别在于效率。json
数据类型存储输入文本的精确副本,处理函数必须在每次执行时重新解析该副本;而jsonb
数据存储在分解的二进制格式中,由于增加了转换开销,这使得输入速度略慢,但由于不需要重新解析,因此处理速度明显更快。jsonb
还支持索引,这可能是一个显著的优势。
由于json
类型存储输入文本的精确副本,因此它将保留标记之间的语义无关空格,以及 JSON 对象中键的顺序。此外,如果值中的 JSON 对象包含相同的键多次,则会保留所有键/值对。(处理函数将最后一个值视为有效值。)相比之下,jsonb
不保留空格,不保留对象键的顺序,也不保留重复的对象键。如果在输入中指定了重复键,则只保留最后一个值。
一般来说,大多数应用程序都应该首选将 JSON 数据存储为jsonb
,除非有非常特殊的需求,例如关于对象键顺序的传统假设。
RFC7159 规定 JSON 字符串应编码为 UTF8。因此,除非数据库编码为 UTF8,否则 JSON 类型不可能严格符合 JSON 规范。尝试直接包含无法在数据库编码中表示的字符将失败;相反,允许在数据库编码中表示但不在 UTF8 中表示的字符。
RFC7159 允许 JSON 字符串包含由\u*
XXXX*
表示的 Unicode 转义序列。在json
类型的输入函数中,无论数据库编码如何,都允许 Unicode 转义,并且仅检查语法是否正确(即,\u
后面有四个十六进制数字)。但是,jsonb
的输入函数更严格:它不允许对数据库编码中无法表示的字符进行 Unicode 转义。jsonb
类型还拒绝\u0000
(因为无法在PostgreSQL的text
类型中表示),并且它坚持正确使用任何 Unicode 代理对来指定 Unicode 基本多语言平面之外的字符。有效的 Unicode 转义将转换为等效的单个字符以进行存储;这包括将代理对折叠成单个字符。
注意
第 9.16 节中描述的许多 JSON 处理函数将把 Unicode 转义转换为常规字符,因此即使它们的输入类型为json
而不是jsonb
,也会引发刚才描述的相同类型的错误。尽管json
输入函数不进行这些检查可能被认为是历史遗留问题,但它确实允许在不支持表示字符的数据库编码中简单存储(不处理)JSON Unicode 转义。
将文本 JSON 输入转换为jsonb
时,RFC7159 描述的基本类型有效地映射到本机PostgreSQL类型,如表 8.23所示。因此,对于构成有效jsonb
数据的内容有一些额外的次要约束,这些约束不适用于json
类型,也不适用于抽象的 JSON,对应于底层数据类型可以表示的内容的限制。值得注意的是,jsonb
将拒绝超出PostgreSQLnumeric
数据类型范围的数字,而json
则不会。此类实现定义的限制受RFC7159 允许。然而,在实践中,此类问题更有可能发生在其他实现中,因为通常将 JSON 的number
基本类型表示为 IEEE 754 双精度浮点数(RFC7159 明确预期并允许这样做)。在将 JSON 作为与此类系统交换的格式使用时,应考虑与PostgreSQL原始存储的数据相比,数字精度丢失的危险。
相反,如表中所述,对 JSON 基本类型的输入格式有一些次要限制,这些限制不适用于相应的PostgreSQL类型。
表 8.23. JSON 基本类型和相应的PostgreSQL类型
JSON 基本类型 | PostgreSQL 类型 | 注释 |
---|---|---|
字符串 | 文本 | \u0000 不允许,表示数据库编码中不可用字符的 Unicode 转义也不允许 |
数字 | 数字 | NaN 和 infinity 值不允许 |
布尔值 | 布尔值 | 仅接受小写的 true 和 false 拼写 |
null | (无) | SQL NULL 是一个不同的概念 |
8.14.1. JSON 输入和输出语法#
JSON 数据类型的输入/输出语法如RFC7159 中所指定。
以下都是有效的json
(或jsonb
)表达式
-- Simple scalar/primitive value
-- Primitive values can be numbers, quoted strings, true, false, or null
SELECT '5'::json;
-- Array of zero or more elements (elements need not be of same type)
SELECT '[1, 2, "foo", null]'::json;
-- Object containing pairs of keys and values
-- Note that object keys must always be quoted strings
SELECT '{"bar": "baz", "balance": 7.77, "active": false}'::json;
-- Arrays and objects can be nested arbitrarily
SELECT '{"foo": [true, "bar"], "tags": {"a": 1, "b": null}}'::json;
如前所述,当输入一个 JSON 值,然后在没有任何其他处理的情况下打印它时,json
输出与输入相同的文本,而jsonb
不会保留语义上不重要的细节,如空格。例如,请注意以下差异
SELECT '{"bar": "baz", "balance": 7.77, "active":false}'::json;
json
-------------------------------------------------
{"bar": "baz", "balance": 7.77, "active":false}
(1 row)
SELECT '{"bar": "baz", "balance": 7.77, "active":false}'::jsonb;
jsonb
--------------------------------------------------
{"bar": "baz", "active": false, "balance": 7.77}
(1 row)
值得注意的一个语义上不重要的细节是,在jsonb
中,数字将根据底层numeric
类型的行为打印。在实践中,这意味着使用E
表示法输入的数字将不带表示法打印,例如
SELECT '{"reading": 1.230e-5}'::json, '{"reading": 1.230e-5}'::jsonb;
json | jsonb
-----------------------+-------------------------
{"reading": 1.230e-5} | {"reading": 0.00001230}
(1 row)
但是,jsonb
将保留尾随小数零,如本例所示,即使这些零对于相等性检查等目的在语义上不重要。
有关用于构造和处理 JSON 值的内置函数和运算符的列表,请参阅第 9.16 节。
8.14.2. 设计 JSON 文档#
将数据表示为 JSON 可以比传统的关联数据模型灵活得多,这在需求不断变化的环境中很有吸引力。两种方法完全有可能在同一应用程序中共存并相互补充。但是,即使对于需要最大灵活性的应用程序,仍然建议 JSON 文档具有一些固定的结构。结构通常是不可强制的(尽管可以声明性地强制一些业务规则),但具有可预测的结构可以更轻松地编写查询,以便对表中的一组“文档”(数据)进行有用的总结。
存储在表中的 JSON 数据受与任何其他数据类型相同的并发控制考虑因素的影响。虽然存储大型文档是可行的,但请记住,任何更新都会对整行获取行级锁。考虑将 JSON 文档限制为可管理的大小,以减少更新事务之间的锁争用。理想情况下,每个 JSON 文档都应该表示一个原子数据,业务规则规定不能合理地将其进一步细分为可以独立修改的较小数据。
8.14.3.jsonb
包含和存在#
测试包含是jsonb
的一项重要功能。json
类型没有一组平行的工具。包含测试一个jsonb
文档是否包含另一个文档。这些示例返回 true,除非另有说明
-- Simple scalar/primitive values contain only the identical value:
SELECT '"foo"'::jsonb @> '"foo"'::jsonb;
-- The array on the right side is contained within the one on the left:
SELECT '[1, 2, 3]'::jsonb @> '[1, 3]'::jsonb;
-- Order of array elements is not significant, so this is also true:
SELECT '[1, 2, 3]'::jsonb @> '[3, 1]'::jsonb;
-- Duplicate array elements don't matter either:
SELECT '[1, 2, 3]'::jsonb @> '[1, 2, 2]'::jsonb;
-- The object with a single pair on the right side is contained
-- within the object on the left side:
SELECT '{"product": "PostgreSQL", "version": 9.4, "jsonb": true}'::jsonb @> '{"version": 9.4}'::jsonb;
-- The array on the right side is not considered contained within the
-- array on the left, even though a similar array is nested within it:
SELECT '[1, 2, [1, 3]]'::jsonb @> '[1, 3]'::jsonb; -- yields false
-- But with a layer of nesting, it is contained:
SELECT '[1, 2, [1, 3]]'::jsonb @> '[[1, 3]]'::jsonb;
-- Similarly, containment is not reported here:
SELECT '{"foo": {"bar": "baz"}}'::jsonb @> '{"bar": "baz"}'::jsonb; -- yields false
-- A top-level key and an empty object is contained:
SELECT '{"foo": {"bar": "baz"}}'::jsonb @> '{"foo": {}}'::jsonb;
一般原则是,包含对象在结构和数据内容上必须与包含对象匹配,可能在从包含对象中丢弃一些不匹配的数组元素或对象键/值对之后。但请记住,在进行包含匹配时,数组元素的顺序并不重要,并且重复的数组元素实际上只考虑一次。
作为对结构必须匹配的一般原则的特殊例外,数组可以包含一个基本值
-- This array contains the primitive string value:
SELECT '["foo", "bar"]'::jsonb @> '"bar"'::jsonb;
-- This exception is not reciprocal -- non-containment is reported here:
SELECT '"bar"'::jsonb @> '["bar"]'::jsonb; -- yields false
jsonb
还具有一个存在运算符,它是包含主题的一个变体:它测试一个字符串(作为text
值给出)是否作为jsonb
值顶层的对象键或数组元素出现。这些示例返回 true,除非另有说明
-- String exists as array element:
SELECT '["foo", "bar", "baz"]'::jsonb ? 'bar';
-- String exists as object key:
SELECT '{"foo": "bar"}'::jsonb ? 'foo';
-- Object values are not considered:
SELECT '{"foo": "bar"}'::jsonb ? 'bar'; -- yields false
-- As with containment, existence must match at the top level:
SELECT '{"foo": {"bar": "baz"}}'::jsonb ? 'bar'; -- yields false
-- A string is considered to exist if it matches a primitive JSON string:
SELECT '"foo"'::jsonb ? 'foo';
当涉及到许多键或元素时,JSON 对象比数组更适合测试包含或存在,因为与数组不同,它们在内部针对搜索进行了优化,并且不需要线性搜索。
提示
由于 JSON 包含是嵌套的,因此适当的查询可以跳过子对象的显式选择。例如,假设我们有一个doc
列,其中包含顶层的对象,其中大多数对象包含tags
字段,其中包含子对象的数组。此查询查找同时包含"term":"paris"
和"term":"food"
的子对象的条目,同时忽略tags
数组外部的任何此类键可以使用以下方法完成相同的事情,但这种方法灵活性较低,而且通常效率也较低。另一方面,JSON 存在运算符不是嵌套的:它只会查找 JSON 值顶层的指定键或数组元素。
各种包含和存在运算符以及所有其他 JSON 运算符和函数在第 9.16 节中有记录。
8.14.4.jsonb
索引#
GIN 索引可用于有效搜索大量jsonb
文档(数据)中出现的键或键/值对。提供了两个 GIN“运算符类”,提供了不同的性能和灵活性权衡。
jsonb
的默认 GIN 运算符类支持带有键存在运算符?
、?|
和?&
、包含运算符@>
以及jsonpath
匹配运算符@?
和@@
的查询。(有关这些运算符实现的语义的详细信息,请参见表 9.46。)使用此运算符类创建索引的示例是
CREATE INDEX idxgin ON api USING GIN (jdoc);
非默认 GIN 运算符类jsonb_path_ops
不支持键存在运算符,但它支持@>
、@?
和@@
。使用此运算符类创建索引的示例是
CREATE INDEX idxginp ON api USING GIN (jdoc jsonb_path_ops);
考虑从第三方 Web 服务中检索 JSON 文档的表的示例,并使用已记录的模式定义。典型的文档是
{
"guid": "9c36adc1-7fb5-4d5b-83b4-90356a46061a",
"name": "Angela Barton",
"is_active": true,
"company": "Magnafone",
"address": "178 Howard Place, Gulf, Washington, 702",
"registered": "2009-11-07T08:53:22 +08:00",
"latitude": 19.793713,
"longitude": 86.513373,
"tags": [
"enim",
"aliquip",
"qui"
]
}
我们将这些文档存储在名为api
的表中,存储在名为jdoc
的jsonb
列中。如果在此列上创建 GIN 索引,则以下查询可以使用该索引
-- Find documents in which the key "company" has value "Magnafone"
SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @> '{"company": "Magnafone"}';
但是,该索引不能用于以下查询,因为尽管运算符?
可索引,但它并未直接应用于已索引列jdoc
-- Find documents in which the key "tags" contains key or array element "qui"
SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc -> 'tags' ? 'qui';
不过,通过适当使用表达式索引,上述查询可以使用索引。如果在"tags"
键中查询特定项很常见,则定义这样的索引可能是值得的
CREATE INDEX idxgintags ON api USING GIN ((jdoc -> 'tags'));
现在,WHERE
子句jdoc -> 'tags' ? 'qui'
将被识别为将可索引运算符?
应用于已索引表达式jdoc -> 'tags'
。(有关表达式索引的更多信息,请参阅第 11.7 节。)
另一种查询方法是利用包含,例如
-- Find documents in which the key "tags" contains array element "qui"
SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @> '{"tags": ["qui"]}';
在jdoc
列上创建简单的 GIN 索引可以支持此查询。但请注意,此类索引将存储jdoc
列中每个键和值的副本,而前一个示例的表达式索引仅存储在tags
键下找到的数据。虽然简单索引方法灵活得多(因为它支持对任何键的查询),但有针对性的表达式索引可能比简单索引更小且搜索速度更快。
GIN 索引还支持@?
和@@
运算符,它们执行jsonpath
匹配。示例如下
SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @? '$.tags[*] ? (@ == "qui")';
SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @@ '$.tags[*] == "qui"';
对于这些运算符,GIN 索引从jsonpath
模式中提取形式为*
accessors_chain*=*
constant*
的子句,并根据这些子句中提到的键和值进行索引搜索。访问器链可能包括.*
key*
、[*]
和[*
index*]
访问器。jsonb_ops
运算符类还支持.*
和.**
访问器,但jsonb_path_ops
运算符类不支持。
尽管jsonb_path_ops
运算符类仅支持带有@>
、@?
和@@
运算符的查询,但它比默认运算符类jsonb_ops
具有显著的性能优势。jsonb_path_ops
索引通常比针对相同数据的jsonb_ops
索引小得多,并且搜索的特殊性更好,尤其是在查询包含数据中经常出现的键时。因此,搜索操作通常比使用默认运算符类执行得更好。
jsonb_ops
和jsonb_path_ops
GIN 索引之间的技术差异在于,前者为数据中的每个键和值创建独立的索引项,而后者仅为数据中的每个值创建索引项。[7]基本上,每个jsonb_path_ops
索引项都是值和导致该值的键的哈希值;例如,要索引{"foo": {"bar": "baz"}}
,将创建一个单个索引项,将foo
、bar
和baz
全部合并到哈希值中。因此,查找此结构的包含查询将导致极具针对性的索引搜索;但完全无法找出foo
是否作为键出现。另一方面,jsonb_ops
索引将分别创建表示foo
、bar
和baz
的三个索引项;然后,为了执行包含查询,它将查找包含这三个项的所有行。虽然 GIN 索引可以相当高效地执行此类 AND 搜索,但它仍然不如等效的jsonb_path_ops
搜索具体且快速,尤其是如果包含三个索引项中任何一个的行的数量非常多时。
jsonb_path_ops
方法的一个缺点是,它不会为不包含任何值的 JSON 结构(例如{"a": {}}
)生成索引条目。如果请求搜索包含此类结构的文档,则需要进行全索引扫描,这非常慢。因此,jsonb_path_ops
不适合经常执行此类搜索的应用程序。
jsonb
还支持btree
和hash
索引。这些通常仅在检查完整 JSON 文档的相等性很重要时才有用。jsonb
数据的btree
排序很少引起极大兴趣,但为了完整性,它
Object > Array > Boolean > Number > String > Null
Object with n pairs > object with n - 1 pairs
Array with n elements > array with n - 1 elements
具有相同对数的 Object 按以下顺序比较
key-1, value-1, key-2 ...
请注意,Object 键按其存储顺序比较;特别是,由于较短的键存储在较长的键之前,这可能导致结果不直观,例如
{ "aa": 1, "c": 1} > {"b": 1, "d": 1}
类似地,具有相同元素数的数组按以下顺序比较
element-1, element-2 ...
原始 JSON 值使用与底层PostgreSQL数据类型相同的比较规则进行比较。字符串使用默认数据库排序规则进行比较。
8.14.5.jsonb
订阅#
jsonb
数据类型支持数组样式订阅表达式以提取和修改元素。嵌套值可以通过链接订阅表达式来指示,遵循与jsonb_set
函数中的path
参数相同的规则。如果jsonb
值是数组,则数字订阅从零开始,负整数从数组的最后一个元素向后计数。不支持切片表达式。订阅表达式的结果始终为 jsonb 数据类型。
UPDATE
语句可以在SET
子句中使用订阅来修改jsonb
值。订阅路径必须可遍历所有受影响的值,只要它们存在。例如,路径val['a']['b']['c']
可以一直遍历到c
,如果每个val
、val['a']
和val['a']['b']
是一个对象。如果任何val['a']
或val['a']['b']
未定义,它将被创建为空对象并根据需要填充。但是,如果任何val
本身或其中一个中间值被定义为非对象(例如字符串、数字或jsonb``null
),则遍历无法继续,因此会引发错误并中止事务。
订阅语法的示例
-- Extract object value by key
SELECT ('{"a": 1}'::jsonb)['a'];
-- Extract nested object value by key path
SELECT ('{"a": {"b": {"c": 1}}}'::jsonb)['a']['b']['c'];
-- Extract array element by index
SELECT ('[1, "2", null]'::jsonb)[1];
-- Update object value by key. Note the quotes around '1': the assigned
-- value must be of the jsonb type as well
UPDATE table_name SET jsonb_field['key'] = '1';
-- This will raise an error if any record's jsonb_field['a']['b'] is something
-- other than an object. For example, the value {"a": 1} has a numeric value
-- of the key 'a'.
UPDATE table_name SET jsonb_field['a']['b']['c'] = '1';
-- Filter records using a WHERE clause with subscripting. Since the result of
-- subscripting is jsonb, the value we compare it against must also be jsonb.
-- The double quotes make "value" also a valid jsonb string.
SELECT * FROM table_name WHERE jsonb_field['key'] = '"value"';
通过订阅处理jsonb
赋值的方式与jsonb_set
处理一些极端情况的方式不同。当源jsonb
值为NULL
时,通过订阅进行的赋值将继续,就像它是一个空 JSON 值(对象或数组)一样,由订阅键暗示
-- Where jsonb_field was NULL, it is now {"a": 1}
UPDATE table_name SET jsonb_field['a'] = '1';
-- Where jsonb_field was NULL, it is now [1]
UPDATE table_name SET jsonb_field[0] = '1';
如果为包含元素过少的数组指定了索引,则将追加NULL
元素,直到索引可达并且可以设置该值。
-- Where jsonb_field was [], it is now [null, null, 2];
-- where jsonb_field was [0], it is now [0, null, 2]
UPDATE table_name SET jsonb_field[2] = '2';
只要最后一个要遍历的现有元素是对象或数组,jsonb
值就会接受对不存在的下标路径的赋值,正如相应的下标所暗示的(路径中最后一个下标指示的元素不会被遍历,可以是任何东西)。嵌套数组和对象结构将被创建,并且在前一种情况下,将按照下标路径指定的填充null
,直到可以放置已分配的值。
-- Where jsonb_field was {}, it is now {"a": [{"b": 1}]}
UPDATE table_name SET jsonb_field['a'][0]['b'] = '1';
-- Where jsonb_field was [], it is now [null, {"a": 1}]
UPDATE table_name SET jsonb_field[1]['a'] = '1';
8.14.6. 转换#
有其他扩展可用于为不同过程语言的jsonb
类型实现转换。
PL/Perl 的扩展名为jsonb_plperl
和jsonb_plperlu
。如果您使用它们,jsonb
值将根据需要映射到 Perl 数组、哈希和标量。
PL/Python 的扩展名为jsonb_plpython3u
。如果您使用它,jsonb
值将根据需要映射到 Python 字典、列表和标量。
在这些扩展中,jsonb_plperl
被认为是“受信任的”,也就是说,它可以由在当前数据库上具有CREATE
权限的非超级用户安装。其余需要超级用户权限才能安装。
8.14.7. jsonpath 类型#
在PostgreSQL中,jsonpath
类型实现了对 SQL/JSON 路径语言的支持,以有效查询 JSON 数据。它提供了已解析 SQL/JSON 路径表达式的二进制表示,该表达式指定路径引擎从 JSON 数据中检索的项目,以便使用 SQL/JSON 查询函数进一步处理。
SQL/JSON 路径谓词和运算符的语义通常遵循 SQL。同时,为了提供一种处理 JSON 数据的自然方式,SQL/JSON 路径语法使用了一些 JavaScript 约定
点 (
.
) 用于成员访问。方括号 (
[]
) 用于数组访问。与从 1 开始的常规 SQL 数组不同,SQL/JSON 数组是从 0 开始的。
SQL/JSON 路径表达式中的数字文字遵循 JavaScript 规则,这些规则在一些小细节上与 SQL 和 JSON 都不同。例如,SQL/JSON 路径允许.1
和1.
,它们在 JSON 中无效。支持非十进制整数文字和下划线分隔符,例如,1_000_000
、0x1EEE_FFFF
、0o273
、0b100101
。在 SQL/JSON 路径(和 JavaScript 中,但不在 SQL 本身中),基数前缀后不能直接出现下划线分隔符。
SQL/JSON 路径表达式通常在 SQL 查询中作为 SQL 字符串文字书写,因此它必须用单引号括起来,并且值中所需的任何单引号都必须加倍(请参阅第 4.1.2.1 节)。某些形式的路径表达式需要在其内部使用字符串文字。这些嵌入式字符串文字遵循 JavaScript/ECMAScript 约定:它们必须用双引号括起来,并且可以在其中使用反斜杠转义来表示难以键入的字符。特别是,在嵌入式字符串文字中编写双引号的方法是\"
,要编写反斜杠本身,您必须编写\\
。其他特殊反斜杠序列包括 JSON 字符串中识别的序列:\b
、\f
、\n
、\r
、\t
、\v
表示各种 ASCII 控制字符,以及\u*
NNNN*
表示由其 4 个十六进制数字代码点标识的 Unicode 字符。反斜杠语法还包括 JSON 不允许的两种情况:\x*
NN*
表示仅用两个十六进制数字编写的字符代码,以及\u{*
N...*}
表示用 1 到 6 个十六进制数字编写的字符代码。
路径表达式由一系列路径元素组成,这些元素可以是以下任何一种
JSON 原始类型的路径文字:Unicode 文本、数字、true、false 或 null。
在 表 8.24 中列出的路径变量。
在 表 8.25 中列出的访问器运算符。
jsonpath
运算符和方法,如 第 9.16.2.2 节 所列。括号,可用于提供筛选表达式或定义路径求值顺序。
有关将jsonpath
表达式与 SQL/JSON 查询函数一起使用的详细信息,请参阅第 9.16.2 节。
表 8.24.jsonpath
变量
变量 | 说明 |
---|---|
$ | 表示正在查询的 JSON 值的变量(上下文项)。 |
$varname | 已命名的变量。它的值可以由几个 JSON 处理函数的参数 vars 设置;有关详细信息,请参阅 表 9.49。 |
@ | 表示筛选表达式中路径求值结果的变量。 |
表 8.25.jsonpath
访问器
访问器运算符 | 说明 |
---|---|
| 返回具有指定键的对象成员的成员访问器。如果键名称与以 |
| 返回位于当前对象顶层的全部成员的值的通配符成员访问器。 |
| 处理当前对象的 JSON 层次结构的所有级别的递归通配符成员访问器,并返回所有成员值,而不管其嵌套级别如何。这是 SQL/JSON 标准的 PostgreSQL 扩展。 |
| 与 |
| 数组元素访问器。 指定的 |
| 返回所有数组元素的通配符数组元素访问器。 |
[7]对于此目的,术语“值”包括数组元素,尽管 JSON 术语有时将数组元素视为与对象中的值不同。