Documentation Home
MySQL 8.4 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 39.8Mb
PDF (A4) - 39.9Mb
Man Pages (TGZ) - 257.9Kb
Man Pages (Zip) - 364.9Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


MySQL 8.4 Reference Manual  /  Data Types  /  The JSON Data Type

13.5 JSON 数据类型

MySQL 支持一个 native 的 JSON 数据类型,定义于RFC 7159,使得对 JSON 文档中的数据进行高效访问。JSON 数据类型提供了以下优点,可以将 JSON 格式字符串存储在 string 列中:

  • 自动验证存储在 JSON 列中的 JSON 文档。如果文档无效,会产生错误。

  • 优化存储格式。JSON 文档在 JSON 列中被转换为一个内部格式,这样可以快速访问文档元素。当服务器后续需要从这个二进制格式中读取 JSON 值时,值不需要从文本表示中解析。二进制格式结构化,以便服务器可以直接通过键或数组索引来查找子对象或嵌套值,而无需先读取所有值或在它们之前或之后。

MySQL 8.4 还支持根据 RFC 7396 定义的 JSON Merge Patch 格式,使用 JSON_MERGE_PATCH() 函数。请查看该函数的描述,以及Normalization, Merging, and Autowrapping of JSON Values,以获取示例和进一步信息。

Note

本讨论使用 JSON 表示特定 JSON 数据类型,并使用JSON 表示一般的 JSON 数据。

存储一个JSON文档所需的空间大致与LONGBLOBLONGTEXT相似;请参阅第13.7节,“数据类型存储要求”,了解更多信息。需要注意的是,任何在JSON列中存储的JSON文档的大小都受max_allowed_packet系统变量的值限制。 (当服务器在内存中 manipulation一个 JSON 值时,它可以大于这个限制;该限制适用于服务器将其存储时。)您可以使用JSON_ STORAGE_SIZE()函数来获取存储一个 JSON 文档所需的空间量;注意,对于一个JSON列,存储大小—和因此该函数返回的值—是对该列进行任何部分更新之前使用的大小(请参阅本节后面的 JSON 部分更新优化讨论)。

除了JSON数据类型外,还有一组 SQL 函数可用于在 JSON 值上执行操作,如创建、 manipulation 和搜索。以下讨论显示了这些操作的示例。关于个别函数的详细信息,请参阅第14.17节,“JSON 函数”

还提供了一个用于操作GeoJSON值的空间函数集。请参阅第14.16.11节,“Spatial GeoJSON Functions”

JSON 列,如其他二进制类型的列,不可以直接索引;相反,可以创建一个从JSON 列中提取标量值的生成列,并对其进行索引。请参阅Indexing a Generated Column to Provide a JSON Column Index,以获取详细示例。

MySQL 优化器还会寻找与JSON表达式匹配的虚拟列索引。

InnoDB存储引擎支持在JSON数组上创建多值索引。请参阅Multi-Valued Indexes

MySQL NDB集群支持JSON 列和MySQL JSON函数,包括在从JSON 列中生成的列上创建索引,以解决无法对JSON 列进行索引的问题。每个NDB 表最多支持3个JSON 列。

在 MySQL 8.4 中,优化器可以对JSON 列进行部分、原地更新,而不是删除旧文档并将新文档写入整个列中。这一优化可以在满足以下条件的更新操作中进行:

  • 正在更新的列被声明为JSON

  • UPDATE语句使用其中三个函数JSON_SET()JSON_REPLACE()JSON_REMOVE()来更新列。不能执行直接将列值分配给的操作(例如:UPDATE mytable SET jcol = '{"a": 10, "b": 25}'),因为这不是部分更新。

    可以在单个UPDATE语句中更新多个JSON列,以优化更新操作;MySQL可以只更新使用前三个函数更新的那些列的部分值。

  • 输入列和目标列必须是同一列;不能执行类似于:UPDATE mytable SET jcol1 = JSON_SET(jcol2, '$.a', 100)的部分更新操作。

    可以使用嵌套调用前三个函数中的任何组合,以便在同一列上执行更新操作,只要输入和目标列相同。

  • 所有更改都将现有数组或对象值替换为新值,而不添加任何新元素到父对象或数组中。

  • 被替换的值必须至少与替换值一样大。换言之,新的值不能比旧值更大。

    在这种情况下,这个要求可能会有例外,当之前的部分更新留下了足够的空间来容纳更大的值。你可以使用函数JSON_STORAGE_FREE()查看任何 JSON 列的部分更新已经释放了多少空间。

这些部分更新可以以紧凑格式写入二进制日志,保存空间,这可以通过将binlog_row_value_options系统变量设置为PARTIAL_JSON来启用。

区分 JSON 列值存储在表中的部分更新和写入二进制日志的部分更新非常重要。可能会出现完整更新 JSON 列被记录到二进制日志中作为部分更新的情况。这可以发生在前两个条件之一不满足但其他条件满足的情况下。

