# 12.18.6 JSON 表函数

本节包含有关将 JSON 数据转换为表格数据的 JSON 函数的信息。MySQL 8.0 支持这样一种功能,JSON_TABLE().

JSON_TABLE(*表达式*, *小路* 列 (*列列表*) [作为] *别名*)

从 JSON 文档中提取数据并将其作为具有指定列的关系表返回。此函数的完整语法如下所示:

JSON_TABLE(
    expr,
    path COLUMNS (column_list)
)   [AS] alias

column_list:
    column[, column][, ...]

column:
    name FOR ORDINALITY
    |  name type PATH string path [on_empty] [on_error]
    |  name type EXISTS PATH string path
    |  NESTED [PATH] path COLUMNS (column_list)

on_empty:
    {NULL | DEFAULT json_string | ERROR} ON EMPTY

on_error:
    {NULL | DEFAULT json_string | ERROR} ON ERROR

表达式:这是一个返回 JSON 数据的表达式。这可以是一个常数 ('{"a":1}'), 一列 (t1.json_data,给定表t1之前指定JSON_TABLE()在里面子句),或函数调用(JSON_EXTRACT(t1.json_data,'$.post.comments'))。

路径:应用于数据源的JSON路径表达式。我们将与路径匹配的JSON值称为行源; 这用于生成一行关系数据。这个子句计算行源,在行源中查找特定的JSON值,并将这些JSON值作为SQL值返回到一行关系数据的各个列中。

这个*别名*是必需的。表别名的常规规则适用(请参见第9.2节,“模式对象名称”).

从MySQL 8.0.27开始,该函数以不区分大小写的方式比较列名。

JSON_表()支持以下列表中描述的四种类型的列:

  1. *名称*为了平凡:此类型枚举中的行条款名为*名称*是一个类型为无符号整数,其初始值为1。这相当于将列指定为自动增量在一个创建表格语句,并可用于区分由嵌套的[路径]条款

  2. *名称* *类型*路径*字符串路径* [*空荡荡的*] [*论错误*]:此类型的列用于提取*字符串路径*. *类型是MySQL标量数据类型(也就是说,它不能是对象或数组)。JSON_表()将数据提取为JSON,然后使用MySQL中应用于JSON数据的常规自动类型转换将其强制为列类型。缺少值会触发空荡荡的条款保存对象或数组会触发可选的论错误*条款在将保存为JSON的值强制转换为表列(例如试图保存字符串)期间发生错误时,也会发生这种情况“自闭症”到整数列。

  3. *名称* *类型*存在路径*路径*:如果在指定的位置存在任何数据,则此列返回1*路径,否则为0。类型*可以是任何有效的MySQL数据类型,但通常应指定为国际的.

  4. 嵌套的[路径]*路径*纵队(*列列表*):这会将JSON数据中的嵌套对象或数组与父对象或数组中的JSON值一起放在一行中。使用多个路径选项允许将JSON值从多个嵌套级别投影到一行中。

    这个*路径*相对于行路径的父路径JSON_表(),或父对象的路径嵌套的[路径]在嵌套路径的情况下。

