本部分包含有关将 JSON 数据转换为表格数据的 JSON 函数的信息。MySQL 8.0 支持这样一种功能,JSON_TABLE()
.
JSON_TABLE(
expr
,
path
COLUMNS
(column_list
) [AS]
alias
)
从 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
expr
:这是一个返回 JSON 数据的表达式。这可以是常量 ( '{"a":1}'
)、列( ,在子句之前指定
的t1.json_data
给定表
)或函数调用 ( )。
t1
JSON_TABLE()
FROM
JSON_EXTRACT(t1.json_data,'$.post.comments')
path
:JSON路径表达式,应用于数据源。我们将匹配路径的 JSON 值称为行源;这用于生成一行关系数据。该COLUMNS
子句评估行源,在行源中查找特定的 JSON 值,并将这些 JSON 值作为关系数据行的各个列中的 SQL 值返回。
alias
是必需
的。适用表别名的常用规则(请参阅第 9.2 节,“模式对象名称”)。
从 MySQL 8.0.27 开始,此函数以不区分大小写的方式比较列名。
JSON_TABLE()
支持四种类型的列,如下表所述:
:此类型枚举name
FOR ORDINALITYCOLUMNS
子句中的行;column namedname
是一个计数器,类型为UNSIGNED INT
,初始值为1。这相当于AUTO_INCREMENT
在CREATE TABLE
语句中指定一列,可以用于区分NESTED [PATH]
子句生成的多行具有相同值的父行。
:这种类型的列用于提取指定的值name
type
PATHstring_path
[on_empty
] [on_error
]string_path
。type
是 MySQL 标量数据类型(也就是说,它不能是对象或数组)。JSON_TABLE()
将数据提取为 JSON,然后使用适用于 MySQL 中 JSON 数据的常规自动类型转换将其强制转换为列类型。缺失值会触发on_empty
子句。保存对象或数组会触发可选on error
子句;当从保存为 JSON 的值到表列的强制转换过程中发生错误时,也会发生这种情况,例如尝试将字符串保存'asd'
到整数列。
:如果 指定的位置存在任何数据,则此列返回 1,否则返回name
type
EXISTS PATHpath
path
0。type
可以是任何有效的 MySQL 数据类型,但通常应指定为INT
.NESTED [PATH]
:这会将 JSON 数据中的嵌套对象或数组与来自父对象或数组的 JSON 值展平为一行。使用多个path
COLUMNS (column_list
)PATH
选项允许将 JSON 值从多个嵌套级别投影到单个行中。是相对于的
path
父路径行路径JSON_TABLE()
,或者在NESTED [PATH]
嵌套路径的情况下父子句的路径。
on empty
,如果指定,确定
JSON_TABLE()
在数据丢失的情况下做什么(取决于类型)。NESTED PATH
当子句没有匹配项并NULL
为其生成补充行时,也会在子句中的列上触发此子句。on empty
采用以下值之一:
NULL ON EMPTY
:该列设置为NULL
;这是默认行为。DEFAULT
:提供json_string
ON EMPTYjson_string
的被解析为JSON,只要它是有效的,并存储而不是缺失值。列类型规则也适用于默认值。ERROR ON EMPTY
: 抛出错误。
如果使用,on_error
则采用以下值之一以及相应的结果,如下所示:
NULL ON ERROR
:该列设置为NULL
;这是默认行为。DEFAULT
:json string
ON ERRORjson_string
被解析为 JSON(前提是它有效)并存储而不是对象或数组。ERROR ON ERROR
: 抛出错误。
NULL ON ERROR
在 MySQL 8.0.20 之前,如果指定或
DEFAULT ... ON ERROR
暗示
发生类型转换错误,则会引发警告。在 MySQL 8.0.20 及更高版本中,情况不再如此。(缺陷号 30628330)
以前,可以按任一顺序指定ON EMPTY
和ON ERROR
子句。这与 SQL 标准背道而驰,SQL 标准规定ON
EMPTY
,如果指定,则必须在任何ON
ERROR
子句之前。为此,从 MySQL 8.0.20 开始,不推荐使用ON ERROR
before指定ON
EMPTY
;尝试这样做会导致服务器发出警告。期望在未来版本的 MySQL 中删除对非标准语法的支持。
当保存到列的值被截断时,例如在列中保存 3.14159,将独立于任何选项DECIMAL(10,1)
发出警告。ON
ERROR
当多个值在单个语句中被截断时,警告只会发出一次。
在 MySQL 8.0.21 之前,当传递给此函数的表达式和路径解析为 JSON null 时,会JSON_TABLE()
引发错误。在 MySQL 8.0.21 及更高版本中,它会
NULL
根据 SQL 标准在这种情况下返回 SQL,如下所示(错误 #31345503,错误 #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)
以下查询演示了ON
EMPTY
and的使用ON ERROR
。对于 path对应的行{"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 路径表达式的有效性;任一类型的无效表达式都会导致错误。
path
前面
关键字
的每个匹配都COLUMNS
映射到结果表中的单独一行。例如,以下查询给出此处显示的结果:
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"
仅传递对应于该匹配项中的键x
和的值。y
有关详细信息,请参阅
JSON 路径语法。
NESTED PATH
(或简单地
NESTED
;是可选的)为它所属PATH
的子句中的每个匹配项生成一组记录
。COLUMNS
如果没有匹配项,则嵌套路径的所有列都设置为
NULL
。这实现了最顶层子句和 之间的外部连接NESTED [PATH]
。可以通过在子句中应用合适的条件来模拟内部联接
WHERE
,如下所示:
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 |
+------+------+
NESTED [PATH]
同级嵌套路径——即同一子句中的
两个或多个实例
COLUMNS
——一个接一个地处理,一次一个。当一个嵌套路径生成记录时,任何同级嵌套路径表达式的列都设置为
NULL
。这意味着单个包含子句中单个匹配项的记录总数
是修饰符COLUMNS
生成的所有记录的总和,而不是其乘积NESTED [PATH]
,如下所示:
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 |
+------+------+------+
列FOR ORDINALITY
枚举子句产生的记录COLUMNS
,可用于区分嵌套路径的父记录,尤其是当父记录中的值相同时,如下所示:
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 |
+---------+---------+---------+------+-------+
源文档包含两个元素的数组;这些元素中的每一个都产生两行。apath
和的值
bpath
在整个结果集中都是相同的;这意味着它们不能用于确定lpath
值是否来自相同或不同的父母。该ord
列的值与等于 1 的记录集保持相同
top_ord
,因此这两个值来自单个对象。其余两个值来自不同的对象,因为它们在
ord
列中具有不同的值。
通常,您不能连接依赖于同一FROM
子句中前面表的列的派生表。根据 SQL 标准,MySQL 对表函数进行了例外处理;这些被认为是横向派生表,即使在尚不支持该
LATERAL
关键字的 MySQL 版本(8.0.13 及更早版本)中也是如此。在LATERAL
支持的版本(8.0.14 及更高版本)中,它是隐式的,因此之前也是不允许
JSON_TABLE()
的,也是根据标准。
假设您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_TABLE()
充当派生表,同时它引用先前引用的表中的列:
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;
尝试将LATERAL
关键字与此查询一起使用会引发ER_PARSE_ERROR
。