请参阅binlog_row_value_options的描述。

以下几节提供了基本信息关于 JSON 值的创建和操作。

JSON 数组包含由逗号分隔的值,enclosed 在[]字符之间:

["abc", 10, null, true, false]

JSON 对象包含由逗号分隔的键-值对,enclosed 在{}字符之间:

{"k1": "value", "k2": 10}

如示例所示,JSON 数组和对象可以包含字符串或数字的标量值、JSON null 文本、JSON 布尔值 true 或 false literals。JSON 对象中的键必须是字符串。时间(日期、时间或 datetime)标量值也被允许:

["12:18:29.000000", "2015-07-29", "2015-07-29 12:18:29.000000"]

在 JSON 数组元素和 JSON 对象键值中,嵌套也是被允许的:

[99, {"id": "HK500", "cost": 75.99}, ["hot", "cold"]]
{"k1": "value", "k2": [10, 20]}

您还可以从 MySQL 提供的一些函数中获取 JSON 值(见第14.17.2节,“Functions That Create JSON Values”),或者将其他类型的值转换为JSON类型使用CAST(value AS JSON)(见将 JSON 和非 JSON 值之间进行转换)。下面几段描述了 MySQL 处理输入 JSON 值的方式。

在 MySQL 中,JSON 值被写作字符串。MySQL 将任何用于需要 JSON 值的上下文中的字符串解析,并在它不是有效的 JSON 时产生错误。这些上下文包括将值插入具有JSON数据类型的列,以及将 JSON 值传递给期望 JSON 值的函数(通常在 MySQL JSON 函数的文档中显示为json_docjson_val),以下示例演示:

  • 尝试将值插入JSON列成功,如果值是有效的 JSON 值,但失败如果不是:

    mysql> CREATE TABLE t1 (jdoc JSON);
    Query OK, 0 rows affected (0.20 sec)
    
    mysql> INSERT INTO t1 VALUES('{"key1": "value1", "key2": "value2"}');
    Query OK, 1 row affected (0.01 sec)
    
    mysql> INSERT INTO t1 VALUES('[1, 2,');
    ERROR 3140 (22032) at line 2: Invalid JSON text:
    "Invalid value." at position 6 in value (or column) '[1, 2,'.

    在错误信息中出现的at position N位置是从0开始的,但应该被认为是问题实际所在位置的粗略指示。

  • 函数JSON_TYPE()期望一个JSON参数,并尝试将其解析为JSON值。如果是有效的JSON值,它返回该值的JSON类型;否则产生错误。

    mysql> SELECT JSON_TYPE('["a", "b", 1]');
    +----------------------------+
    | JSON_TYPE('["a", "b", 1]') |
    +----------------------------+
    | ARRAY                      |
    +----------------------------+
    
    mysql> SELECT JSON_TYPE('"hello"');
    +----------------------+
    | JSON_TYPE('"hello"') |
    +----------------------+
    | STRING               |
    +----------------------+
    
    mysql> SELECT JSON_TYPE('hello');
    ERROR 3146 (22032): Invalid data type for JSON data in argument 1
    to function json_type; a JSON string or JSON type is required.

MySQL 在JSON上下文中使用utf8mb4字符集和utf8mb4_bin排序规则。其他字符集的字符串将被转换为utf8mb4以适应JSON格式。(对于在asciiutf8mb3字符集中使用的字符串,不需要转换,因为asciiutf8mb3utf8mb4的子集。)

作为将JSON值写入使用文字字符串的替代方案,存在用于从组件元素构建JSON值的函数。JSON_ARRAY()以可能为空的值列表为参数,并返回包含这些值的JSON数组:

mysql> SELECT JSON_ARRAY('a', 1, NOW());
+----------------------------------------+
| JSON_ARRAY('a', 1, NOW())              |
+----------------------------------------+
| ["a", 1, "2015-07-27 09:43:47.000000"] |
+----------------------------------------+

JSON_OBJECT()以可能为空的键值对列表为参数,并返回包含这些对的JSON对象:

mysql> SELECT JSON_OBJECT('key1', 1, 'key2', 'abc');
+---------------------------------------+
| JSON_OBJECT('key1', 1, 'key2', 'abc') |
+---------------------------------------+
| {"key1": 1, "key2": "abc"}            |
+---------------------------------------+

JSON_MERGE_PRESERVE()以两个或多个JSON文档为参数,并返回组合结果:

mysql> SELECT JSON_MERGE_PRESERVE('["a", 1]', '{"key": "value"}');
+-----------------------------------------------------+
| JSON_MERGE_PRESERVE('["a", 1]', '{"key": "value"}') |
+-----------------------------------------------------+
| ["a", 1, {"key": "value"}]                          |
+-----------------------------------------------------+
1 row in set (0.00 sec)

关于合并规则的信息,请见JSON值的 normalize、merge 和 autowrapping

(MySQL还支持JSON_MERGE_PATCH(),它具有不同行为。见JSON_MERGE_PATCH()与 JSON_MERGE_PRESERVE()的比较,了解这两个函数之间的差异。)