空荡荡的,如果指定,则确定JSON_表()在数据丢失时执行(取决于类型)。此子句也会在中的列上触发嵌套路径当后者没有匹配项且无效的为它生成了一行。*空荡荡的*采用以下值之一:

  • 空时为空:该列设置为无效的; 这是默认行为。

  • 违约*json_字符串*空荡荡的:提供的*json_字符串*被解析为JSON,只要它是有效的,并存储而不是缺少的值。列类型规则也适用于默认值。

  • 空时出错:抛出一个错误。

    如果使用,*论错误*获取以下值之一,并获得如下所示的相应结果:

  • 错误时为空:该列设置为无效的; 这是默认行为。

  • 违约*json字符串*论错误当前位置*json_字符串*被解析为JSON(前提是它有效)并存储,而不是对象或数组。

  • 一个接一个的错误:抛出一个错误。

    在MySQL 8.0.20之前,如果出现类型转换错误,则会引发警告错误时为空违约论错误被指定或暗示。在MySQL 8.0.20及更高版本中,情况不再如此。(错误#30628330)

    以前,可以指定空荡荡的论错误按任意顺序排列的子句。这与SQL标准背道而驰,后者规定空荡荡的,如果指定,则必须在任何论错误条款因此,从MySQL 8.0.20开始论错误之前空荡荡的不赞成;尝试这样做会导致服务器发出警告。在MySQL的未来版本中,对非标准语法的支持将被删除。

    当保存到列中的值被截断时,例如将3.14159保存到十进制(10,1)列中,将独立于任何论错误选项当在一条语句中截断多个值时,只发出一次警告。

    在MySQL 8.0.21之前,当传递给该函数的表达式和路径解析为JSON null时,JSON_表()提出了一个错误。在MySQL 8.0.21及更高版本中,它返回SQL无效的在这种情况下,根据SQL标准,如下所示(Bug#31345503,Bug#99557):

mysql> SELECT *
    ->   FROM
    ->     JSON_TABLE(
    ->       '[ {"c1": null} ]',
    ->       '$[*]' COLUMNS( c1 INT PATH '$.c1' ERROR ON ERROR )
    ->     ) as jt;
+------+
| c1   |
+------+
| NULL |
+------+
1 row in set (0.00 sec)

下面的查询演示了空荡荡的论错误.对应于{“b”:1}这条路是空的“$a”,并试图保存[1,2]当标量产生错误时;这些行在显示的输出中高亮显示。

mysql> SELECT *
    -> FROM
    ->   JSON_TABLE(
    ->     '[{"a":"3"},{"a":2},{"b":1},{"a":0},{"a":[1,2]}]',
    ->     "$[*]"
    ->     COLUMNS(
    ->       rowid FOR ORDINALITY,
    ->       ac VARCHAR(100) PATH "$.a" DEFAULT '111' ON EMPTY DEFAULT '999' ON ERROR,
    ->       aj JSON PATH "$.a" DEFAULT '{"x": 333}' ON EMPTY,
    ->       bx INT EXISTS PATH "$.b"
    ->     )
    ->   ) AS tt;

+-------+------+------------+------+
| rowid | ac   | aj         | bx   |
+-------+------+------------+------+
|     1 | 3    | "3"        |    0 |
|     2 | 2    | 2          |    0 |
|     3 | 111  | {"x": 333} |    1 |
|     4 | 0    | 0          |    0 |
|     5 | 999  | [1, 2]     |    0 |
+-------+------+------------+------+
5 rows in set (0.00 sec)

列名受制于管理表列名的常规规则和限制。看见第9.2节,“模式对象名称”.

检查所有JSON和JSON路径表达式的有效性;任何一种类型的无效表达式都会导致错误。

每一场比赛*路径*在关键字映射到结果表中的单个行。例如,以下查询给出的结果如下所示:

mysql> SELECT *
    -> FROM
    ->   JSON_TABLE(
    ->     '[{"x":2,"y":"8"},{"x":"3","y":"7"},{"x":"4","y":6}]',
    ->     "$[*]" COLUMNS(
    ->       xval VARCHAR(100) PATH "$.x",
    ->       yval VARCHAR(100) PATH "$.y"
    ->     )
    ->   ) AS  jt1;

+------+------+
| xval | yval |
+------+------+
| 2    | 8    |
| 3    | 7    |
| 4    | 6    |
+------+------+

表情"$[*]"匹配数组的每个元素。可以通过修改路径来过滤结果中的行。例如,使用"$[1]"将提取限制为用作源的JSON数组的第二个元素,如下所示:

mysql> SELECT *
    -> FROM
    ->   JSON_TABLE(
    ->     '[{"x":2,"y":"8"},{"x":"3","y":"7"},{"x":"4","y":6}]',
    ->     "$[1]" COLUMNS(
    ->       xval VARCHAR(100) PATH "$.x",
    ->       yval VARCHAR(100) PATH "$.y"
    ->     )
    ->   ) AS  jt1;

+------+------+
| xval | yval |
+------+------+
| 3    | 7    |
+------+------+

在列定义中,"$"将整个匹配传递给列;“$.x”“$y”仅传递与键对应的值十、y在那场比赛中。有关更多信息,请参阅JSON路径语法.

嵌套路径(或者干脆嵌套的; 路径是可选的)为中的每个匹配生成一组记录它所属的条款。如果不匹配,嵌套路径的所有列都将设置为无效的。这将在最顶端的子句和嵌套的[路径]。可以通过在中应用适当的条件来模拟内部联接哪里子句,如下所示:

mysql> SELECT *
    -> FROM
    ->   JSON_TABLE(
    ->     '[ {"a": 1, "b": [11,111]}, {"a": 2, "b": [22,222]}, {"a":3}]',
    ->     '$[*]' COLUMNS(
    ->             a INT PATH '$.a',
    ->             NESTED PATH '$.b[*]' COLUMNS (b INT PATH '$')
    ->            )
    ->    ) AS jt
    -> WHERE b IS NOT NULL;

+------+------+
| a    | b    |
+------+------+
|    1 |   11 |
|    1 |  111 |
|    2 |   22 |
|    2 |  222 |
+------+------+

同级嵌套路径,即两个或多个嵌套的[路径]同时子句一次一个接一个地处理。当一个嵌套路径生成记录时,任何同级嵌套路径表达式的列都设置为无效的。这意味着在一个包含子句是生成的所有记录的总和,而不是乘积嵌套的[路径]修饰符,如下所示:

mysql> SELECT *
    -> FROM
    ->   JSON_TABLE(
    ->     '[{"a": 1, "b": [11,111]}, {"a": 2, "b": [22,222]}]',
    ->     '$[*]' COLUMNS(
    ->         a INT PATH '$.a',
    ->         NESTED PATH '$.b[*]' COLUMNS (b1 INT PATH '$'),
    ->         NESTED PATH '$.b[*]' COLUMNS (b2 INT PATH '$')
    ->     )
    -> ) AS jt;

+------+------+------+
| a    | b1   | b2   |
+------+------+------+
|    1 |   11 | NULL |
|    1 |  111 | NULL |
|    1 | NULL |   11 |
|    1 | NULL |  111 |
|    2 |   22 | NULL |
|    2 |  222 | NULL |
|    2 | NULL |   22 |
|    2 | NULL |  222 |
+------+------+------+

A.为了平凡列枚举由子句,并可用于区分嵌套路径的父记录,尤其是当父记录中的值相同时,如图所示:

mysql> SELECT *
    -> FROM
    ->   JSON_TABLE(
    ->     '[{"a": "a_val",
    '>       "b": [{"c": "c_val", "l": [1,2]}]},
    '>     {"a": "a_val",
    '>       "b": [{"c": "c_val","l": [11]}, {"c": "c_val", "l": [22]}]}]',
    ->     '$[*]' COLUMNS(
    ->       top_ord FOR ORDINALITY,
    ->       apath VARCHAR(10) PATH '$.a',
    ->       NESTED PATH '$.b[*]' COLUMNS (
    ->         bpath VARCHAR(10) PATH '$.c',
    ->         ord FOR ORDINALITY,
    ->         NESTED PATH '$.l[*]' COLUMNS (lpath varchar(10) PATH '$')
    ->         )
    ->     )
    -> ) as jt;

+---------+---------+---------+------+-------+
| top_ord | apath   | bpath   | ord  | lpath |
+---------+---------+---------+------+-------+
|       1 |  a_val  |  c_val  |    1 | 1     |
|       1 |  a_val  |  c_val  |    1 | 2     |
|       2 |  a_val  |  c_val  |    1 | 11    |
|       2 |  a_val  |  c_val  |    2 | 22    |
+---------+---------+---------+------+-------+

源文档包含两个元素的数组;每个元素产生两行。价值观阿帕斯B路径在整个结果集中是相同的;这意味着它们不能用于确定lpath价值观来自相同或不同的父母。价值奥德列与具有头条等于1,所以这两个值来自单个对象。其余两个值来自不同的对象,因为它们在奥德

通常,不能联接依赖于同一个表中前面表的列的派生表从…起条款根据SQL标准,MySQL对表函数例外;这些表被认为是横向派生表,即使在MySQL版本中,这些表还不支持侧面的关键词(8.0.13及更早版本)。在哪个版本侧面的是受支持的(8.0.14及更高版本),它是隐式的,因此以前是不允许的JSON_表(),也符合标准。

假设你有一张桌子t1使用以下语句创建和填充:

CREATE TABLE t1 (c1 INT, c2 CHAR(1), c3 JSON);

INSERT INTO t1 () VALUES
	ROW(1, 'z', JSON_OBJECT('a', 23, 'b', 27, 'c', 1)),
	ROW(1, 'y', JSON_OBJECT('a', 44, 'b', 22, 'c', 11)),
	ROW(2, 'x', JSON_OBJECT('b', 1, 'c', 15)),
	ROW(3, 'w', JSON_OBJECT('a', 5, 'b', 6, 'c', 7)),
	ROW(5, 'v', JSON_OBJECT('a', 123, 'c', 1111))
;

然后可以执行连接,比如这个连接JSON_表()充当派生表,同时引用以前引用的表中的列:

SELECT c1, c2, JSON_EXTRACT(c3, '$.*')
FROM t1 AS m
JOIN
JSON_TABLE(
  m.c3,
  '$.*'
  COLUMNS(
    at VARCHAR(10) PATH '$.a' DEFAULT '1' ON EMPTY,
    bt VARCHAR(10) PATH '$.b' DEFAULT '2' ON EMPTY,
    ct VARCHAR(10) PATH '$.c' DEFAULT '3' ON EMPTY
  )
) AS tt
ON m.c1 > tt.at;

试图使用侧面的带有此查询的关键字将引发ER_解析_错误 (opens new window).