⌘+k ctrl+k
1.3 (稳定版)
搜索快捷键 cmd + k | ctrl + k
JSON 处理函数

JSON 提取函数

有两种提取函数,它们各自有对应的运算符。这些运算符仅当字符串存储为 JSON 逻辑类型时才能使用。这些函数支持与 JSON 标量函数 相同的两种位置表示法。

函数 别名 运算符 描述
json_exists(json, path)     如果提供的 path 存在于 json 中,则返回 true,否则返回 false
json_extract(json, path) json_extract_path -> 从给定 pathjson 中提取 JSON。如果 path 是一个 LIST,结果将是一个 JSONLIST
json_extract_string(json, path) json_extract_path_text ->> 从给定 pathjson 中提取 VARCHAR。如果 path 是一个 LIST,结果将是一个 VARCHARLIST
json_value(json, path)     从给定 pathjson 中提取 JSON。如果所提供路径上的 json 不是标量值,它将返回 NULL

请注意,用于 JSON 提取的箭头运算符 -> 优先级较低,因为它也用于 lambda 函数。因此,在表示相等比较 (=) 等操作时,您需要用括号将 -> 运算符括起来。例如

SELECT ((JSON '{"field": 42}')->'field') = 42;

警告 DuckDB 的 JSON 数据类型使用 0-based indexing(从0开始的索引)

示例

CREATE TABLE example (j JSON);
INSERT INTO example VALUES
    ('{ "family": "anatidae", "species": [ "duck", "goose", "swan", null ] }');
SELECT json_extract(j, '$.family') FROM example;
"anatidae"
SELECT j->'$.family' FROM example;
"anatidae"
SELECT j->'$.species[0]' FROM example;
"duck"
SELECT j->'$.species[*]' FROM example;
["duck", "goose", "swan", null]
SELECT j->>'$.species[*]' FROM example;
[duck, goose, swan, null]
SELECT j->'$.species'->0 FROM example;
"duck"
SELECT j->'species'->['/0', '/1'] FROM example;
['"duck"', '"goose"']
SELECT json_extract_string(j, '$.family') FROM example;
anatidae
SELECT j->>'$.family' FROM example;
anatidae
SELECT j->>'$.species[0]' FROM example;
duck
SELECT j->'species'->>0 FROM example;
duck
SELECT j->'species'->>['/0', '/1'] FROM example;
[duck, goose]

请注意,DuckDB 的 JSON 数据类型使用 0-based indexing(从0开始的索引)

如果需要从同一个 JSON 中提取多个值,更有效的方法是提取一个路径列表

以下操作将导致 JSON 被解析两次,

导致查询速度变慢且使用更多内存

SELECT
    json_extract(j, 'family') AS family,
    json_extract(j, 'species') AS species
FROM example;
family species
"anatidae" ["duck","goose","swan",null]

以下操作产生相同的结果,但更快且更节省内存

WITH extracted AS (
    SELECT json_extract(j, ['family', 'species']) AS extracted_list
    FROM example
)
SELECT
    extracted_list[1] AS family,
    extracted_list[2] AS species
FROM extracted;

JSON 标量函数

以下标量 JSON 函数可用于获取有关存储的 JSON 值的信息。除了 json_valid(json) 之外,所有 JSON 函数在提供无效 JSON 时都会产生错误。

我们支持两种表示法来描述 JSON 中的位置:JSON Pointer 和 JSONPath。

函数 描述
json_array_length(json[, path]) 返回 JSON 数组 json 中的元素数量,如果它不是 JSON 数组,则返回 0。如果指定了 path,则返回给定 path 处 JSON 数组中的元素数量。如果 path 是一个 LIST,结果将是数组长度的 LIST
json_contains(json_haystack, json_needle) 如果 json_needle 包含在 json_haystack 中,则返回 true。两个参数都是 JSON 类型,但 json_needle 也可以是数字值或字符串,不过字符串必须用双引号括起来。
json_keys(json[, path]) 如果 json 是一个 JSON 对象,则将其键作为 VARCHARLIST 返回。如果指定了 path,则返回给定 path 处 JSON 对象的键。如果 path 是一个 LIST,结果将是 VARCHARLISTLIST
json_structure(json) 返回 json 的结构。如果结构不一致(例如,数组中存在不兼容的类型),则默认为 JSON
json_type(json[, path]) 返回提供的 json 的类型,可以是 ARRAY, BIGINT, BOOLEAN, DOUBLE, OBJECT, UBIGINT, VARCHAR, 和 NULL 之一。如果指定了 path,则返回给定 path 处元素的类型。如果 path 是一个 LIST,结果将是类型的 LIST
json_valid(json) 返回 json 是否为有效的 JSON。
json(json) 解析并压缩 json