可以将JSON值分配给用户定义的变量:

mysql> SET @j = JSON_OBJECT('key', 'value');
mysql> SELECT @j;
+------------------+
| @j               |
+------------------+
| {"key": "value"} |
+------------------+

然而,用户定义的变量不能是JSON数据类型,因此尽管在前一个示例中@j看起来像JSON值,并且具有与JSON值相同的字符集和排序规则,但它实际上不是JSON数据类型,而是将JSON_OBJECT()的结果转换为字符串并分配给变量。

由JSON值转换产生的字符串具有字符集utf8mb4和排序规则utf8mb4_ bin

mysql> SELECT CHARSET(@j), COLLATION(@j);
+-------------+---------------+
| CHARSET(@j) | COLLATION(@j) |
+-------------+---------------+
| utf8mb4     | utf8mb4_bin   |
+-------------+---------------+

由于utf8mb4_ bin是二进制排序规则,因此JSON值的比较是区分大小写的。

mysql> SELECT JSON_ARRAY('x') = JSON_ARRAY('X');
+-----------------------------------+
| JSON_ARRAY('x') = JSON_ARRAY('X') |
+-----------------------------------+
|                                 0 |
+-----------------------------------+

大小写敏感也适用于JSON的nulltruefalse字面量,它们总是必须以小写形式书写:

mysql> SELECT JSON_VALID('null'), JSON_VALID('Null'), JSON_VALID('NULL');
+--------------------+--------------------+--------------------+
| JSON_VALID('null') | JSON_VALID('Null') | JSON_VALID('NULL') |
+--------------------+--------------------+--------------------+
|                  1 |                  0 |                  0 |
+--------------------+--------------------+--------------------+

mysql> SELECT CAST('null' AS JSON);
+----------------------+
| CAST('null' AS JSON) |
+----------------------+
| null                 |
+----------------------+
1 row in set (0.00 sec)

mysql> SELECT CAST('NULL' AS JSON);
ERROR 3141 (22032): Invalid JSON text in argument 1 to function cast_as_json:
"Invalid value." at position 0 in 'NULL'.

JSON 文本的大小写敏感性不同于 SQL NULLTRUEFALSE 文本的大小写敏感性,这些文本可以以任何字母大小写书写:

mysql> SELECT ISNULL(null), ISNULL(Null), ISNULL(NULL);
+--------------+--------------+--------------+
| ISNULL(null) | ISNULL(Null) | ISNULL(NULL) |
+--------------+--------------+--------------+
|            1 |            1 |            1 |
+--------------+--------------+--------------+

