From 488244311a5ee6dea772293711762783da79a60f Mon Sep 17 00:00:00 2001 From: Ti Chi Robot Date: Wed, 10 Jul 2024 17:09:04 +0800 Subject: [PATCH] Extend JSON function docs (#17784) (#17909) --- TOC.md | 10 +- data-type-default-values.md | 2 +- data-type-json.md | 27 +- .../aggregate-group-by-functions.md | 37 +- functions-and-operators/json-functions.md | 145 +++++-- .../json-functions-aggregate.md | 132 ++++++ .../json-functions/json-functions-create.md | 64 +++ .../json-functions/json-functions-modify.md | 346 ++++++++++++++++ .../json-functions/json-functions-return.md | 168 ++++++++ .../json-functions/json-functions-search.md | 360 +++++++++++++++++ .../json-functions/json-functions-utility.md | 73 ++++ .../json-functions/json-functions-validate.md | 382 ++++++++++++++++++ 12 files changed, 1674 insertions(+), 72 deletions(-) create mode 100644 functions-and-operators/json-functions/json-functions-aggregate.md create mode 100644 functions-and-operators/json-functions/json-functions-create.md create mode 100644 functions-and-operators/json-functions/json-functions-modify.md create mode 100644 functions-and-operators/json-functions/json-functions-return.md create mode 100644 functions-and-operators/json-functions/json-functions-search.md create mode 100644 functions-and-operators/json-functions/json-functions-utility.md create mode 100644 functions-and-operators/json-functions/json-functions-validate.md diff --git a/TOC.md b/TOC.md index 8144c11d9183..2655c9127bdb 100644 --- a/TOC.md +++ b/TOC.md @@ -908,7 +908,15 @@ - [加密和压缩函数](/functions-and-operators/encryption-and-compression-functions.md) - [锁函数](/functions-and-operators/locking-functions.md) - [信息函数](/functions-and-operators/information-functions.md) - - [JSON 函数](/functions-and-operators/json-functions.md) + - JSON 函数 + - [概览](/functions-and-operators/json-functions.md) + - [创建 JSON 的函数](/functions-and-operators/json-functions/json-functions-create.md) + - [搜索 JSON 的函数](/functions-and-operators/json-functions/json-functions-search.md) + - [修改 JSON 的函数](/functions-and-operators/json-functions/json-functions-modify.md) + - [返回 JSON 的函数](/functions-and-operators/json-functions/json-functions-return.md) + - [JSON 效用函数](/functions-and-operators/json-functions/json-functions-utility.md) + - [聚合 JSON 的函数](/functions-and-operators/json-functions/json-functions-aggregate.md) + - [验证 JSON 的函数](/functions-and-operators/json-functions/json-functions-validate.md) - [GROUP BY 聚合函数](/functions-and-operators/aggregate-group-by-functions.md) - [GROUP BY 修饰符](/functions-and-operators/group-by-modifier.md) - [窗口函数](/functions-and-operators/window-functions.md) diff --git a/data-type-default-values.md b/data-type-default-values.md index 035c2907e14a..e1e6d2ae551c 100644 --- a/data-type-default-values.md +++ b/data-type-default-values.md @@ -13,7 +13,7 @@ summary: 数据类型的默认值描述了列的默认值设置规则。默认 - 对于整数类型,可以使用 `NEXT VALUE FOR` 函数将序列的下一个值作为列的默认值,使用 [`RAND()`](/functions-and-operators/numeric-functions-and-operators.md) 函数生成随机浮点值作为列的默认值。 - 对于字符串类型,可以使用 [`UUID()`](/functions-and-operators/miscellaneous-functions.md) 函数生成[通用唯一标识符 (UUID)](/best-practices/uuid.md) 作为列的默认值。 - 对于二进制类型,可以使用 [`UUID_TO_BIN()`](/functions-and-operators/miscellaneous-functions.md) 函数将 UUID 转换为二进制格式后作为列的默认值。 -- 从 v8.0.0 开始,新增支持 [`BLOB`](/data-type-string.md#blob-类型)、[`TEXT`](/data-type-string.md#text-类型) 以及 [`JSON`](/data-type-json.md#json-类型) 这三种数据类型设置默认值,但仅支持使用表达式设置[默认值](#表达式默认值)。 +- 从 v8.0.0 开始,新增支持 [`BLOB`](/data-type-string.md#blob-类型)、[`TEXT`](/data-type-string.md#text-类型) 以及 [`JSON`](/data-type-json.md#json-数据类型) 这三种数据类型设置默认值,但仅支持使用表达式设置[默认值](#表达式默认值)。 如果一个列的定义中没有 `DEFAULT` 的设置。TiDB 按照如下的规则决定: diff --git a/data-type-json.md b/data-type-json.md index a5522f20615f..1196238adb1b 100644 --- a/data-type-json.md +++ b/data-type-json.md @@ -3,7 +3,7 @@ title: JSON 类型 summary: JSON 类型存储半结构化数据,使用 Binary 格式序列化,加快查询和解析速度。JSON 字段不能创建索引,但可以对 JSON 文档中的子字段创建索引。TiDB 仅支持下推部分 JSON 函数到 TiFlash,不建议使用 BR 恢复包含 JSON 列的数据到 v6.3.0 之前的 TiDB 集群。请勿同步非标准 JSON 类型的数据。MySQL 误标记二进制类型数据为 STRING 类型,TiDB 保持正确的二进制类型。ENUM 或 SET 数据类型转换为 JSON 时,TiDB 会检查格式正确性。TiDB 支持使用 ORDER BY 对 JSON Array 或 JSON Object 进行排序。在 INSERT JSON 列时,TiDB 会将值隐式转换为 JSON。 --- -# JSON 类型 +# JSON 数据类型 JSON 类型可以存储 JSON 这种半结构化的数据,相比于直接将 JSON 存储为字符串,它的好处在于: @@ -12,8 +12,6 @@ JSON 类型可以存储 JSON 这种半结构化的数据,相比于直接将 JS JSON 字段本身上,并不能创建索引,但是可以对 JSON 文档中的某个子字段创建索引。例如: -{{< copyable "sql" >}} - ```sql CREATE TABLE city ( id INT PRIMARY KEY, @@ -25,6 +23,29 @@ INSERT INTO city (id,detail) VALUES (1, '{"name": "Beijing", "population": 100}' SELECT id FROM city WHERE population >= 100; ``` +更多信息,请参考 [JSON 函数](/functions-and-operators/json-functions.md)和[生成列](/generated-columns.md)。 + +## JSON 值的类型 + +JSON 文档中的每个值都属于一种特定的数据类型。可以通过 [`JSON_TYPE`()](/functions-and-operators/json-functions/json-functions-return.md#json_type) 的输出结果查看。 + +| 类型 | 示例 | +|------------------|--------------------------------| +| ARRAY | `[]` | +| BIT | | +| BLOB | `0x616263` | +| BOOLEAN | `true` | +| DATE | `"2025-06-14"` | +| DATETIME | `"2025-06-14 09:05:10.000000"` | +| DOUBLE | `1.14` | +| INTEGER | `5` | +| NULL | `null` | +| OBJECT | `{}` | +| OPAQUE | | +| STRING | `"foobar"` | +| TIME | `"09:10:00.000000"` | +| UNSIGNED INTEGER | `9223372036854776000` | + ## 使用限制 - 目前 TiDB 仅支持下推部分 JSON 函数到 TiFlash。详情请参考 [TiFlash 支持下推的表达式](/tiflash/tiflash-supported-pushdown-calculations.md#支持下推的表达式)。 diff --git a/functions-and-operators/aggregate-group-by-functions.md b/functions-and-operators/aggregate-group-by-functions.md index 17f661d312e8..b8424cf2c785 100644 --- a/functions-and-operators/aggregate-group-by-functions.md +++ b/functions-and-operators/aggregate-group-by-functions.md @@ -24,7 +24,8 @@ TiDB 支持的 MySQL `GROUP BY` 聚合函数如下所示: | [`STD()`,`STDDEV()`,`STDDEV_POP`](https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html#function_std) | 返回总体标准差 | | [`VAR_SAMP()`](https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html#function_var-samp) | 返回采样方差 | | [`STDDEV_SAMP()`](https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html#function_stddev-samp) | 返回采样标准方差 | -| [`JSON_OBJECTAGG(key, value)`](https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html#function_json-objectagg) | 将结果集返回为单个含 (key, value) 键值对的 JSON object | +| [`JSON_ARRAYAGG()`](/functions-and-operators/json-functions/json-functions-aggregate.md#json_arrayagg) | 将结果集返回为单个 JSON 数组 | +| [`JSON_OBJECTAGG()`](/functions-and-operators/json-functions/json-functions-aggregate.md#json_objectagg) | 将结果集返回为单个含 (key, value) 键值对的 JSON 对象 | > **注意:** > @@ -41,23 +42,19 @@ TiDB 支持的 MySQL `GROUP BY` 聚合函数如下所示: 以下是一个计算第 50 百分位数的例子: - {{< copyable "sql" >}} - ```sql - drop table if exists t; - create table t(a int); - insert into t values(1), (2), (3); + DROP TABLE IF EXISTS t; + CREATE TABLE t(a INT); + INSERT INTO t VALUES(1), (2), (3); ``` - {{< copyable "sql" >}} - ```sql - select approx_percentile(a, 50) from t; + SELECT APPROX_PERCENTILE(a, 50) FROM t; ``` ```sql +--------------------------+ - | approx_percentile(a, 50) | + | APPROX_PERCENTILE(a, 50) | +--------------------------+ | 2 | +--------------------------+ @@ -74,16 +71,12 @@ TiDB 支持的 MySQL `GROUP BY` 聚合函数如下所示: TiDB 支持 SQL 模式 `ONLY_FULL_GROUP_BY`,当启用该模式时,TiDB 拒绝不明确的非聚合列的查询。例如,以下查询在启用 `ONLY_FULL_GROUP_BY` 时是不合规的,因为 `SELECT` 列表中的非聚合列 "b" 在 `GROUP BY` 语句中不显示: -{{< copyable "sql" >}} - ```sql drop table if exists t; create table t(a bigint, b bigint, c bigint); insert into t values(1, 2, 3), (2, 2, 3), (3, 2, 3); ``` -{{< copyable "sql" >}} - ```sql select a, b, sum(c) from t group by a; ``` @@ -99,8 +92,6 @@ select a, b, sum(c) from t group by a; 3 rows in set (0.01 sec) ``` -{{< copyable "sql" >}} - ```sql set sql_mode = 'ONLY_FULL_GROUP_BY'; ``` @@ -109,8 +100,6 @@ set sql_mode = 'ONLY_FULL_GROUP_BY'; Query OK, 0 rows affected (0.00 sec) ``` -{{< copyable "sql" >}} - ```sql select a, b, sum(c) from t group by a; ``` @@ -125,8 +114,6 @@ ERROR 1055 (42000): Expression #2 of SELECT list is not in GROUP BY clause and c TiDB 目前实现的 `ONLY_FULL_GROUP_BY` 没有 MySQL 5.7 严格。例如,假设我们执行以下查询,希望结果按 "c" 排序: -{{< copyable "sql" >}} - ```sql drop table if exists t; create table t(a bigint, b bigint, c bigint); @@ -145,8 +132,6 @@ select distinct a, b from t order by c; TiDB 中另一个标准 SQL 的扩展允许 `HAVING` 子句中的引用使用 `SELECT` 列表中的别名表达式。例如:以下查询返回在 `orders` 中只出现一次的 `name` 值 -{{< copyable "sql" >}} - ```sql select name, count(name) from orders group by name @@ -155,8 +140,6 @@ having count(name) = 1; 这个 TiDB 扩展允许在聚合列的 `HAVING` 子句中使用别名: -{{< copyable "sql" >}} - ```sql select name, count(name) as c from orders group by name @@ -165,8 +148,6 @@ having c = 1; 标准 SQL 只支持 `GROUP BY` 子句中的列表达式,以下语句不合规,因为 `FLOOR(value/100)` 是一个非列表达式: -{{< copyable "sql" >}} - ```sql select id, floor(value/100) from tbl_name @@ -177,8 +158,6 @@ TiDB 对标准 SQL 的扩展支持 `GROUP BY` 子句中非列表达式,认为 标准 SQL 也不支持 `GROUP BY` 子句中使用别名。TiDB 对标准 SQL 的扩展支持使用别名,查询的另一种写法如下: -{{< copyable "sql" >}} - ```sql select id, floor(value/100) as val from tbl_name @@ -187,4 +166,4 @@ group by id, val; ## 相关系统变量 -`group_concat_max_len` 变量设置 `GROUP_CONCAT()` 函数缓冲区的最大长度。 +[`group_concat_max_len`](/system-variables.md#group_concat_max_len) 变量设置 `GROUP_CONCAT()` 函数缓冲区的最大长度。 diff --git a/functions-and-operators/json-functions.md b/functions-and-operators/json-functions.md index 63d135534aa1..f1ef41cb3fbc 100644 --- a/functions-and-operators/json-functions.md +++ b/functions-and-operators/json-functions.md @@ -5,80 +5,145 @@ summary: TiDB 支持 MySQL 8.0 中提供的大部分 JSON 函数。 # JSON 函数 -TiDB 支持 MySQL 8.0 中提供的大部分 [JSON 函数](https://dev.mysql.com/doc/refman/8.0/en/json-functions.html)。 +你可以使用 JSON 函数处理 [JSON 类型](/data-type-json.md)的数据。 ## 创建 JSON 值的函数 | 函数 | 功能描述 | | ------------------------------------------------------------------ | ---------------------------------------------------------- | -| [JSON_ARRAY([val[, val] ...])](https://dev.mysql.com/doc/refman/8.0/en/json-creation-functions.html#function_json-array) | 根据一系列元素创建一个 JSON 文档 | -| [JSON_OBJECT(key, val[, key, val] ...)](https://dev.mysql.com/doc/refman/8.0/en/json-creation-functions.html#function_json-object) | 根据一系列 K/V 对创建一个 JSON 文档 | -| [JSON_QUOTE(string)](https://dev.mysql.com/doc/refman/8.0/en/json-creation-functions.html#function_json-quote) | 返回一个字符串,该字符串为带引号的 JSON 值 | +| [JSON_ARRAY()](/functions-and-operators/json-functions/json-functions-create.md#json_array) | 根据一系列元素(也可以为空)创建一个 JSON 数组 | +| [JSON_OBJECT()](/functions-and-operators/json-functions/json-functions-create.md#json_object) | 根据一系列包含 (key, value) 键值对的元素(也可以为空)创建一个 JSON 对象 | +| [JSON_QUOTE()](/functions-and-operators/json-functions/json-functions-create.md#json_quote) | 返回一个字符串,该字符串为带引号的 JSON 值 | ## 搜索 JSON 值的函数 | 函数 | 功能描述 | | ------------------------------------------------------------ | ------------------------------------------------------------ | -| [JSON_CONTAINS(target, candidate[, path])](https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#function_json-contains) | 通过返回 1 或 0 来表示目标 JSON 文档中是否包含给定的 candidate JSON 文档 | -| [JSON_CONTAINS_PATH(json_doc, one_or_all, path[, path] ...)](https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#function_json-contains-path) | 通过返回 0 或 1 来表示一个 JSON 文档在给定路径是否包含数据 | -| [JSON_EXTRACT(json_doc, path[, path] ...)](https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#function_json-extract) | 从 JSON 文档中解出某一路径对应的子文档 | -| [->](https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#operator_json-column-path) | 返回执行路径后面的 JSON 列的值;`JSON_EXTRACT(doc, path_literal)` 的别名 | -| [->>](https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#operator_json-inline-path) | 返回执行路径后面的 JSON 列的值和转义后的结果; `JSON_UNQUOTE(JSON_EXTRACT(doc, path_literal))` 的别名 | -| [JSON_KEYS(json_doc[, path])](https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#function_json-keys) | 返回从 JSON 对象的顶级值作为 JSON array 的键,如果给定了路径参数,则从选定路径中获取顶级键 | -| [JSON_SEARCH(json_doc, one_or_all, search_str[, escape_char[, path] ...])](https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#function_json-search) | 返回指定字符在 JSON 文档中的路径 | -| [value MEMBER OF(json_array)](https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#operator_member-of) | 如果传入值是 JSON array 中的一个元素,返回 1,否则返回 0 | -| [JSON_OVERLAPS(json_doc1, json_doc2)](https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#function_json-overlaps) | 表示两个 JSON 文档中是否包含公共部分。返回 1 表示两个 JSON 文档中包含公共部分,否则返回 0 | +| [JSON_CONTAINS()](/functions-and-operators/json-functions/json-functions-search.md#json_contains) | 通过返回 1 或 0 来表示目标 JSON 文档中是否包含给定的 candidate JSON 文档 | +| [JSON_CONTAINS_PATH()](/functions-and-operators/json-functions/json-functions-search.md#json_contains_path) | 通过返回 0 或 1 来表示一个 JSON 文档在给定路径是否包含数据 | +| [JSON_EXTRACT()](/functions-and-operators/json-functions/json-functions-search.md#json_extract) | 从 JSON 文档中解出某一路径对应的子文档 | +| [->](/functions-and-operators/json-functions/json-functions-search.md#-) | 返回执行路径后面的 JSON 列的值;`JSON_EXTRACT(doc, path_literal)` 的别名 | +| [->>](/functions-and-operators/json-functions/json-functions-search.md#--1) | 返回执行路径后面的 JSON 列的值和转义后的结果; `JSON_UNQUOTE(JSON_EXTRACT(doc, path_literal))` 的别名 | +| [JSON_KEYS()](/functions-and-operators/json-functions/json-functions-search.md#json_keys) | 返回从 JSON 对象的顶级值作为 JSON array 的键,如果给定了路径参数,则从选定路径中获取顶级键 | +| [JSON_SEARCH()](/functions-and-operators/json-functions/json-functions-search.md#json_search) | 在 JSON 文档中搜索字符串的一个或所有匹配项 | +| [MEMBER OF()](/functions-and-operators/json-functions/json-functions-search.md#member-of) | 如果传入值是 JSON array 中的一个元素,返回 1,否则返回 0 | +| [JSON_OVERLAPS()](/functions-and-operators/json-functions/json-functions-search.md#json_overlaps) | 表示两个 JSON 文档中是否包含公共部分。返回 1 表示两个 JSON 文档中包含公共部分,否则返回 0 | ## 修改 JSON 值的函数 | 函数 | 功能描述 | | --------------------------------- | ----------- | -| [JSON_APPEND(json_doc, path, value)](https://dev.mysql.com/doc/refman/8.0/en/json-modification-functions.html#function_json-append) | `JSON_ARRAY_APPEND` 的别名 | -| [JSON_ARRAY_APPEND(json_doc, path, val[, path, val] ...)](https://dev.mysql.com/doc/refman/8.0/en/json-modification-functions.html#function_json-array-append) | 将值添加到 JSON 文档指定数组的末尾,并返回添加结果 | -| [JSON_ARRAY_INSERT(json_doc, path, val[, path, val] ...)](https://dev.mysql.com/doc/refman/8.0/en/json-modification-functions.html#function_json-array-insert) | 将值插入到 JSON 文档中的指定位置并返回结果 | -| [JSON_INSERT(json_doc, path, val[, path, val] ...)](https://dev.mysql.com/doc/refman/8.0/en/json-modification-functions.html#function_json-insert) | 在 JSON 文档中在某一路径下插入子文档 | -| [JSON_MERGE_PATCH(json_doc, json_doc[, json_doc] ...)](https://dev.mysql.com/doc/refman/8.0/en/json-modification-functions.html#function_json-merge-patch) | 合并 JSON 文档 | -| [JSON_MERGE_PRESERVE(json_doc, json_doc[, json_doc] ...)](https://dev.mysql.com/doc/refman/8.0/en/json-modification-functions.html#function_json-merge-preserve) | 将两个或多个 JSON 文档合并成一个文档,并返回合并结果 | -| [JSON_MERGE(json_doc, json_doc[, json_doc] ...)](https://dev.mysql.com/doc/refman/8.0/en/json-modification-functions.html#function_json-merge) | 已废弃,`JSON_MERGE_PRESERVE` 的别名 | -| [JSON_REMOVE(json_doc, path[, path] ...)](https://dev.mysql.com/doc/refman/8.0/en/json-modification-functions.html#function_json-remove) | 移除 JSON 文档中某一路径下的子文档 | -| [JSON_REPLACE(json_doc, path, val[, path, val] ...)](https://dev.mysql.com/doc/refman/8.0/en/json-modification-functions.html#function_json-replace) | 替换 JSON 文档中的某一路径下的子文档 | -| [JSON_SET(json_doc, path, val[, path, val] ...)](https://dev.mysql.com/doc/refman/8.0/en/json-modification-functions.html#function_json-set) | 在 JSON 文档中为某一路径设置子文档 | -| [JSON_UNQUOTE(json_val)](https://dev.mysql.com/doc/refman/8.0/en/json-modification-functions.html#function_json-unquote) | 去掉 JSON 值外面的引号,返回结果为字符串 | +| [JSON_APPEND()](/functions-and-operators/json-functions/json-functions-modify.md#json_append) | `JSON_ARRAY_APPEND()` 的别名 | +| [JSON_ARRAY_APPEND()](/functions-and-operators/json-functions/json-functions-modify.md#json_array_append) | 将值添加到 JSON 文档指定数组的末尾,并返回添加结果 | +| [JSON_ARRAY_INSERT()](/functions-and-operators/json-functions/json-functions-modify.md#json_array_insert) | 将值插入到 JSON 文档中的指定位置并返回结果 | +| [JSON_INSERT()](/functions-and-operators/json-functions/json-functions-modify.md#json_insert) | 在 JSON 文档中在某一路径下插入子文档 | +| [JSON_MERGE_PATCH()](/functions-and-operators/json-functions/json-functions-modify.md#json_merge_patch) | 将两个或多个 JSON 文档合并为一个 JSON 文档,但不保留重复键的值 | +| [JSON_MERGE_PRESERVE()](/functions-and-operators/json-functions/json-functions-modify.md#json_merge_preserve) | 通过保留所有值的方式将两个或多个 JSON 文档合并成一个文档,并返回合并结果 | +| [JSON_MERGE()](/functions-and-operators/json-functions/json-functions-modify.md#json_merge) | 已废弃,`JSON_MERGE_PRESERVE()` 的别名 | +| [JSON_REMOVE()](/functions-and-operators/json-functions/json-functions-modify.md#json_remove) | 移除 JSON 文档中某一路径下的子文档,并返回结果 | +| [JSON_REPLACE()](/functions-and-operators/json-functions/json-functions-modify.md#json_replace)| 替换 JSON 文档中的某一路径下的子文档,并返回结果 | +| [JSON_SET()](/functions-and-operators/json-functions/json-functions-modify.md#json_set) | 在 JSON 文档中为某一路径设置子文档,并返回结果 | +| [JSON_UNQUOTE()](/functions-and-operators/json-functions/json-functions-modify.md#json_unquote) | 去掉 JSON 值外面的引号,返回结果为字符串 | ## 返回 JSON 值属性的函数 | 函数 | 功能描述 | | --------------------------------- | ----------- | -| [JSON_DEPTH(json_doc)](https://dev.mysql.com/doc/refman/8.0/en/json-attribute-functions.html#function_json-depth) | 返回 JSON 文档的最大深度 | -| [JSON_LENGTH(json_doc[, path])](https://dev.mysql.com/doc/refman/8.0/en/json-attribute-functions.html#function_json-length) | 返回 JSON 文档的长度;如果路径参数已定,则返回该路径下值的长度 | -| [JSON_TYPE(json_val)](https://dev.mysql.com/doc/refman/8.0/en/json-attribute-functions.html#function_json-type) | 检查某 JSON 文档内部内容的类型 | -| [JSON_VALID(json_doc)](https://dev.mysql.com/doc/refman/8.0/en/json-attribute-functions.html#function_json-valid) | 检查 JSON 文档内容是否有效;用于将列转换为 JSON 类型之前对该列进行检查 | +| [JSON_DEPTH()](/functions-and-operators/json-functions/json-functions-return.md#json_depth) | 返回 JSON 文档的最大深度 | +| [JSON_DEPTH()](/functions-and-operators/json-functions/json-functions-return.md#json_depth) | 返回 JSON 文档的长度;如果路径参数已定,则返回该路径下值的长度 | +| [JSON_TYPE()](/functions-and-operators/json-functions/json-functions-return.md#json_type) | 检查某 JSON 文档内部内容的类型 | +| [JSON_VALID()](/functions-and-operators/json-functions/json-functions-return.md#json_valid) | 检查 json\_doc 是否为有效的 JSON 文档 | ## 效用函数 | 函数 | 功能描述 | | --------------------------------- | ----------- | -| [JSON_PRETTY(json_doc)](https://dev.mysql.com/doc/refman/8.0/en/json-utility-functions.html#function_json-pretty) |格式化 JSON 文档 | -| [JSON_STORAGE_FREE(json_doc)](https://dev.mysql.com/doc/refman/8.0/en/json-utility-functions.html#function_json-storage-free) | 返回该 JSON 对象的存储空间中空闲的字节数。由于 TiDB 采用与 MySQL 完全不同的存储结构,本函数对合法的 JSON 值总是返回 0,主要用于兼容 MySQL 8.0 | -| [JSON_STORAGE_SIZE(json_doc)](https://dev.mysql.com/doc/refman/8.0/en/json-utility-functions.html#function_json-storage-size) | 返回存储 JSON 值所需的大致字节大小,由于不考虑 TiKV 压缩的字节大小,因此函数的输出与 MySQL 不严格兼容 | +| [JSON_PRETTY()](/functions-and-operators/json-functions/json-functions-utility.md#json_pretty) |格式化 JSON 文档 | +| [JSON_STORAGE_FREE()](/functions-and-operators/json-functions/json-functions-utility.md#json_storage_free) | 返回 JSON 值在原地更新操作后释放了多少存储空间,以二进制表示。 | +| [JSON_STORAGE_SIZE()](/functions-and-operators/json-functions/json-functions-utility.md#json_storage_size) | 返回存储 JSON 值所需的大致字节大小,由于不考虑 TiKV 压缩的字节大小,因此函数的输出与 MySQL 不严格兼容 | ## 聚合函数 | 函数 | 功能描述 | | --------------------------------- | ----------- | -| [JSON_ARRAYAGG(key)](https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html#function_json-arrayagg) | 提供指定列 key 的聚合 | -| [JSON_OBJECTAGG(key, value)](https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html#function_json-objectagg) | 提供给定两列键值对的聚合 | +| [JSON_ARRAYAGG()](/functions-and-operators/json-functions/json-functions-aggregate.md#json_arrayagg) | 提供指定列 key 的聚合 | +| [JSON_OBJECTAGG()](/functions-and-operators/json-functions/json-functions-aggregate.md#json_objectagg) | 提供给定两列键值对的聚合 | ## 验证函数 -| 函数 | 功能描述 | +| 函数 | 功能描述 | | --------------------------------- | ----------- | -| [JSON_SCHEMA_VALID(schema,json_doc)](https://dev.mysql.com/doc/refman/8.0/en/json-validation-functions.html#function_json-schema-valid) | 根据 schema 验证 JSON 文档,确保数据的完整性和一致性 | +| [JSON_SCHEMA_VALID()](/functions-and-operators/json-functions/json-functions-validate.md#json_schema_valid) | 根据 schema 验证 JSON 文档,确保数据的完整性和一致性 | + +## JSONPath + +许多 JSON 函数都使用 [JSONPath](https://www.rfc-editor.org/rfc/rfc9535.html) 来选择 JSON 文档中的特定内容。 + +| 符号 | 描述 | +| -------------- | ---------------------------- | +| `$` | 文件根目录 | +| `.` | 选择成员 | +| `[]` | 选择数组 | +| `*` | 通配符 | +| `**` | 路径通配符 | +| `[ to ]` | 选择数组范围 | + +下面以如下 JSON 文档为例,说明如何使用 JSONPath: + +```json +{ + "database": { + "name": "TiDB", + "features": [ + "distributed", + "scalable", + "relational", + "cloud native" + ], + "license": "Apache-2.0 license", + "versions": [ + { + "version": "v8.1.0", + "type": "lts", + "release_date": "2024-05-24" + }, + { + "version": "v8.0.0", + "type": "dmr", + "release_date": "2024-03-29" + } + ] + }, + "migration_tool": { + "name": "TiDB Data Migration", + "features": [ + "MySQL compatible", + "Shard merging" + ], + "license": "Apache-2.0 license" + } +} +``` + +| JSONPath | 描述 | [`JSON_EXTRACT()`](/functions-and-operators/json-functions/json-functions-search.md#json_extract) 示例| +|-------------------------------------- |-----------------------------------------|-------------------------------| +| `$` | 文档根目录 | 返回完整文档 | +| `$.database` | `database` 对象 | 返回以 `"database"` 开头的完整结构。不包括 `"migration_tool"` 和其下的结构。 | +| `$.database.name` | `database` 的 `name` 值 | `"TiDB"` | +| `$.database.features` | `database` 的 `features` 值 | `["distributed", "scalable", "relational", "cloud native"]` | +| `$.database.features[0]` | `database` 的 `features` 中的第一个值 | `"distributed"` | +| `$.database.features[2]` | `database` 的 `features` 中的第三个值 | `"relational"` | +| `$.database.versions[0].type` | `database` 的 `versions` 中第一个元素的 `type` 值 | `"lts"` | +| `$.database.versions[*].release_date` | `versions` 中所有的 `release_date` 值 | `["2024-05-24","2024-03-29"]` | +| `$.*.features` | 由所有的 `features` 值组成的两个数组 | `[["distributed", "scalable", "relational", "cloud native"], ["MySQL compatible", "Shard merging"]]` | +| `$**.version` | 包含用通配符匹配到所有的 `version` 值 | `["v8.1.0","v8.0.0"]` | +| `$.database.features[0 to 2]` | `database` 中指定范围的 `features` 值,`features[0 to 2]` 代表从 `features` 的第一个值到第三个值 | `["scalable","relational"]` | + +更多信息,请参考 [JSONPath -- XPath for JSON](https://www.ietf.org/archive/id/draft-goessner-dispatch-jsonpath-00.html)。 ## 另请参阅 -* [JSON Function Reference](https://dev.mysql.com/doc/refman/8.0/en/json-function-reference.html) -* [JSON Data Type](/data-type-json.md) +- [JSON 数据类型](/data-type-json.md) ## 不支持的函数 @@ -86,4 +151,8 @@ TiDB 支持 MySQL 8.0 中提供的大部分 [JSON 函数](https://dev.mysql.com/ - `JSON_TABLE()` - `JSON_VALUE()` -更多信息,请参考 [#14486](https://github.com/pingcap/tidb/issues/14486)。 \ No newline at end of file +更多信息,请参考 [#14486](https://github.com/pingcap/tidb/issues/14486)。 + +## MySQL 兼容性 + +- TiDB 支持 MySQL 8.0 中的大部分 [JSON 函数](https://dev.mysql.com/doc/refman/8.0/en/json-functions.html)。 diff --git a/functions-and-operators/json-functions/json-functions-aggregate.md b/functions-and-operators/json-functions/json-functions-aggregate.md new file mode 100644 index 000000000000..a419676cc506 --- /dev/null +++ b/functions-and-operators/json-functions/json-functions-aggregate.md @@ -0,0 +1,132 @@ +--- +title: 聚合 JSON 值的 JSON 函数 +summary: 了解聚合 JSON 值的 JSON 函数。 +--- + +# 聚合 JSON 值的 JSON 函数 + +本文档介绍 TiDB [聚合函数](/functions-and-operators/aggregate-group-by-functions.md) 中专门用于处理 JSON 的聚合函数。 + +## [JSON_ARRAYAGG()](https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html#function_json-arrayagg) + +`JSON_ARRAYAGG(key)` 函数可以根据给定的 `key` 将 `key` 值聚合到一个 JSON 数组中。`key` 通常为表达式或列名。 + +示例: + +在下面示例中,表格一列中的两条记录被聚合到一个 JSON 数组中。 + +```sql +SELECT JSON_ARRAYAGG(v) FROM (SELECT 1 'v' UNION SELECT 2); +``` + +``` ++------------------+ +| JSON_ARRAYAGG(v) | ++------------------+ +| [2, 1] | ++------------------+ +1 row in set (0.00 sec) +``` + +## [JSON_OBJECTAGG()](https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html#function_json-objectagg) + +`JSON_OBJECTAGG(key,value)` 函数可以根据给定的 `key` 和 `value` 将 `key` 值和 `value` 值聚合成一个 JSON 对象。`key` 和 `value` 通常为表达式或列名。 + +示例: + +首先创建两个表,并在其中添加几行数据。 + +```sql +CREATE TABLE plants ( + id INT PRIMARY KEY, + name VARCHAR(255) +); + +CREATE TABLE plant_attributes ( + id INT PRIMARY KEY AUTO_INCREMENT, + plant_id INT, attribute VARCHAR(255), + value VARCHAR(255), + FOREIGN KEY (plant_id) REFERENCES plants(id) +); + +INSERT INTO plants +VALUES +(1,"rose"), +(2,"tulip"), +(3,"orchid"); + +INSERT INTO plant_attributes(plant_id,attribute,value) +VALUES +(1,"color","red"), +(1,"thorns","yes"), +(2,"color","orange"), +(2,"thorns","no"), +(2,"grows_from","bulb"), +(3,"color","white"), +(3, "thorns","no"); +``` + +查看创建的表格的结果。 + +```sql +TABLE plants; +``` + +``` ++----+--------+ +| id | name | ++----+--------+ +| 1 | rose | +| 2 | tulip | +| 3 | orchid | ++----+--------+ +3 rows in set (0.00 sec) +``` + +```sql +TABLE plant_attributes; +``` + +``` ++----+----------+------------+--------+ +| id | plant_id | attribute | value | ++----+----------+------------+--------+ +| 1 | 1 | color | red | +| 2 | 1 | thorns | yes | +| 3 | 2 | color | orange | +| 4 | 2 | thorns | no | +| 5 | 2 | grows_from | bulb | +| 6 | 3 | color | white | +| 7 | 3 | thorns | no | ++----+----------+------------+--------+ +7 rows in set (0.00 sec) +``` + +你可以使用 `JSON_OBJECTAGG()` 函数来处理这些数据。在下面示例中,你可以看到每个 Group 中,多个键/值对被聚合成一个 JSON 对象。 + +```sql +SELECT + p.name, + JSON_OBJECTAGG(attribute,value) +FROM + plant_attributes pa + LEFT JOIN plants p ON pa.plant_id=p.id +GROUP BY + plant_id; +``` + +``` ++--------+-----------------------------------------------------------+ +| name | JSON_OBJECTAGG(attribute,value) | ++--------+-----------------------------------------------------------+ +| rose | {"color": "red", "thorns": "yes"} | +| orchid | {"color": "white", "thorns": "no"} | +| tulip | {"color": "orange", "grows_from": "bulb", "thorns": "no"} | ++--------+-----------------------------------------------------------+ +3 rows in set (0.00 sec) +``` + +## 另请参阅 + +- [JSON 函数](/functions-and-operators/json-functions.md) +- [JSON 数据类型](/data-type-json.md) \ No newline at end of file diff --git a/functions-and-operators/json-functions/json-functions-create.md b/functions-and-operators/json-functions/json-functions-create.md new file mode 100644 index 000000000000..fc8d3b3faa2f --- /dev/null +++ b/functions-and-operators/json-functions/json-functions-create.md @@ -0,0 +1,64 @@ +--- +title: 创建 JSON 值的 JSON 函数 +summary: 了解创建 JSON 值的 JSON 函数。 +--- + +# 创建 JSON 值的 JSON 函数 + +本文档介绍用于创建 JSON 值的 JSON 函数。 + +## [JSON_ARRAY()](https://dev.mysql.com/doc/refman/8.0/en/json-creation-functions.html#function_json-array) + +`JSON_ARRAY([val[, val] ...])` 函数接受一个值列表(可能为空)作为参数,并返回一个包含这些值的 JSON 数组。 + +```sql +SELECT JSON_ARRAY(1,2,3,4,5), JSON_ARRAY("foo", "bar"); +``` + +``` ++-----------------------+--------------------------+ +| JSON_ARRAY(1,2,3,4,5) | JSON_ARRAY("foo", "bar") | ++-----------------------+--------------------------+ +| [1, 2, 3, 4, 5] | ["foo", "bar"] | ++-----------------------+--------------------------+ +1 row in set (0.00 sec) +``` + +## [JSON_OBJECT()](https://dev.mysql.com/doc/refman/8.0/en/json-creation-functions.html#function_json-object) + +`JSON_OBJECT([key,val[,key,val]...])` 函数接受一个键值对列表(可能为空)作为参数,并返回一个包含这些键值对的 JSON 对象。 + +```sql +SELECT JSON_OBJECT("database", "TiDB", "distributed", TRUE); +``` + +``` ++------------------------------------------------------+ +| JSON_OBJECT("database", "TiDB", "distributed", TRUE) | ++------------------------------------------------------+ +| {"database": "TiDB", "distributed": true} | ++------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +## [JSON_QUOTE()](https://dev.mysql.com/doc/refman/8.0/en/json-creation-functions.html#function_json-quote) + +`JSON_QUOTE(str)` 函数将字符串返回为带引号的 JSON 值。 + +```sql +SELECT JSON_QUOTE('The name is "O\'Neil"'); +``` + +``` ++-------------------------------------+ +| JSON_QUOTE('The name is "O\'Neil"') | ++-------------------------------------+ +| "The name is \"O'Neil\"" | ++-------------------------------------+ +1 row in set (0.00 sec) +``` + +## 另请参阅 + +- [JSON 函数](/functions-and-operators/json-functions.md) +- [JSON 数据类型](/data-type-json.md) \ No newline at end of file diff --git a/functions-and-operators/json-functions/json-functions-modify.md b/functions-and-operators/json-functions/json-functions-modify.md new file mode 100644 index 000000000000..2f4f170c5759 --- /dev/null +++ b/functions-and-operators/json-functions/json-functions-modify.md @@ -0,0 +1,346 @@ +--- +title: 修改 JSON 值的 JSON 函数 +summary: 了解修改 JSON 值的 JSON 函数。 +--- + +# 修改 JSON 值的 JSON 函数 + +本文档介绍用于修改 JSON 值的 JSON 函数。 + +## [JSON_APPEND()](https://dev.mysql.com/doc/refman/8.0/en/json-modification-functions.html#function_json-append) + +该函数为 [`JSON_ARRAY_APPEND()`](#json_array_append) 的别名。 + +## [JSON_ARRAY_APPEND()](https://dev.mysql.com/doc/refman/8.0/en/json-modification-functions.html#function_json-array-append) + +`JSON_ARRAY_APPEND(json_array, path, value [,path, value] ...)` 函数将 `value` 插入 `path` 中指定的 `json_array` 数组的末尾,并返回结果。 + +该函数可接受成对的 `path` 和 `value` 参数。 + +示例: + +下面示例向 JSON 文档根目录的数组添加了一个元素。 + +```sql +SELECT JSON_ARRAY_APPEND('["Car", "Boat", "Train"]', '$', "Airplane") AS "Transport options"; +``` + +``` ++--------------------------------------+ +| Transport options | ++--------------------------------------+ +| ["Car", "Boat", "Train", "Airplane"] | ++--------------------------------------+ +1 row in set (0.00 sec) +``` + +下面的示例向指定路径下的数组添加了一个元素。 + +```sql +SELECT JSON_ARRAY_APPEND('{"transport_options": ["Car", "Boat", "Train"]}', '$.transport_options', "Airplane") AS "Transport options"; +``` + +``` ++-------------------------------------------------------------+ +| Transport options | ++-------------------------------------------------------------+ +| {"transport_options": ["Car", "Boat", "Train", "Airplane"]} | ++-------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +## [JSON_ARRAY_INSERT()](https://dev.mysql.com/doc/refman/8.0/en/json-modification-functions.html#function_json-array-insert) + +`JSON_ARRAY_INSERT(json_array, path, value [,path, value] ...)` 函数将 `value` 插入 `path` 中 `json_array` 的指定位置,并返回结果。 + +该函数可接受成对的 `path` 和 `value` 参数。 + +示例: + +下面的示例在数组中索引为 0 的位置插入了一个值。 + +```sql +SELECT JSON_ARRAY_INSERT('["Car", "Boat", "Train"]', '$[0]', "Airplane") AS "Transport options"; +``` + +``` ++--------------------------------------+ +| Transport options | ++--------------------------------------+ +| ["Airplane", "Car", "Boat", "Train"] | ++--------------------------------------+ +1 row in set (0.01 sec) +``` + +下面的示例在数组中索引为 1 的位置插入了一个值。 + +```sql +SELECT JSON_ARRAY_INSERT('["Car", "Boat", "Train"]', '$[1]', "Airplane") AS "Transport options"; +``` + +``` ++--------------------------------------+ +| Transport options | ++--------------------------------------+ +| ["Car", "Airplane", "Boat", "Train"] | ++--------------------------------------+ +1 row in set (0.00 sec) +``` + +## [JSON_INSERT()](https://dev.mysql.com/doc/refman/8.0/en/json-modification-functions.html#function_json-insert) + +`JSON_INSERT(json_doc,path,value[,path,value] ...)` 函数将一个或多个值插入到 JSON 文档,并返回结果。 + +该函数可接受成对的 `path` 和 `value` 参数。 + +```sql +SELECT JSON_INSERT( + '{"language": ["Go", "Rust", "C++"]}', + '$.architecture', 'riscv', + '$.os', JSON_ARRAY("linux","freebsd") +) AS "Demo"; +``` + +``` ++------------------------------------------------------------------------------------------+ +| Demo | ++------------------------------------------------------------------------------------------+ +| {"architecture": "riscv", "language": ["Go", "Rust", "C++"], "os": ["linux", "freebsd"]} | ++------------------------------------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +请注意,该函数不会覆盖现有属性。例如,以下语句看起来会覆盖 `"a"` 属性现有的值,但实际上并不会。 + +```sql +SELECT JSON_INSERT('{"a": 61, "b": 62}', '$.a', 41, '$.c', 63); +``` + +``` ++---------------------------------------------------------+ +| JSON_INSERT('{"a": 61, "b": 62}', '$.a', 41, '$.c', 63) | ++---------------------------------------------------------+ +| {"a": 61, "b": 62, "c": 63} | ++---------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +## [JSON_MERGE_PATCH()](https://dev.mysql.com/doc/refman/8.0/en/json-modification-functions.html#function_json-merge-patch) + +`JSON_MERGE_PATCH(json_doc, json_doc [,json_doc] ...)` 将两个或多个 JSON 文档合并为一个 JSON 文档,但不保留重复键的值。如果其中某些 `json_doc` 参数包含重复的键,合并后的结果只保留后面指定的那个 `json_doc` 参数中的值。 + +示例: + +在下面的示例中,可以看到合并结果中 `a` 的值被第二个参数覆盖,而 `c` 被添加为一个新属性。 + +```sql +SELECT JSON_MERGE_PATCH( + '{"a": 1, "b": 2}', + '{"a": 100}', + '{"c": 300}' +); +``` + +``` ++-----------------------------------------------------------------+ +| JSON_MERGE_PATCH('{"a": 1, "b": 2}','{"a": 100}', '{"c": 300}') | ++-----------------------------------------------------------------+ +| {"a": 100, "b": 2, "c": 300} | ++-----------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +## [JSON_MERGE_PRESERVE()](https://dev.mysql.com/doc/refman/8.0/en/json-modification-functions.html#function_json-merge-preserve) + +`JSON_MERGE_PRESERVE(json_doc, json_doc [,json_doc] ...)` 函数通过保留所有键值的方式合并两个或多个 JSON 文档,并返回合并结果。 + +示例: + +在下面的示例中,可以看到第二个参数的值被附加到了 `a` 中,并且 `c` 被添加为一个新属性。 + +```sql +SELECT JSON_MERGE_PRESERVE('{"a": 1, "b": 2}','{"a": 100}', '{"c": 300}'); +``` + +``` ++--------------------------------------------------------------------+ +| JSON_MERGE_PRESERVE('{"a": 1, "b": 2}','{"a": 100}', '{"c": 300}') | ++--------------------------------------------------------------------+ +| {"a": [1, 100], "b": 2, "c": 300} | ++--------------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +## [JSON_MERGE()](https://dev.mysql.com/doc/refman/8.0/en/json-modification-functions.html#function_json-merge) + +> **警告:** +> +> 该函数已废弃。 + +该函数为 [`JSON_MERGE_PRESERVE()`](#json_merge_preserve) 已废弃的别名。 + +## [JSON_REMOVE()](https://dev.mysql.com/doc/refman/8.0/en/json-modification-functions.html#function_json-remove) + +`JSON_REMOVE(json_doc,path [,path] ...)` 函数从 JSON 文档中删除指定 `path` 的数据并返回结果。 + +示例: + +下面示例删除了 JSON 文档中的 `b` 属性。 + +```sql +SELECT JSON_REMOVE('{"a": 61, "b": 62, "c": 63}','$.b'); +``` + +``` ++--------------------------------------------------+ +| JSON_REMOVE('{"a": 61, "b": 62, "c": 63}','$.b') | ++--------------------------------------------------+ +| {"a": 61, "c": 63} | ++--------------------------------------------------+ +1 row in set (0.00 sec) +``` + +下面示例删除了 JSON 文档中的 `b` 和 `c` 属性。 + +```sql +SELECT JSON_REMOVE('{"a": 61, "b": 62, "c": 63}','$.b','$.c'); +``` + +``` ++--------------------------------------------------------+ +| JSON_REMOVE('{"a": 61, "b": 62, "c": 63}','$.b','$.c') | ++--------------------------------------------------------+ +| {"a": 61} | ++--------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +## [JSON_REPLACE()](https://dev.mysql.com/doc/refman/8.0/en/json-modification-functions.html#function_json-replace) + +`JSON_REPLACE(json_doc,path,value[,path,value]...)` 函数替换 JSON 文档中的现有的值并返回结果。如果指定的路径不存在,该路径对应的值不会添加到结果中。 + +该函数可接受成对的 `path` 和 `value` 参数。 + +示例: + +下面的示例将 `$.b` 的值从 `62` 替换为 `42`。 + +```sql +SELECT JSON_REPLACE('{"a": 41, "b": 62}','$.b',42); +``` + +``` ++---------------------------------------------+ +| JSON_REPLACE('{"a": 41, "b": 62}','$.b',42) | ++---------------------------------------------+ +| {"a": 41, "b": 42} | ++---------------------------------------------+ +1 row in set (0.00 sec) +``` + +下面的示例将 `$.b` 的值从 `62` 替换为 `42`。此外,该语句试图用 `43` 替换 `$.c` 中的值,但不会替换成功,因为在 `{"a": 41, "b": 62}` 中 `$.c` 路径不存在。 + +```sql +SELECT JSON_REPLACE('{"a": 41, "b": 62}','$.b',42,'$.c',43); +``` + +``` ++------------------------------------------------------+ +| JSON_REPLACE('{"a": 41, "b": 62}','$.b',42,'$.c',43) | ++------------------------------------------------------+ +| {"a": 41, "b": 42} | ++------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +## [JSON_SET()](https://dev.mysql.com/doc/refman/8.0/en/json-modification-functions.html#function_json-set) + +`JSON_SET(json_doc,path,value[,path,value] ...)` 函数在 JSON 文档中插入或更新数据,并返回结果。 + +该函数可接受成对的 `path` 和 `value` 参数。 + +示例: + +下面的示例将 `$.version` 从 `1.1` 更新为 `1.2`。 + +```sql +SELECT JSON_SET('{"version": 1.1, "name": "example"}','$.version',1.2); +``` + +``` ++-----------------------------------------------------------------+ +| JSON_SET('{"version": 1.1, "name": "example"}','$.version',1.2) | ++-----------------------------------------------------------------+ +| {"name": "example", "version": 1.2} | ++-----------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +下面的示例将 `$.version` 从 `1.1` 更新为 `1.2`,并将之前不存在的 `$.branch` 更新为 `main`。 + +```sql +SELECT JSON_SET('{"version": 1.1, "name": "example"}','$.version',1.2,'$.branch', "main"); +``` + +``` ++------------------------------------------------------------------------------------+ +| JSON_SET('{"version": 1.1, "name": "example"}','$.version',1.2,'$.branch', "main") | ++------------------------------------------------------------------------------------+ +| {"branch": "main", "name": "example", "version": 1.2} | ++------------------------------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +## [JSON_UNQUOTE()](https://dev.mysql.com/doc/refman/8.0/en/json-modification-functions.html#function_json-unquote) + +`JSON_UNQUOTE(json)` 函数去掉 JSON 值的引号,并以字符串形式返回结果。该函数与 [`JSON_QUOTE()`](/functions-and-operators/json-functions/json-functions-create.md#json_quote) 函数作用相反。 + +示例: + +下面示例将 `"foo"` 去掉引号,变成 `foo`。 + +```sql +SELECT JSON_UNQUOTE('"foo"'); +``` + +``` ++-----------------------+ +| JSON_UNQUOTE('"foo"') | ++-----------------------+ +| foo | ++-----------------------+ +1 row in set (0.00 sec) +``` + +该函数通常与 [`JSON_EXTRACT()`](/functions-and-operators/json-functions/json-functions-search.md#json_extract) 一起使用。在下面的示例中,第一个示例提取带引号的 JSON 值,第二个示例通过将两个函数结合使用去掉提取值的引号。请注意,你可以使用 [`->>`](/functions-and-operators/json-functions/json-functions-search.md#--1) 操作符来代替 `JSON_UNQUOTE(JSON_EXTRACT(...))`。 + +```sql +SELECT JSON_EXTRACT('{"database": "TiDB"}', '$.database'); +``` + +``` ++----------------------------------------------------+ +| JSON_EXTRACT('{"database": "TiDB"}', '$.database') | ++----------------------------------------------------+ +| "TiDB" | ++----------------------------------------------------+ +1 row in set (0.00 sec) +``` + +```sql +SELECT JSON_UNQUOTE(JSON_EXTRACT('{"database": "TiDB"}', '$.database')); +``` + +``` ++------------------------------------------------------------------+ +| JSON_UNQUOTE(JSON_EXTRACT('{"database": "TiDB"}', '$.database')) | ++------------------------------------------------------------------+ +| TiDB | ++------------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +## 另请参阅 + +- [JSON 函数](/functions-and-operators/json-functions.md) +- [JSON 数据类型](/data-type-json.md) \ No newline at end of file diff --git a/functions-and-operators/json-functions/json-functions-return.md b/functions-and-operators/json-functions/json-functions-return.md new file mode 100644 index 000000000000..9adb89071d6b --- /dev/null +++ b/functions-and-operators/json-functions/json-functions-return.md @@ -0,0 +1,168 @@ +--- +title: 返回 JSON 值的 JSON 函数 +summary: 了解返回 JSON 值的 JSON 函数。 +--- + +# 返回 JSON 值的 JSON 函数 + +本文介绍返回 JSON 值的 JSON 函数。 + +## [JSON_DEPTH()](https://dev.mysql.com/doc/refman/8.0/en/json-attribute-functions.html#function_json-depth) + +`JSON_DEPTH(json_doc)` 函数返回 JSON 文档的最大深度。 + +示例: + +在下面的示例中,`JSON_DEPTH()` 返回 `3`,因为有三层: + +- root (`$`) +- weather (`$.weather`) +- weather current (`$.weather.sunny`) + +```sql +SELECT JSON_DEPTH('{"weather": {"current": "sunny"}}'); +``` + +``` ++-------------------------------------------------+ +| JSON_DEPTH('{"weather": {"current": "sunny"}}') | ++-------------------------------------------------+ +| 3 | ++-------------------------------------------------+ +1 row in set (0.00 sec) +``` + +## [JSON_LENGTH()](https://dev.mysql.com/doc/refman/8.0/en/json-attribute-functions.html#function_json-length) + +`JSON_LENGTH(json_doc [,path])` 函数返回 JSON 文档的长度。如果指定了 `path` 参数,则返回路径中的值的长度。 + +示例: + +在下面的示例中,返回值是 `1`,因为文档根目录下仅有一个元素 `weather`。 + +```sql +SELECT JSON_LENGTH('{"weather": {"current": "sunny", "tomorrow": "cloudy"}}','$'); +``` + +``` ++----------------------------------------------------------------------------+ +| JSON_LENGTH('{"weather": {"current": "sunny", "tomorrow": "cloudy"}}','$') | ++----------------------------------------------------------------------------+ +| 1 | ++----------------------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +在下面的示例中,`$.weather` 包含两个元素 `current` 和`tomorrow`,因此返回值为 `2`。 + +```sql +SELECT JSON_LENGTH('{"weather": {"current": "sunny", "tomorrow": "cloudy"}}','$.weather'); +``` + +``` ++------------------------------------------------------------------------------------+ +| JSON_LENGTH('{"weather": {"current": "sunny", "tomorrow": "cloudy"}}','$.weather') | ++------------------------------------------------------------------------------------+ +| 2 | ++------------------------------------------------------------------------------------+ +1 row in set (0.01 sec) +``` + +## [JSON_TYPE()](https://dev.mysql.com/doc/refman/8.0/en/json-attribute-functions.html#function_json-type) + +`JSON_TYPE(json_val)` 函数返回一个字符串,表示 [JSON 值的类型](/data-type-json.md#json-值的类型)。 + +示例: + +```sql +WITH demo AS ( + SELECT 'null' AS 'v' + UNION SELECT '"foobar"' + UNION SELECT 'true' + UNION SELECT '5' + UNION SELECT '1.14' + UNION SELECT '[]' + UNION SELECT '{}' + UNION SELECT POW(2,63) +) +SELECT v, JSON_TYPE(v) FROM demo ORDER BY 2; +``` + +``` ++----------------------+--------------+ +| v | JSON_TYPE(v) | ++----------------------+--------------+ +| [] | ARRAY | +| true | BOOLEAN | +| 1.14 | DOUBLE | +| 9.223372036854776e18 | DOUBLE | +| 5 | INTEGER | +| null | NULL | +| {} | OBJECT | +| "foobar" | STRING | ++----------------------+--------------+ +8 rows in set (0.00 sec) +``` + +请注意,看起来相同的值可能属于不同的类型,如下例所示。 + +```sql +SELECT '"2025-06-14"',CAST(CAST('2025-06-14' AS date) AS json); +``` + +``` ++--------------+------------------------------------------+ +| "2025-06-14" | CAST(CAST('2025-06-14' AS date) AS json) | ++--------------+------------------------------------------+ +| "2025-06-14" | "2025-06-14" | ++--------------+------------------------------------------+ +1 row in set (0.00 sec) +``` + +```sql +SELECT JSON_TYPE('"2025-06-14"'),JSON_TYPE(CAST(CAST('2025-06-14' AS date) AS json)); +``` + +``` ++---------------------------+-----------------------------------------------------+ +| JSON_TYPE('"2025-06-14"') | JSON_TYPE(CAST(CAST('2025-06-14' AS date) AS json)) | ++---------------------------+-----------------------------------------------------+ +| STRING | DATE | ++---------------------------+-----------------------------------------------------+ +1 row in set (0.00 sec) +``` + +## [JSON_VALID()](https://dev.mysql.com/doc/refman/8.0/en/json-attribute-functions.html#function_json-valid) + +`JSON_VALID(str)` 函数检查输入的参数是否为有效的 JSON 格式。该函数对于在将列转换为 `JSON` 类型之前进行检查非常有用。 + +```sql +SELECT JSON_VALID('{"foo"="bar"}'); +``` + +``` ++-----------------------------+ +| JSON_VALID('{"foo"="bar"}') | ++-----------------------------+ +| 0 | ++-----------------------------+ +1 row in set (0.01 sec) +``` + +```sql +SELECT JSON_VALID('{"foo": "bar"}'); +``` + +``` ++------------------------------+ +| JSON_VALID('{"foo": "bar"}') | ++------------------------------+ +| 1 | ++------------------------------+ +1 row in set (0.01 sec) +``` + +## 另请参考 + +- [JSON 函数](/functions-and-operators/json-functions.md) +- [JSON 数据类型](/data-type-json.md) diff --git a/functions-and-operators/json-functions/json-functions-search.md b/functions-and-operators/json-functions/json-functions-search.md new file mode 100644 index 000000000000..ee9d4e626b61 --- /dev/null +++ b/functions-and-operators/json-functions/json-functions-search.md @@ -0,0 +1,360 @@ +--- +title: 搜索 JSON 值的 JSON 函数 +summary: 了解搜索 JSON 值的 JSON 函数。 +--- + +# 搜索 JSON 值的 JSON 函数 + +本文档介绍用于搜索 JSON 值的 JSON 函数。 + +## [JSON_CONTAINS()](https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#function_json-contains) + +通过返回 `1` 或 `0`,`JSON_CONTAINS(json_doc, candidate [,path])` 函数用于确认指定的 JSON 文档 `candidate` 是否包含在目标 JSON 文档中。 + +示例: + +下面示例中,`a` 包含在了目标文档中。 + +```sql +SELECT JSON_CONTAINS('["a","b","c"]','"a"'); +``` + +``` ++--------------------------------------+ +| JSON_CONTAINS('["a","b","c"]','"a"') | ++--------------------------------------+ +| 1 | ++--------------------------------------+ +1 row in set (0.00 sec) +``` + +下面示例中,`a` 没有包含在目标文档中。 + +```sql +SELECT JSON_CONTAINS('["a","b","c"]','"e"'); +``` + +``` ++--------------------------------------+ +| JSON_CONTAINS('["a","b","c"]','"e"') | ++--------------------------------------+ +| 0 | ++--------------------------------------+ +1 row in set (0.00 sec) +``` + +下面示例中,`{"foo": "bar"}` 包含在了目标文档中。 + +```sql +SELECT JSON_CONTAINS('{"foo": "bar", "aaa": 5}','{"foo": "bar"}'); +``` + +``` ++------------------------------------------------------------+ +| JSON_CONTAINS('{"foo": "bar", "aaa": 5}','{"foo": "bar"}') | ++------------------------------------------------------------+ +| 1 | ++------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +下面示例中,`"bar"` 没有包含在目标文档的根目录中。 + +```sql +SELECT JSON_CONTAINS('{"foo": "bar", "aaa": 5}','"bar"'); +``` + +``` ++---------------------------------------------------+ +| JSON_CONTAINS('{"foo": "bar", "aaa": 5}','"bar"') | ++---------------------------------------------------+ +| 0 | ++---------------------------------------------------+ +1 row in set (0.00 sec) +``` + +下面示例中,`"bar"` 包含在了目标文档的 `$.foo` 属性中。 + +```sql +SELECT JSON_CONTAINS('{"foo": "bar", "aaa": 5}','"bar"', '$.foo'); +``` + +``` ++------------------------------------------------------------+ +| JSON_CONTAINS('{"foo": "bar", "aaa": 5}','"bar"', '$.foo') | ++------------------------------------------------------------+ +| 1 | ++------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +## [JSON_CONTAINS_PATH()](https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#function_json-contains-path) + +`JSON_CONTAINS_PATH(json_doc,all_or_one,path [,path, ...])` 函数返回 `0` 或 `1`,表示 JSON 文档是否包含指定路径下的数据。 + +示例: + +下面的示例文档中包含 `$.foo`。 + +```sql +SELECT JSON_CONTAINS_PATH('{"foo": "bar", "aaa": 5}','all','$.foo'); +``` + +``` ++--------------------------------------------------------------+ +| JSON_CONTAINS_PATH('{"foo": "bar", "aaa": 5}','all','$.foo') | ++--------------------------------------------------------------+ +| 1 | ++--------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +下面的示例文档中没有包含 `$.bar`。 + +```sql +SELECT JSON_CONTAINS_PATH('{"foo": "bar", "aaa": 5}','all','$.bar'); +``` + +``` ++--------------------------------------------------------------+ +| JSON_CONTAINS_PATH('{"foo": "bar", "aaa": 5}','all','$.bar') | ++--------------------------------------------------------------+ +| 0 | ++--------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +下面的示例文档中包含了 `$.foo` 和 `$.aaa`。 + +```sql +SELECT JSON_CONTAINS_PATH('{"foo": "bar", "aaa": 5}','all','$.foo', '$.aaa'); +``` + +``` ++-----------------------------------------------------------------------+ +| JSON_CONTAINS_PATH('{"foo": "bar", "aaa": 5}','all','$.foo', '$.aaa') | ++-----------------------------------------------------------------------+ +| 1 | ++-----------------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +## [JSON_EXTRACT()](https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#function_json-extract) + +`JSON_EXTRACT(json_doc, path[, path] ...)` 函数从 JSON 文档中提取与 `path` 参数匹配的数据。 + +```sql +SELECT JSON_EXTRACT('{"foo": "bar", "aaa": 5}', '$.foo'); +``` + +``` ++---------------------------------------------------+ +| JSON_EXTRACT('{"foo": "bar", "aaa": 5}', '$.foo') | ++---------------------------------------------------+ +| "bar" | ++---------------------------------------------------+ +1 row in set (0.00 sec) +``` + +## [->](https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#operator_json-column-path) + +`column->path` 函数返回 `column` 中与 `path` 参数匹配的数据。该函数是 [`JSON_EXTRACT()`](#json_extract) 的别名。 + +```sql +SELECT + j->'$.foo', + JSON_EXTRACT(j, '$.foo') +FROM ( + SELECT + '{"foo": "bar", "aaa": 5}' AS j + ) AS tbl; +``` + +``` ++------------+--------------------------+ +| j->'$.foo' | JSON_EXTRACT(j, '$.foo') | ++------------+--------------------------+ +| "bar" | "bar" | ++------------+--------------------------+ +1 row in set (0.00 sec) +``` + +## [->>](https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#operator_json-inline-path) + +`column->>path` 函数去掉 `column` 中与 `path` 参数匹配的数据的引号。它是 `JSON_UNQUOTE(JSON_EXTRACT(doc,path_literal))` 的别名。 + +```sql +SELECT + j->'$.foo', + JSON_EXTRACT(j, '$.foo') + j->>'$.foo', + JSON_UNQUOTE(JSON_EXTRACT(j, '$.foo')) +FROM ( + SELECT + '{"foo": "bar", "aaa": 5}' AS j + ) AS tbl; +``` + +``` ++------------+--------------------------+-------------+----------------------------------------+ +| j->'$.foo' | JSON_EXTRACT(j, '$.foo') | j->>'$.foo' | JSON_UNQUOTE(JSON_EXTRACT(j, '$.foo')) | ++------------+--------------------------+-------------+----------------------------------------+ +| "bar" | "bar" | bar | bar | ++------------+--------------------------+-------------+----------------------------------------+ +1 row in set (0.00 sec) +``` + +## [JSON_KEYS()](https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#function_json-keys) + +`JSON_KEYS(json_doc [,path])` 函数以 JSON 数组的形式返回 JSON 对象的顶层键 (key)。如果指定了 `path` 参数,则返回所选路径的顶层键 (key)。 + +示例: + +下面示例返回了 JSON 文档中的两个顶层键。 + +```sql +SELECT JSON_KEYS('{"name": {"first": "John", "last": "Doe"}, "type": "Person"}'); +``` + +``` ++---------------------------------------------------------------------------+ +| JSON_KEYS('{"name": {"first": "John", "last": "Doe"}, "type": "Person"}') | ++---------------------------------------------------------------------------+ +| ["name", "type"] | ++---------------------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +下面示例返回了 JSON 文档的 `$.name` 路径中的顶层键。 + +```sql +SELECT JSON_KEYS('{"name": {"first": "John", "last": "Doe"}, "type": "Person"}', '$.name'); +``` + +``` ++-------------------------------------------------------------------------------------+ +| JSON_KEYS('{"name": {"first": "John", "last": "Doe"}, "type": "Person"}', '$.name') | ++-------------------------------------------------------------------------------------+ +| ["first", "last"] | ++-------------------------------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +## [JSON_SEARCH()](https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#function_json-search) + +`JSON_SEARCH(json_doc,one_or_all,str)` 函数会在 JSON 文档中搜索与字符串匹配的一个或所有的匹配项。 + +示例: + +在下面的示例中,搜索 `cc` 的第一个结果,它在 `a` 数组中索引为 2 的位置。 + +```sql +SELECT JSON_SEARCH('{"a": ["aa", "bb", "cc"], "b": ["cc", "dd"]}','one','cc'); +``` + +``` ++------------------------------------------------------------------------+ +| JSON_SEARCH('{"a": ["aa", "bb", "cc"], "b": ["cc", "dd"]}','one','cc') | ++------------------------------------------------------------------------+ +| "$.a[2]" | ++------------------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +现在执行和上个示例类似的操作,但将 `one_or_all` 设置为 `all`。这将获取全部搜索结果,而不仅仅是第一个结果。 + +```json +SELECT JSON_SEARCH('{"a": ["aa", "bb", "cc"], "b": ["cc", "dd"]}','all','cc'); +``` + +``` ++------------------------------------------------------------------------+ +| JSON_SEARCH('{"a": ["aa", "bb", "cc"], "b": ["cc", "dd"]}','all','cc') | ++------------------------------------------------------------------------+ +| ["$.a[2]", "$.b[0]"] | ++------------------------------------------------------------------------+ +1 row in set (0.01 sec) +``` + +## [MEMBER OF()](https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#operator_member-of) + +`str MEMBER OF (json_array)` 函数测试传入的 `str` 值是否是 `json_array` 的元素,如果是则返回 `1`,否则返回 `0`。如果任一参数为 `NULL`,则返回 `NULL`。 + +``` +SELECT '🍍' MEMBER OF ('["🍍","🥥","🥭"]') AS 'Contains pineapple'; +``` + +``` ++--------------------+ +| Contains pineapple | ++--------------------+ +| 1 | ++--------------------+ +1 row in set (0.00 sec) + +``` + +## [JSON_OVERLAPS()](https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#function_json-overlaps) + +`JSON_OVERLAPS(json_doc, json_doc)` 函数检查两个 JSON 文档是否有重叠部分。如果有重叠,则返回 `1`,如果没有重叠,则返回 `0`。如果任一参数为 `NULL`,则返回 `NULL`。 + +示例: + +下面的示例显示没有重叠,因为数组值的元素数量不一样。 + +```sql +SELECT JSON_OVERLAPS( + '{"languages": ["Go","Rust","C#"]}', + '{"languages": ["Go","Rust"]}' +) AS 'Overlaps'; +``` + +``` ++----------+ +| Overlaps | ++----------+ +| 0 | ++----------+ +1 row in set (0.00 sec) +``` + +下面的示例显示两个 JSON 文档重叠,因为它们完全相同。 + +```sql +SELECT JSON_OVERLAPS( + '{"languages": ["Go","Rust","C#"]}', + '{"languages": ["Go","Rust","C#"]}' +) AS 'Overlaps'; +``` + +``` ++----------+ +| Overlaps | ++----------+ +| 1 | ++----------+ +1 row in set (0.00 sec) +``` + +下面的示例显示存在重叠,而第二个文件有一个额外的属性。 + +```sql +SELECT JSON_OVERLAPS( + '{"languages": ["Go","Rust","C#"]}', + '{"languages": ["Go","Rust","C#"], "arch": ["arm64"]}' +) AS 'Overlaps'; +``` + +``` ++----------+ +| Overlaps | ++----------+ +| 1 | ++----------+ +1 row in set (0.00 sec) +``` + +## 另请参阅 + +- [JSON 函数](/functions-and-operators/json-functions.md) +- [JSON 数据类型](/data-type-json.md) \ No newline at end of file diff --git a/functions-and-operators/json-functions/json-functions-utility.md b/functions-and-operators/json-functions/json-functions-utility.md new file mode 100644 index 000000000000..3c60681b006f --- /dev/null +++ b/functions-and-operators/json-functions/json-functions-utility.md @@ -0,0 +1,73 @@ +--- +title: JSON 效用函数 +summary: 了解 JSON 效用函数。 +--- + +# JSON 效用函数 + +本文档介绍 JSON 效用函数。 + +## [JSON_PRETTY()](https://dev.mysql.com/doc/refman/8.0/en/json-utility-functions.html#function_json-pretty) + +`JSON_PRETTY(json_doc)` 函数用于格式化 JSON 文档。 + +```sql +SELECT JSON_PRETTY('{"person":{"name":{"first":"John","last":"Doe"},"age":23}}')\G +``` + +``` +*************************** 1. row *************************** +JSON_PRETTY('{"person":{"name":{"first":"John","last":"Doe"},"age":23}}'): { + "person": { + "age": 23, + "name": { + "first": "John", + "last": "Doe" + } + } +} +1 row in set (0.00 sec) +``` + +## [JSON_STORAGE_FREE()](https://dev.mysql.com/doc/refman/8.0/en/json-utility-functions.html#function_json-storage-free) + +`JSON_STORAGE_FREE(json_doc)` 函数返回 JSON 值在原地更新操作后释放了多少存储空间,以二进制表示。 + +> **注意:** +> +> 由于 TiDB 的存储架构与 MySQL 不同,因此对于有效的 JSON 值,该函数总是返回 `0`,而且它的实现是为了[与 MySQL 8.0 兼容](/mysql-compatibility.md)。请注意,TiDB 不能进行原地更新。更多信息,请参阅 [RocksDB 的空间占用](/storage-engine/rocksdb-overview.md#rocksdb-的空间占用)。 + +```sql +SELECT JSON_STORAGE_FREE('{}'); +``` + +``` ++-------------------------+ +| JSON_STORAGE_FREE('{}') | ++-------------------------+ +| 0 | ++-------------------------+ +1 row in set (0.00 sec) +``` + +## [JSON_STORAGE_SIZE()](https://dev.mysql.com/doc/refman/8.0/en/json-utility-functions.html#function_json-storage-size) + +`JSON_STORAGE_SIZE(json_doc)` 函数返回存储 JSON 值所需的大致字节数。由于计算该大小时不考虑 TiKV 对数据的压缩,因此该函数的输出与 MySQL 并不完全兼容。 + +```sql +SELECT JSON_STORAGE_SIZE('{}'); +``` + +``` ++-------------------------+ +| JSON_STORAGE_SIZE('{}') | ++-------------------------+ +| 9 | ++-------------------------+ +1 row in set (0.00 sec) +``` + +## 另请参阅 + +- [JSON 函数](/functions-and-operators/json-functions.md) +- [JSON 数据类型](/data-type-json.md) \ No newline at end of file diff --git a/functions-and-operators/json-functions/json-functions-validate.md b/functions-and-operators/json-functions/json-functions-validate.md new file mode 100644 index 000000000000..cde80373a5d8 --- /dev/null +++ b/functions-and-operators/json-functions/json-functions-validate.md @@ -0,0 +1,382 @@ +--- +title: 验证 JSON 文档的函数 +summary: 了解验证 JSON 文档的函数。 +--- + +# 验证 JSON 文档的函数 + +本文档介绍用于验证 JSON 文档的函数。 + +## [JSON_SCHEMA_VALID()](https://dev.mysql.com/doc/refman/8.0/en/json-validation-functions.html#function_json-schema-valid) + +`JSON_SCHEMA_VALID(schema, json_doc)` 函数根据 schema 验证 JSON 文档,确保数据的完整性和一致性。该函数可以与 [CHECK](/constraints.md#check-约束) 约束一起使用,以便在修改表时自动进行 schema 验证。该函数遵循 [JSON Schema specification](https://json-schema.org/specification)。 + +验证关键词如下: + +| 验证关键词 | 适用于 | 描述 | +|---|---|---| +| `type` | Any | 测试类型,如 `array`、`string` | +| `enum` | Any | 测试某个值是否在指定的值数组中 | +| `const` | Any | 与 `enum` 相似,但只适用于单个值 | +| `allOf` | Any | 匹配所有指定的 schema | +| `anyOf` | Any | 匹配任意指定的 schema | +| `multipleOf` | `number`/`integer` | 测试值是否是指定值的倍数 | +| `maximum` | `number`/`integer` | 测试数值是否小于最大值(包括最大值在内)| +| `exclusiveMaximum` | `number`/`integer` | 测试数值是否小于最大值(不包括最大值) | +| `minimum` | `number`/`integer` | 测试数值是否大于最小值(包括最小值在内)| +| `exclusiveMinimum` | `number`/`integer` | 测试值是否大于最小值(不包括最小值) | +| `maxlength` | `string` | 测试值的长度是否不超过指定值 | +| `minLength` | `string` | 测试值的长度是否不低于指定值 | +| `format` | `string` | 测试字符串是否符合指定格式 | +| `pattern` | `string` | 测试字符串是否与模式匹配 | +| `items` | `array` | 适用于数组项的 schema | +| `prefixItems` | `array` | 适用于数组的位置项的 schema | +| `maxItems` | `array` | 测试数组中的元素数量是否不超过指定值 | +| `minItems` | `array` | 测试数组中的元素数量是否不低于指定值 | +| `uniqueItems` | `array` | 测试数组中的元素是否唯一,`true`/`false`| +| `contains` | `array` | 为数组中的元素设置 schema | +| `maxContains` | `array` | 与 `contains` 一起使用时,用于测试某些元素出现的最多次数 | +| `minContains` | `array` | 与 `contains` 一起使用时,用于测试某些元素出现的最少次数 | +| `properties` | `object` | 适用于对象属性的 schema | +| `patternProperties` | `object` | 根据属性名称的模式匹配,应用于某些属性的 schema | +| `additionalProperties` | `object` | 是否允许额外的属性,`true`/`false` | +| `minProperties` | `object` | 测试对象的最小属性数量 | +| `maxProperties` | `object` | 测试对象的最大属性数量 | +| `required` | `object` | 必须填写的属性名称 | + +示例: + +下面一些示例使用了如下 JSON 文档: + +```json +{ + "fruits": [ + "orange", + "apple", + "pear" + ], + "vegetables": [ + "carrot", + "pepper", + "kale"] +} +``` + +使用[用户自定义的变量](/user-defined-variables.md)存放 JSON 文档。 + +```sql +SET @j := '{"fruits": ["orange", "apple", "pear"], "vegetables": ["carrot", "pepper", "kale"]}'; +``` + +先测试类型: + +```sql +SELECT JSON_SCHEMA_VALID('{"type": "object"}',@j); +``` + +``` ++--------------------------------------------+ +| JSON_SCHEMA_VALID('{"type": "object"}',@j) | ++--------------------------------------------+ +| 1 | ++--------------------------------------------+ +1 row in set (0.00 sec) +``` + +```sql +SELECT JSON_SCHEMA_VALID('{"type": "array"}',@j); +``` + +``` ++-------------------------------------------+ +| JSON_SCHEMA_VALID('{"type": "array"}',@j) | ++-------------------------------------------+ +| 0 | ++-------------------------------------------+ +1 row in set (0.00 sec) +``` + +```sql +mysql> SELECT JSON_TYPE(@j); +``` + +``` ++---------------+ +| JSON_TYPE(@j) | ++---------------+ +| OBJECT | ++---------------+ +1 row in set (0.00 sec) +``` + +从上面的输出中可以看到,`@j` 的类型是 `object`,与 [`JSON_TYPE()`](/functions-and-operators/json-functions/json-functions-return.md#json_type) 的输出结果一致。 + +现在验证某些属性是否存在。 + +```sql +SELECT JSON_SCHEMA_VALID('{"required": ["fruits","vegetables"]}',@j); +``` + +``` ++---------------------------------------------------------------+ +| JSON_SCHEMA_VALID('{"required": ["fruits","vegetables"]}',@j) | ++---------------------------------------------------------------+ +| 1 | ++---------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +在前面的输出中可以看到,`fruits` 和 `vegetables` 的属性是存在的,验证成功。 + +```sql +SELECT JSON_SCHEMA_VALID('{"required": ["fruits","vegetables","grains"]}',@j); +``` + +``` ++------------------------------------------------------------------------+ +| JSON_SCHEMA_VALID('{"required": ["fruits","vegetables","grains"]}',@j) | ++------------------------------------------------------------------------+ +| 0 | ++------------------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +在前面的输出中可以看到,验证 `fruits`、`vegetables` 和 `grains` 属性是否存在失败了,因为 `grains` 不存在。 + +现在验证 `fruits` 是否为数组。 + +```sql +SELECT JSON_SCHEMA_VALID('{"properties": {"fruits": {"type": "array"}}}',@j); +``` + +``` ++-----------------------------------------------------------------------+ +| JSON_SCHEMA_VALID('{"properties": {"fruits": {"type": "array"}}}',@j) | ++-----------------------------------------------------------------------+ +| 1 | ++-----------------------------------------------------------------------+ +1 row in set (0.01 sec) +``` + +从上面的输出结果,可以确认 `fruits` 是数组。 + +```sql +SELECT JSON_SCHEMA_VALID('{"properties": {"fruits": {"type": "string"}}}',@j); +``` + +``` ++------------------------------------------------------------------------+ +| JSON_SCHEMA_VALID('{"properties": {"fruits": {"type": "string"}}}',@j) | ++------------------------------------------------------------------------+ +| 0 | ++------------------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +上面的输出结果显示 `fruits` **不是**字符串。 + +现在验证数组中的元素数量。 + +```sql +SELECT JSON_SCHEMA_VALID('{"properties": {"fruits": {"type": "array", "minItems": 3}}}',@j); +``` + +``` ++--------------------------------------------------------------------------------------+ +| JSON_SCHEMA_VALID('{"properties": {"fruits": {"type": "array", "minItems": 3}}}',@j) | ++--------------------------------------------------------------------------------------+ +| 1 | ++--------------------------------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +前面的输出结果显示,`fruits` 是一个至少包含 3 个元素的数组。 + +```sql +SELECT JSON_SCHEMA_VALID('{"properties": {"fruits": {"type": "array", "minItems": 4}}}',@j); +``` + +``` ++--------------------------------------------------------------------------------------+ +| JSON_SCHEMA_VALID('{"properties": {"fruits": {"type": "array", "minItems": 4}}}',@j) | ++--------------------------------------------------------------------------------------+ +| 0 | ++--------------------------------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +前面的输出结果显示,`fruits` **不是**一个至少包含 4 个元素的数组,它没有达到元素数量的最低要求。 + +对于整数值,可以检查它们是否在某个范围内。 + +```sql +SELECT JSON_SCHEMA_VALID('{"type": "integer", "minimum": 40, "maximum": 45}', '42'); ++------------------------------------------------------------------------------+ +| JSON_SCHEMA_VALID('{"type": "integer", "minimum": 40, "maximum": 45}', '42') | ++------------------------------------------------------------------------------+ +| 1 | ++------------------------------------------------------------------------------+ +1 row in set (0.01 sec) +``` + +```sql +SELECT JSON_SCHEMA_VALID('{"type": "integer", "minimum": 40, "maximum": 45}', '123'); +``` + +``` ++-------------------------------------------------------------------------------+ +| JSON_SCHEMA_VALID('{"type": "integer", "minimum": 40, "maximum": 45}', '123') | ++-------------------------------------------------------------------------------+ +| 0 | ++-------------------------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +对于字符串,可以验证是否匹配指定的模式。 + +```sql +SELECT JSON_SCHEMA_VALID('{"type": "string", "pattern": "^Ti"}', '"TiDB"'); +``` + +``` ++---------------------------------------------------------------------+ +| JSON_SCHEMA_VALID('{"type": "string", "pattern": "^Ti"}', '"TiDB"') | ++---------------------------------------------------------------------+ +| 1 | ++---------------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +```sql +SELECT JSON_SCHEMA_VALID('{"type": "string", "pattern": "^Ti"}', '"PingCAP"'); +``` + +``` ++------------------------------------------------------------------------+ +| JSON_SCHEMA_VALID('{"type": "string", "pattern": "^Ti"}', '"PingCAP"') | ++------------------------------------------------------------------------+ +| 0 | ++------------------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +你可以检查一个值是否符合指定的命名格式。可验证的格式包括:`ipv4`、`ipv6`、`time`、`date`、`duration`、`email`、`hostname`、`uuid` 和 `uri`。 + +```sql +SELECT JSON_SCHEMA_VALID('{"format": "ipv4"}', '"127.0.0.1"'); +``` + +``` ++--------------------------------------------------------+ +| JSON_SCHEMA_VALID('{"format": "ipv4"}', '"127.0.0.1"') | ++--------------------------------------------------------+ +| 1 | ++--------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +```sql +SELECT JSON_SCHEMA_VALID('{"format": "ipv4"}', '"327.0.0.1"'); +``` + +``` ++--------------------------------------------------------+ +| JSON_SCHEMA_VALID('{"format": "ipv4"}', '"327.0.0.1"') | ++--------------------------------------------------------+ +| 0 | ++--------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +你还可以使用 `enum` 来检查一个字符串是否在一个数组中。 + +```sql +SELECT JSON_SCHEMA_VALID('{"enum": ["TiDB", "MySQL"]}', '"TiDB"'); +``` + +``` ++------------------------------------------------------------+ +| JSON_SCHEMA_VALID('{"enum": ["TiDB", "MySQL"]}', '"TiDB"') | ++------------------------------------------------------------+ +| 1 | ++------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +```sql +SELECT JSON_SCHEMA_VALID('{"enum": ["TiDB", "MySQL"]}', '"MySQL"'); +``` + +``` ++-------------------------------------------------------------+ +| JSON_SCHEMA_VALID('{"enum": ["TiDB", "MySQL"]}', '"MySQL"') | ++-------------------------------------------------------------+ +| 1 | ++-------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +```sql +SELECT JSON_SCHEMA_VALID('{"enum": ["TiDB", "MySQL"]}', '"SQLite"'); +``` + +``` ++--------------------------------------------------------------+ +| JSON_SCHEMA_VALID('{"enum": ["TiDB", "MySQL"]}', '"SQLite"') | ++--------------------------------------------------------------+ +| 0 | ++--------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +你可以使用 `anyOf` 将某些要求组合起来,验证是否满足其中任意一个要求。 + +```sql +SELECT JSON_SCHEMA_VALID('{"anyOf": [{"type": "string"},{"type": "integer"}]}', '"TiDB"'); +``` + +``` ++------------------------------------------------------------------------------------+ +| JSON_SCHEMA_VALID('{"anyOf": [{"type": "string"},{"type": "integer"}]}', '"TiDB"') | ++------------------------------------------------------------------------------------+ +| 1 | ++------------------------------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +```sql +SELECT JSON_SCHEMA_VALID('{"anyOf": [{"type": "string"},{"type": "integer"}]}', '["TiDB", "MySQL"]'); +``` + +``` ++-----------------------------------------------------------------------------------------------+ +| JSON_SCHEMA_VALID('{"anyOf": [{"type": "string"},{"type": "integer"}]}', '["TiDB", "MySQL"]') | ++-----------------------------------------------------------------------------------------------+ +| 0 | ++-----------------------------------------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +```sql +SELECT JSON_SCHEMA_VALID('{"anyOf": [{"type": "string"},{"type": "integer"}]}', '5'); +``` + +``` ++-------------------------------------------------------------------------------+ +| JSON_SCHEMA_VALID('{"anyOf": [{"type": "string"},{"type": "integer"}]}', '5') | ++-------------------------------------------------------------------------------+ +| 1 | ++-------------------------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +## MySQL 兼容性 + +- 如果 `JSON_SCHEMA_VALID()` 中待验证的 schema 无效(如 `{"type": "sting"}`),MySQL 可能会接受该 schema ,但 TiDB 会返回错误。注意这里的 `"sting"` 存在拼写错误,应为 `"string"`。 +- MySQL 使用的是较早 draft 版本的 JSON Schema standard。 + +## 另请参阅 + +- [JSON Schema Reference](https://json-schema.org/understanding-json-schema/reference) +- [JSON 函数](/functions-and-operators/json-functions.md) +- [JSON 数据类型](/data-type-json.md) \ No newline at end of file