JSONPointer 语法使用 / 分隔每个字段。例如,要提取键为 duck 的数组的第一个元素,您可以这样做

SELECT json_extract('{"duck": [1, 2, 3]}', '/duck/0');
1

JSONPath 语法使用 . 分隔字段,并使用 [i] 访问数组元素,并且总是以 $ 开头。使用相同的例子,我们可以这样做

SELECT json_extract('{"duck": [1, 2, 3]}', '$.duck[0]');
1

请注意,DuckDB 的 JSON 数据类型使用 0-based indexing(从0开始的索引)

JSONPath 更具表达力,也可以从列表末尾访问

SELECT json_extract('{"duck": [1, 2, 3]}', '$.duck[#-1]');
3

JSONPath 还允许使用双引号转义语法标记

SELECT json_extract('{"duck.goose": [1, 2, 3]}', '$."duck.goose"[1]');
2

使用 鸭科生物家族 的示例

CREATE TABLE example (j JSON);
INSERT INTO example VALUES
    ('{ "family": "anatidae", "species": [ "duck", "goose", "swan", null ] }');
SELECT json(j) FROM example;
{"family":"anatidae","species":["duck","goose","swan",null]}
SELECT j.family FROM example;
"anatidae"
SELECT j.species[0] FROM example;
"duck"
SELECT json_valid(j) FROM example;
true
SELECT json_valid('{');
false
SELECT json_array_length('["duck", "goose", "swan", null]');
4
SELECT json_array_length(j, 'species') FROM example;
4
SELECT json_array_length(j, '/species') FROM example;
4
SELECT json_array_length(j, '$.species') FROM example;
4
SELECT json_array_length(j, ['$.species']) FROM example;
[4]
SELECT json_type(j) FROM example;
OBJECT
SELECT json_keys(j) FROM example;
[family, species]
SELECT json_structure(j) FROM example;
{"family":"VARCHAR","species":["VARCHAR"]}
SELECT json_structure('["duck", {"family": "anatidae"}]');
["JSON"]
SELECT json_contains('{"key": "value"}', '"value"');
true
SELECT json_contains('{"key": 1}', '1');
true
SELECT json_contains('{"top_key": {"key": "value"}}', '{"key": "value"}');
true

JSON 聚合函数

有三个 JSON 聚合函数。

函数 描述
json_group_array(any) 返回一个 JSON 数组,其中包含聚合中 any 的所有值。
json_group_object(key, value) 返回一个 JSON 对象,其中包含聚合中所有 keyvalue 对。
json_group_structure(json) 返回聚合中所有 json 的合并 json_structure

示例

CREATE TABLE example1 (k VARCHAR, v INTEGER);
INSERT INTO example1 VALUES ('duck', 42), ('goose', 7);
SELECT json_group_array(v) FROM example1;
[42, 7]
SELECT json_group_object(k, v) FROM example1;
{"duck":42,"goose":7}
CREATE TABLE example2 (j JSON);
INSERT INTO example2 VALUES
    ('{"family": "anatidae", "species": ["duck", "goose"], "coolness": 42.42}'),
    ('{"family": "canidae", "species": ["labrador", "bulldog"], "hair": true}');
SELECT json_group_structure(j) FROM example2;
{"family":"VARCHAR","species":["VARCHAR"],"coolness":"DOUBLE","hair":"BOOLEAN"}

将 JSON 转换为嵌套类型

在许多情况下,逐个从 JSON 中提取值效率低下。相反,我们可以一次性“提取”所有值,将 JSON 转换为嵌套类型 LISTSTRUCT

函数 描述
json_transform(json, structure) 根据指定的 structure 转换 json
from_json(json, structure) json_transform 的别名。
json_transform_strict(json, structure) json_transform 相同,但当类型转换失败时会抛出错误。
from_json_strict(json, structure) json_transform_strict 的别名。

structure 参数是与 json_structure 返回形式相同的 JSON。可以修改 structure 参数以将 JSON 转换为所需的结构和类型。可以提取比 JSON 中存在的键/值对更少的对,也可以提取更多:缺失的键将变为 NULL

示例