有时可能需要或想在 JSON 文档中插入引号字符("')。假设你想要将一些包含字符串表示 MySQL 事实的句子,每个句子与相应关键字配对,插入到使用以下 SQL 语句创建的表中:

mysql> CREATE TABLE facts (sentence JSON);

这些关键字-句子对中有一对:

mascot: The MySQL mascot is a dolphin named "Sakila".

一种将其作为 JSON 对象插入 facts 表的方法是使用 MySQL JSON_OBJECT() 函数。在这种情况下,你必须使用反斜杠来转义每个引号字符,如下所示:

mysql> INSERT INTO facts VALUES
     >   (JSON_OBJECT("mascot", "Our mascot is a dolphin named \"Sakila\"."));

这在将值插入 JSON 对象文字时不起作用,在这种情况下,你必须使用双反斜杠转义序列,如下所示:

mysql> INSERT INTO facts VALUES
     >   ('{"mascot": "Our mascot is a dolphin named \\"Sakila\\"."}');

使用双反斜杠保持 MySQL 不执行转义序列处理,而是将字符串文字传递给存储引擎进行处理。插入 JSON 对象后,您可以通过简单的 SELECT 语句,如下所示,查看反斜杠是否存在于 JSON 列值中:

mysql> SELECT sentence FROM facts;
+---------------------------------------------------------+
| sentence                                                |
+---------------------------------------------------------+
| {"mascot": "Our mascot is a dolphin named \"Sakila\"."} |
+---------------------------------------------------------+

要使用 mascot 作为键来查找这个特定的句子,可以使用列路径操作符 ->,如以下所示:

mysql> SELECT col->"$.mascot" FROM qtest;
+---------------------------------------------+
| col->"$.mascot"                             |
+---------------------------------------------+
| "Our mascot is a dolphin named \"Sakila\"." |
+---------------------------------------------+
1 row in set (0.00 sec)

这将保留反斜杠的原始状态,包括周围的引号。要使用mascot作为键,但不包括周围的引号或任何转义符,请使用内联路径操作符->>,例如:

mysql> SELECT sentence->>"$.mascot" FROM facts;
+-----------------------------------------+
| sentence->>"$.mascot"                   |
+-----------------------------------------+
| Our mascot is a dolphin named "Sakila". |
+-----------------------------------------+
Note

前面的示例在NO_BACKSLASH_ESCAPES服务器SQL模式下不起作用。如果启用了这个模式,可以使用单个反斜杠来插入JSON对象字面量,而不是双反斜杠,并保留反斜杠。如果您在执行插入操作时使用JSON_OBJECT()函数,并且启用了这个模式,必须交替使用单引号和双引号,如下所示:

mysql> INSERT INTO facts VALUES
     > (JSON_OBJECT('mascot', 'Our mascot is a dolphin named "Sakila".'));

请查看JSON_UNQUOTE()函数的描述,以了解这个模式对 JSON 值中的转义字符的影响。

当字符串被解析并发现是一个有效的 JSON 文档时,它也将被 normalize。这意味着,读取从左到右的文档中,遇到的成员键如果与后续文档中的键重复,将被丢弃。以下JSON_OBJECT()调用产生的对象值只包含第二个key1元素,因为该键名在值中出现更早,如下所示:

mysql> SELECT JSON_OBJECT('key1', 1, 'key2', 'abc', 'key1', 'def');
+------------------------------------------------------+
| JSON_OBJECT('key1', 1, 'key2', 'abc', 'key1', 'def') |
+------------------------------------------------------+
| {"key1": "def", "key2": "abc"}                       |
+------------------------------------------------------+

normalization 也将在插入 JSON 列时进行,如下所示:

mysql> CREATE TABLE t1 (c1 JSON);

mysql> INSERT INTO t1 VALUES
     >     ('{"x": 17, "x": "red"}'),
     >     ('{"x": 17, "x": "red", "x": [3, 5, 7]}');

mysql> SELECT c1 FROM t1;
+------------------+
| c1               |
+------------------+
| {"x": "red"}     |
| {"x": [3, 5, 7]} |
+------------------+

这项last duplicate key wins行为是由RFC 7159建议的,并且大多数JavaScript解析器都实现了。 (Bug #86866, Bug #26369555)

MySQL 会将原始JSON文档中的额外空格去除,或者在必要时插入一个单个空格,以便于显示。这是为了提高可读性。

MySQL 生成JSON值的函数(见第14.17.2节,“Functions That Create JSON Values”)总是返回标准化的值。

为了提高查找效率,MySQL 也会对JSON对象的键进行排序。您应该注意的是,这个顺序结果可能会改变,并且不保证在不同版本中的一致性

支持两个合并算法,分别由函数JSON_MERGE_PRESERVE()JSON_MERGE_PATCH()实现。这些函数在处理重复键时有所不同:JSON_MERGE_PRESERVE()保留重复键的值,而JSON_MERGE_PATCH()则丢弃所有except the last value。下面几段将解释这两个函数如何处理不同组合的JSON文档(即对象和数组)。

合并数组。 在组合多个数组的上下文中,数组将被合并成一个单独的数组。JSON_ MERGE_PRESERVE()通过将后续数组连接到第一个数组的末尾来实现此操作。JSON_ MERGE_PATCH()将每个参数视为一个包含单个元素的数组(因此具有0索引),然后应用“最后重复键获胜”逻辑,以选择最后一个参数。你可以比较由此查询显示的结果:

mysql> SELECT
    ->   JSON_MERGE_PRESERVE('[1, 2]', '["a", "b", "c"]', '[true, false]') AS Preserve,
    ->   JSON_MERGE_PATCH('[1, 2]', '["a", "b", "c"]', '[true, false]') AS Patch\G
*************************** 1. row ***************************
Preserve: [1, 2, "a", "b", "c", true, false]
   Patch: [true, false]

多个对象合并将产生单个对象。JSON_ MERGE_PRESERVE()在遇到同名键时将组合所有唯一值为该键的数组,然后将该数组用作结果中的该键的值;JSON_ MERGE_PATCH()会丢弃找到的重复键,自左向右工作,因此结果只包含最后一个该键的值。以下查询展示了对于重复键a的结果差异:

mysql> SELECT
    ->   JSON_MERGE_PRESERVE('{"a": 1, "b": 2}', '{"c": 3, "a": 4}', '{"c": 5, "d": 3}') AS Preserve,
    ->   JSON_MERGE_PATCH('{"a": 3, "b": 2}', '{"c": 3, "a": 4}', '{"c": 5, "d": 3}') AS Patch\G
*************************** 1. row ***************************
Preserve: {"a": [1, 4], "b": 2, "c": [3, 5], "d": 3}
   Patch: {"a": 4, "b": 2, "c": 5, "d": 3}

在需要数组值的上下文中使用非数组值将被自动包围:将该值 surround by []字符以将其转换为数组。在以下语句中,每个参数都将被自动包围为数组([1][2])。这些然后将被合并成单个结果数组;正如前两个情况一样,JSON_ MERGE_PRESERVE()将组合具有同名键的值,而JSON_ MERGE_PATCH()将丢弃所有重复键除了最后一个,如下所示:

mysql> SELECT
	  ->   JSON_MERGE_PRESERVE('1', '2') AS Preserve,
	  ->   JSON_MERGE_PATCH('1', '2') AS Patch\G
*************************** 1. row ***************************
Preserve: [1, 2]
   Patch: 2

数组和对象值通过将对象自动包装为数组并根据合并函数的选择(JSON_ MERGE_PRESERVE()JSON_ MERGE_PATCH())合并数组,或者根据“最后一个重复键获胜”规则合并数组,可以在以下示例中看到:

mysql> SELECT
	  ->   JSON_MERGE_PRESERVE('[10, 20]', '{"a": "x", "b": "y"}') AS Preserve,
	  ->   JSON_MERGE_PATCH('[10, 20]', '{"a": "x", "b": "y"}') AS Patch\G
*************************** 1. row ***************************
Preserve: [10, 20, {"a": "x", "b": "y"}]
   Patch: {"a": "x", "b": "y"}

JSON路径表达式选择 JSON 文档中的值。

路径表达式在提取或修改 JSON 文档的函数中非常有用,可以指定要操作的文档部分。例如,以下查询从 JSON 文档中提取具有name键的成员的值:

mysql> SELECT JSON_EXTRACT('{"id": 14, "name": "Aztalan"}', '$.name');
+---------------------------------------------------------+
| JSON_EXTRACT('{"id": 14, "name": "Aztalan"}', '$.name') |
+---------------------------------------------------------+
| "Aztalan"                                               |
+---------------------------------------------------------+

路径语法使用leading $ 字符来表示正在考虑的 JSON 文档,optional 后跟选择器,以指示文档中的更具体部分:

  • 以点号开头的键名命名对象中的成员,该键名必须在双引号中指定,如果名称不包含引号且不是合法的路径表达式(例如,如果它包含空格)。

  • [N] 附加到选择数组的 path,命名该数组中的第 N 个值。数组位置是从零开始的整数。如果 path 不选择数组值,则 path[0] 的评估结果与 path 相同:

    mysql> SELECT JSON_SET('"x"', '$[0]', 'a');
    +------------------------------+
    | JSON_SET('"x"', '$[0]', 'a') |
    +------------------------------+
    | "a"                          |
    +------------------------------+
    1 row in set (0.00 sec)
  • [MN] 指定了一个数组值的子集或范围,从位置 M 开始,到位置 N 结束。

    last 也可以用作右侧数组元素的索引-relative addressing of array elements 也是支持的。如果 path 不选择数组值,path[last] 的评估结果与 path 相同,如后面一节所示(见右侧数组元素)。

  • 路径可以包含***通配符:

    • .[*] 评估为 JSON 对象中的所有成员值。

    • [*] 评估为 JSON 数组中的所有元素值。

    • prefix**suffix 评估为以指定前缀开始并以指定后缀结束的所有路径。

  • 在文档中不存在的路径(评估为无效数据)将评估为NULL

$ 表示以下 JSON 数组,其中包含三个元素:

[3, {"a": [5, 6], "b": 10}, [99, 100]]

然后:

  • $[0] 评估为 3

  • $[1] 评估为 {"a": [5, 6], "b": 10}

  • $[2] 评估为 [99, 100]

  • $[3] evaluates to NULL(它引用的是第四个数组元素,但不存在)。

因为$[1]$[2]的值不是标量值,所以它们可以用作选择嵌套值的基础路径表达式的依据。示例:

  • $[1].a evaluates to [5, 6]

  • $[1].a[1] evaluates to 6

  • $[1].b evaluates to 10

  • $[2][0] evaluates to 99

如前所述,路径组件如果命名的键不是合法的路径表达式,可以使用引号来引用。让$指向这个值:

{"a fish": "shark", "a bird": "sparrow"}

这两个键都包含空格,需要使用引号:

  • $."a fish" evaluates to shark

  • $."a bird" evaluates to sparrow

使用通配符的路径表达式将评估为包含多个值的数组:

mysql> SELECT JSON_EXTRACT('{"a": 1, "b": 2, "c": [3, 4, 5]}', '$.*');
+---------------------------------------------------------+
| JSON_EXTRACT('{"a": 1, "b": 2, "c": [3, 4, 5]}', '$.*') |
+---------------------------------------------------------+
| [1, 2, [3, 4, 5]]                                       |
+---------------------------------------------------------+
mysql> SELECT JSON_EXTRACT('{"a": 1, "b": 2, "c": [3, 4, 5]}', '$.c[*]');
+------------------------------------------------------------+
| JSON_EXTRACT('{"a": 1, "b": 2, "c": [3, 4, 5]}', '$.c[*]') |
+------------------------------------------------------------+
| [3, 4, 5]                                                  |
+------------------------------------------------------------+

在以下示例中,路径$**.b评估为多个路径($.a.b$.c.b),并产生匹配路径值的数组:

mysql> SELECT JSON_EXTRACT('{"a": {"b": 1}, "c": {"b": 2}}', '$**.b');
+---------------------------------------------------------+
| JSON_EXTRACT('{"a": {"b": 1}, "c": {"b": 2}}', '$**.b') |
+---------------------------------------------------------+
| [1, 2]                                                  |
+---------------------------------------------------------+

JSON 数组中的范围. 您可以使用关键字 to 来指定 JSON 数组的子集。例如,$[1 to 3] 包含数组的第二、第三和第四个元素,如下所示:

mysql> SELECT JSON_EXTRACT('[1, 2, 3, 4, 5]', '$[1 to 3]');
+----------------------------------------------+
| JSON_EXTRACT('[1, 2, 3, 4, 5]', '$[1 to 3]') |
+----------------------------------------------+
| [2, 3, 4]                                    |
+----------------------------------------------+
1 row in set (0.00 sec)

语法为MN,其中MN 分别是 JSON 数组中范围元素的第一个和最后一个索引。N 必须大于 MM 必须大于或等于 0。数组元素从 0 开始索引。

您可以在支持通配符的上下文中使用范围。

最右侧数组元素。 关键字last 支持作为最后一个数组元素的索引synonym。形式为last - N 可以用于相对地址和范围定义,如下所示:

mysql> SELECT JSON_EXTRACT('[1, 2, 3, 4, 5]', '$[last-3 to last-1]');
+--------------------------------------------------------+
| JSON_EXTRACT('[1, 2, 3, 4, 5]', '$[last-3 to last-1]') |
+--------------------------------------------------------+
| [2, 3, 4]                                              |
+--------------------------------------------------------+
1 row in set (0.01 sec)

如果路径评估的是不是数组值,则评估结果与将该值包装在单个元素数组中相同:

mysql> SELECT JSON_REPLACE('"Sakila"', '$[last]', 10);
+-----------------------------------------+
| JSON_REPLACE('"Sakila"', '$[last]', 10) |
+-----------------------------------------+
| 10                                      |
+-----------------------------------------+
1 row in set (0.00 sec)

您可以使用column->path,将JSON列标识符和JSON路径表达式作为JSON_EXTRACT()的同义词。请参阅第14.17.3节,“搜索JSON值的函数”,了解更多信息。另外,也可以查看为生成列提供JSON列索引

一些函数将现有JSON文档,修改它以某种方式,并返回结果修改后的文档。路径表达式指示在文档中哪里进行更改。例如,JSON_SET()JSON_INSERT()JSON_REPLACE()函数每个都接受一个JSON文档,以及一个或多个路径-值对,该对描述了要修改文档的位置和要使用的值。这些函数在处理文档中的现有和不存在值方面不同。

请考虑这个文档:

mysql> SET @j = '["a", {"b": [true, false]}, [10, 20]]';

JSON_SET()将存在的路径替换为新值,并添加不存在的路径:

mysql> SELECT JSON_SET(@j, '$[1].b[0]', 1, '$[2][2]', 2);
+--------------------------------------------+
| JSON_SET(@j, '$[1].b[0]', 1, '$[2][2]', 2) |
+--------------------------------------------+
| ["a", {"b": [1, false]}, [10, 20, 2]]      |
+--------------------------------------------+

在这种情况下,路径$[1].b[0]选择了一个现有的值(true),并将其替换为路径参数后的值(1)。路径$[2][2]不存在,所以对应的值(2)将被添加到由$[2]选择的值中。

JSON_INSERT() 添加新的值,但不替换现有值:

mysql> SELECT JSON_INSERT(@j, '$[1].b[0]', 1, '$[2][2]', 2);
+-----------------------------------------------+
| JSON_INSERT(@j, '$[1].b[0]', 1, '$[2][2]', 2) |
+-----------------------------------------------+
| ["a", {"b": [true, false]}, [10, 20, 2]]      |
+-----------------------------------------------+

JSON_REPLACE() 替换现有值,并忽略新值:

mysql> SELECT JSON_REPLACE(@j, '$[1].b[0]', 1, '$[2][2]', 2);
+------------------------------------------------+
| JSON_REPLACE(@j, '$[1].b[0]', 1, '$[2][2]', 2) |
+------------------------------------------------+
| ["a", {"b": [1, false]}, [10, 20]]             |
+------------------------------------------------+

路径-值对是从左到右评估的。评估一个对时,产生的文档将成为下一个对的新的值。

JSON_REMOVE() 接受一个 JSON 文档和一个或多个路径,这些路径指定要从文档中删除的值。返回值是原始文档减去由路径选择的值:

mysql> SELECT JSON_REMOVE(@j, '$[2]', '$[1].b[1]', '$[1].b[1]');
+---------------------------------------------------+
| JSON_REMOVE(@j, '$[2]', '$[1].b[1]', '$[1].b[1]') |
+---------------------------------------------------+
| ["a", {"b": [true]}]                              |
+---------------------------------------------------+

这些路径具有以下效果:

  • $[2] 匹配 [10, 20] 并删除它。

  • 第一个$[1].b[1]的实例匹配 falseb 元素中,并删除它。

  • 第二个$[1].b[1]的实例匹配 nothing:该元素已经被删除,路径不再存在,对其没有影响。

MySQL 中许多 JSON 函数(见第14.17节,“JSON Functions”)需要路径表达式以确定 JSON 文档中的特定元素。路径由作用域和一个或多个路径腿组成。用于 MySQL JSON 函数的路径中,作用域总是正在搜索或操作的文档,表示为带有leading $ 字符的路径。路径腿之间使用点字符(.)。数组中的单元格由[N] 表示,其中N 是非负整数。键名必须是双引号字符串或有效的 ECMAScript 标识符(见Identifier Names and Identifiers,在 ECMAScript Language Specification 中)。路径表达式,如 JSON 文本,应该使用asciiutf8mb3utf8mb4 字符集编码。其他字符编码将被隐式转换为 utf8mb4。完整的语法如下:

pathExpression:
    scope[(pathLeg)*]

pathLeg:
    member | arrayLocation | doubleAsterisk

member:
    period ( keyName | asterisk )

arrayLocation:
    leftBracket ( nonNegativeInteger | asterisk ) rightBracket

keyName:
    ESIdentifier | doubleQuotedString

doubleAsterisk:
    '**'

period:
    '.'

asterisk:
    '*'

leftBracket:
    '['

rightBracket:
    ']'

如前所述,在 MySQL 中,路径的作用域总是正在操作的文档,表示为$。您可以使用'$' 作为 JSON 路径表达式中的文档同义词。

Note

一些实现支持 JSON 路径的列引用;MySQL 8.4 不支持这些。

通配符 *** token 用于以下方式:

  • .* 表示对象中的所有成员的值。

  • [*] 表示数组中的所有单元的值。

  • [prefix]**suffix 表示以 prefix 开头,结尾为 suffix 的所有路径。 prefix 可选,而 suffix 是必需的;换言之,路径不能以 ** 结尾。

    此外,路径也不能包含序列 ***

关于路径语法示例,请参阅各种 JSON 函数的描述,这些函数将路径作为参数,如 JSON_CONTAINS_PATH()JSON_SET()JSON_REPLACE()。关于使用 *** 通配符的示例,请参阅 JSON_SEARCH() 函数的描述。

MySQL 还支持使用 to 关键字(如 $[2 to 10])来表示 JSON 数组的子集,以及使用 last 关键字作为数组右侧元素的同义词。请参阅 Searching and Modifying JSON Values,了解更多信息和示例。

可以使用=<<=>>=<>!=<=>操作符来比较JSON值。

以下比较操作符和函数当前不支持与JSON值的比较:

对上述比较操作符和函数的解决方法是将JSON值转换为native MySQL数字或字符串数据类型,以便它们具有consistent non-JSON scalar type。

JSON 值的比较发生在两个级别上。第一个级别的比较是基于被比较值的 JSON 类型。如果类型不同,比较结果将由哪个类型具有更高的优先级决定。如果两个值具有相同的 JSON 类型,则会发生第二级别的比较,使用类型特定的规则。

以下列表显示了 JSON 类型的优先顺序,从最高到最低。 (类型名称来自 JSON_TYPE() 函数。) 在同一行上显示的类型具有相同的优先级。任何值,拥有在列表中早期列出的 JSON 类型,相比于拥有在列表中晚期列出的 JSON 类型具有更高的优先级。

BLOB
BIT
OPAQUE
DATETIME
TIME
DATE
BOOLEAN
ARRAY
OBJECT
STRING
INTEGER, DOUBLE
NULL

对于具有相同优先级的 JSON 值,比较规则是类型特定的:

  • BLOB

    将比较两个值的前 N 个字节,其中 N 是较短值的字节数。如果两个值的前 N 个字节相同,较短值将被排序在较长值之前。

  • BIT

    BLOB 类型相同的规则。

  • OPAQUE

    BLOB 类型相同的规则。OPAQUE 值是未被分类为其他类型的值。

  • DATETIME

    时间值的顺序是根据它们所代表的时间点来确定的。如果两个值来自MySQL的DATETIMETIMESTAMP类型,分别表示同一时间点,则它们相等。

  • TIME

    两个时间值中较小的那个被排序在前面。

  • DATE

    较早的日期被排序在前面。

  • ARRAY

    两个JSON数组相等,如果它们具有相同的长度,并且对应位置的值相等。

    如果数组不相等,则其顺序由第一个不同元素的位置确定。拥有较小值的数组被排序在前面。如果较短数组中的所有值都等于对应长数组中的值,那么较短数组被排序在前面。

    示例:

    [] < ["a"] < ["ab"] < ["ab", "cd", "ef"] < ["ab", "ef"]
  • BOOLEAN

    JSON false字面量小于JSON true字面量。

  • OBJECT

    两个JSON对象相等,如果它们具有相同的键集,并且每个键都在两个对象中具有相同的值。

    示例:

    {"a": 1, "b": 2} = {"b": 2, "a": 1}

    不相等的两个对象的顺序是未指定但确定的。

  • STRING

    字符串按utf8mb4表示形式的前N个字节进行字典顺序比较,where N是较短字符串的长度。如果前N个字节相同,则较短字符串被认为小于较长字符串。

    示例:

    "a" < "ab" < "b" < "bc"

    这种顺序与使用collation utf8mb4_bin的SQL字符串顺序相等。由于utf8mb4_bin是二进制排序,因此JSON值的比较是区分大小写的:

    "A" < "a"
  • INTEGERDOUBLE

    JSON值可以包含精确值数字和近似值数字。关于这些类型数字的总体讨论,请见第11.1.2节,“数字字面量”

    native MySQL数值类型的比较规则在第14.3节,“表达式求值中的类型转换”中讨论,但JSON值中的数字比较规则有一些不同:

    • 在使用native MySQLINTDOUBLE数值类型的两个列之间进行比较时,知道所有比较都涉及到整数和双精度浮点数,因此将整数转换为双精度浮点数,以便于所有行。也就是说,精确值数字被转换为近似值数字。

    • 另一方面,如果查询比较两个JSON列,其中包含数字,那么无法提前知道这些数字是否是整数或双精度浮点数。为了在所有行中提供最一致的行为,MySQL将近似值数字转换为精确值数字。结果的排序是一致的,不会失去精确值数字的精度。例如,给定标量9223372036854775805、9223372036854775806、9223372036854775807和9.223372036854776e18,顺序如下:

      9223372036854775805 < 9223372036854775806 < 9223372036854775807
      < 9.223372036854776e18 = 9223372036854776000 < 9223372036854776001

    如果使用非JSON数值比较规则,则可能会出现不一致的排序。MySQL通常的数字比较规则为这些顺序:

    • 整数比较:

      9223372036854775805 < 9223372036854775806 < 9223372036854775807

      (对9.223372036854776e18无定义)

    • 双精度浮点数比较:

      9223372036854775805 = 9223372036854775806 = 9223372036854775807 = 9.223372036854776e18

对于任何JSON值与SQLNULL的比较结果为UNKNOWN

对于JSON和非JSON值之间的比较,非JSON值将根据以下表格规则转换为JSON,然后按照之前描述的方式进行比较。

以下表格总结了 MySQL 在将 JSON 值转换为其他类型值时遵循的规则:

表13.3 JSON 转换规则

other type CAST(other type AS JSON) CAST(JSON AS other type)
JSON
utf8 character type (utf8mb4, utf8mb3, ascii) 将字符串解析为 JSON 值。 将 JSON 值序列化为 utf8mb4 字符串。
Other character types 其他字符编码隐式转换为 utf8mb4 并按该字符类型的描述处理。 将 JSON 值序列化为 utf8mb4 字符串,然后将其转换为其他字符编码。结果可能无意义。
NULL 结果是一个 NULL 值类型的 JSON 值。
Geometry types 将几何值转换为 JSON 文档,通过调用ST_AsGeoJSON() 非法操作。解决方案:将CAST(json_val AS CHAR)的结果传递给ST_GeomFromGeoJSON()
All other types 结果是一个包含单个标量值的 JSON 文档。 如果 JSON 文档包含目标类型的单个标量值,并且该标量值可以被转换为目标类型,则成功。否则,返回 NULL 并产生警告。

ORDER BYGROUP BY 对 JSON 值的工作原理如下:

  • 标量 JSON 值的排序规则与前面讨论的一致。

  • 对于升序排序,SQL NULL 将在所有 JSON 值之前排序,包括 JSON null 字面量;对于降序排序,SQL NULL 将在所有 JSON 值之后排序,包括 JSON null 字面量。

  • JSON 值的排序键由系统变量 max_sort_length 确定,因此只在前 max_sort_length 字节不同的键将被视为相等。

  • 当前不支持非标量值的排序,并且会出现警告。

对排序有益的是,可以将 JSON 标量强制转换为 MySQL 的其他native类型。例如,如果一个名为 jdoc 的列包含 JSON 对象,其中的一个成员是具有非负值的 id 键,可以使用以下表达式对 id 值进行排序:

ORDER BY CAST(JSON_EXTRACT(jdoc, '$.id') AS UNSIGNED)

如果在 ORDER BY 中出现了生成列定义,以相同的表达式作为查询执行计划,则 MySQL 优化器会识别出这个情况,并考虑使用索引。见第10.3.11节,“Optimizer Use of Generated Column Indexes”

对于JSON值的聚合,SQLNULL值将被忽略,就像对其他数据类型一样。非NULL值将被转换为数字类型并聚合,除非是MIN()MAX()GROUP_CONCAT()。将JSON值转换为数字类型应该能够产生有意义的结果,对于numeric scalar JSON值来说,可能会出现截断和精度损失的问题。其他JSON值的转换为数字类型可能不会产生有意义的结果。