CREATE TABLE example (j JSON);
INSERT INTO example VALUES
    ('{"family": "anatidae", "species": ["duck", "goose"], "coolness": 42.42}'),
    ('{"family": "canidae", "species": ["labrador", "bulldog"], "hair": true}');
SELECT json_transform(j, '{"family": "VARCHAR", "coolness": "DOUBLE"}') FROM example;
{'family': anatidae, 'coolness': 42.420000}
{'family': canidae, 'coolness': NULL}
SELECT json_transform(j, '{"family": "TINYINT", "coolness": "DECIMAL(4, 2)"}') FROM example;
{'family': NULL, 'coolness': 42.42}
{'family': NULL, 'coolness': NULL}
SELECT json_transform_strict(j, '{"family": "TINYINT", "coolness": "DOUBLE"}') FROM example;
Invalid Input Error: Failed to cast value: "anatidae"

JSON 表函数

DuckDB 实现了两个 JSON 表函数,它们接受一个 JSON 值并从中生成一个表。

函数 描述
json_each(json[ ,path] 遍历 json 并为顶级数组或对象中的每个元素返回一行。
json_tree(json[ ,path] 以深度优先的方式遍历 json,并为结构中的每个元素返回一行。

如果元素不是数组或对象,则返回元素本身。如果提供了可选的 path 参数,则遍历将从给定路径处的元素开始,而不是根元素。

结果表包含以下列

字段 类型 描述
key VARCHAR 元素相对于其父级的键
value JSON 元素的值
type VARCHAR 此元素的 json_type(函数)
atom JSON 此元素的 json_value(函数)
id UBIGINT 元素标识符,按解析顺序编号
parent UBIGINT 父元素的 id
fullkey VARCHAR 元素的 JSON 路径
path VARCHAR 父元素的 JSON 路径
json JSON(虚拟) json 参数
root TEXT(虚拟) path 参数
rowid BIGINT(虚拟) 行标识符

这些函数类似于 SQLite 中同名函数。请注意,因为 json_eachjson_tree 函数引用了同一 FROM 子句中的先前子查询,所以它们是横向连接(lateral joins)

示例

CREATE TABLE example (j JSON);
INSERT INTO example VALUES
    ('{"family": "anatidae", "species": ["duck", "goose"], "coolness": 42.42}'),
    ('{"family": "canidae", "species": ["labrador", "bulldog"], "hair": true}');
SELECT je.*, je.rowid
FROM example AS e, json_each(e.j) AS je;
key value type atom id parent fullkey path rowid
family "anatidae" VARCHAR "anatidae" 2 NULL $.family $ 0
species ["duck","goose"] ARRAY NULL 4 NULL $.species $ 1
coolness 42.42 DOUBLE 42.42 8 NULL $.coolness $ 2
family "canidae" VARCHAR "canidae" 2 NULL $.family $ 0
species ["labrador","bulldog"] ARRAY NULL 4 NULL $.species $ 1
hair true BOOLEAN true 8 NULL $.hair $ 2
SELECT je.*, je.rowid
FROM example AS e, json_each(e.j, '$.species') AS je;
key value type atom id parent fullkey path rowid
0 "duck" VARCHAR "duck" 5 NULL $.species[0] $.species 0
1 "goose" VARCHAR "goose" 6 NULL $.species[1] $.species 1
0 "labrador" VARCHAR "labrador" 5 NULL $.species[0] $.species 0
1 "bulldog" VARCHAR "bulldog" 6 NULL $.species[1] $.species 1
SELECT je.key, je.value, je.type, je.id, je.parent, je.fullkey, je.rowid
FROM example AS e, json_tree(e.j) AS je;
key value type id parent fullkey rowid
NULL {"family":"anatidae","species":["duck","goose"],"coolness":42.42} OBJECT 0 NULL $ 0
family "anatidae" VARCHAR 2 0 $.family 1
species ["duck","goose"] ARRAY 4 0 $.species 2
0 "duck" VARCHAR 5 4 $.species[0] 3
1 "goose" VARCHAR 6 4 $.species[1] 4
coolness 42.42 DOUBLE 8 0 $.coolness 5
NULL {"family":"canidae","species":["labrador","bulldog"],"hair":true} OBJECT 0 NULL $ 0
family "canidae" VARCHAR 2 0 $.family 1
species ["labrador","bulldog"] ARRAY 4 0 $.species 2
0 "labrador" VARCHAR 5 4 $.species[0] 3
1 "bulldog" VARCHAR 6 4 $.species[1] 4
hair true BOOLEAN 8 0 $.hair 5