“MySQL 数据类型:JSON 数据类型”的版本间差异
(建立内容为“category:MySQL”的新页面) |
无编辑摘要 |
||
第1行: | 第1行: | ||
[[category:MySQL]] | [[category:MySQL]] | ||
== 关于:JSON 数据类型 == | |||
从 MySQL 5.7.8 开始,MySQL 支持由RFC 7159定义的本机JSON数据类型,该类型可有效访问 JSON(JavaScript 对象表示法)文档中的数据。 | |||
与将 JSON 格式的字符串存储在字符串列中相比,JSON数据类型具有以下优点: | |||
# '''自动验证'''存储在JSON列中的 JSON 文档。无效的文档会产生错误。 | |||
# '''优化的存储格式'''。存储在JSON列中的 JSON 文档将转换为内部格式,从而可以快速读取文档元素。当服务器稍后必须读取以该二进制格式存储的 JSON 值时,则无需从文本 table 示形式解析该值。二进制格式的结构使服务器可以直接通过键或数组索引查找子对象或嵌套值,而无需读取文档中它们之前或之后的所有值。 | |||
注意: | |||
* 存储JSON文档所需的空间与“LONGBLOB”【???】或“LONGTEXT”【???】大致相同;存储在JSON列中的任何 JSON 文档的大小都限于“'''max_allowed_packet'''”系统变量的值。 | |||
* JSON列不能具有非NULL的默认值。 | |||
* 除JSON数据类型外,还有一组 SQL 函数可用于启用对 JSON 值的操作,例如创建,操作和搜索。 | |||
* MySQL 还提供了一组用于处理 '''GeoJSON''' 值的空间函数。【???】 | |||
* JSON列与其他二进制类型的列一样,'''不会直接构建索引''';而是相反,您可以在生成的列上创建索引,该索引从JSON列中提取标量值。 | |||
* MySQL 优化器还在与 JSON 表达式匹配的虚拟列上寻找兼容的索引。 | |||
* MySQL NDB Cluster 7.5(7.5.2 及更高版本)支持JSON列和 MySQL JSON 函数,包括在从JSON列生成的列上创建索引,以作为无法对JSON列构建索引的解决方法。每个NDB 表最多支持 3 个JSON列。 | |||
== 创建 JSON 值 == | |||
创建 JSON 值: | |||
# JSON 数组:包含用逗号分隔并包含在“'''['''”和“''']'''”字符内的值的列表: | |||
#: <syntaxhighlight lang="xml"> | |||
["abc", 10, null, true, false] | |||
</syntaxhighlight> | |||
# JSON 对象:包含一组键值对,以逗号分隔并包含在“'''{'''”和“'''}'''”字符内: | |||
#: <syntaxhighlight lang="xml"> | |||
{"k1": "value", "k2": 10} | |||
</syntaxhighlight> | |||
#* JSON 数组和对象可以包含:字符串、数字的标量值,JSON 空字面量 或 JSON 布尔值“true”或“false”。 | |||
#* JSON 对象中的键必须是字符串。 | |||
#* 还允许使用时间(date, time 或 datetime)标量值: | |||
#*: <syntaxhighlight lang="xml"> | |||
["12:18:29.000000", "2015-07-29", "2015-07-29 12:18:29.000000"] | |||
</syntaxhighlight> | |||
# JSON 数组元素和 JSON 对象键值中允许嵌套: | |||
#: <syntaxhighlight lang="xml" inline> | |||
[99, {"id": "HK500", "cost": 75.99}, ["hot", "cold"]] | |||
{"k1": "value", "k2": [10, 20]} | |||
</syntaxhighlight> | |||
# 还可以从 MySQL 提供的许多'''函数'''中获取 JSON 值: | |||
## “'''JSON_ARRAY([val[, val] ...])'''”:计算(可能为空)值列表,并返回包含这些值的 JSON 数组。 | |||
##: <syntaxhighlight lang="xml"> | |||
mysql> SELECT JSON_ARRAY(1, "abc", NULL, TRUE, CURTIME()); | |||
+---------------------------------------------+ | |||
| JSON_ARRAY(1, "abc", NULL, TRUE, CURTIME()) | | |||
+---------------------------------------------+ | |||
| [1, "abc", null, true, "11:30:24.000000"] | | |||
+---------------------------------------------+ | |||
</syntaxhighlight> | |||
## “'''JSON_OBJECT([key, val[, key, val] ...])'''”:评估键值对的列表(可能为空),并返回包含这些对的 JSON 对象。如果任何键名称为“NULL”或参数个数为奇数,则会发生错误。 | |||
##: <syntaxhighlight lang="xml"> | |||
mysql> SELECT JSON_OBJECT('id', 87, 'name', 'carrot'); | |||
+-----------------------------------------+ | |||
| JSON_OBJECT('id', 87, 'name', 'carrot') | | |||
+-----------------------------------------+ | |||
| {"id": 87, "name": "carrot"} | | |||
+-----------------------------------------+ | |||
</syntaxhighlight> | |||
## “'''JSON_MERGE()'''”接收两个或多个 JSON 文档并返回合并的结果: | |||
##: <syntaxhighlight lang="xml"> | |||
mysql> SELECT JSON_MERGE('["a", 1]', '{"key": "value"}'); | |||
+--------------------------------------------+ | |||
| JSON_MERGE('["a", 1]', '{"key": "value"}') | | |||
+--------------------------------------------+ | |||
| ["a", 1, {"key": "value"}] | | |||
+--------------------------------------------+ | |||
</syntaxhighlight> | |||
## “'''JSON_QUOTE(string)'''”:通过“1、将字符串用双引号引起来,2、并转义内部引号和其他字符”来将字符串引为 JSON 值,然后将结果作为'''utf8mb4'''字符串返回。如果参数为 NULL,则返回 NULL。 | |||
##: <syntaxhighlight lang="xml"> | |||
mysql> SELECT JSON_QUOTE('null'), JSON_QUOTE('"null"'); | |||
+--------------------+----------------------+ | |||
| JSON_QUOTE('null') | JSON_QUOTE('"null"') | | |||
+--------------------+----------------------+ | |||
| "null" | "\"null\"" | | |||
+--------------------+----------------------+ | |||
mysql> SELECT JSON_QUOTE('[1, 2, 3]'); | |||
+-------------------------+ | |||
| JSON_QUOTE('[1, 2, 3]') | | |||
+-------------------------+ | |||
| "[1, 2, 3]" | | |||
+-------------------------+ | |||
</syntaxhighlight> | |||
# 使用“'''CAST(值为 JSON)'''”将其他类型的值'''强制转换'''为JSON类型。【见下面:“在 JSON 和非 JSON 值之间转换”】 | |||
在 MySQL 中,JSON 值被编写为字符串。 MySQL 会解析在需要 JSON 值的上下文中使用的任何字符串,如果该字符串作为 JSON 无效,则会产生错误。这些上下文包括将值插入具有JSON数据类型的列中,以及将参数传递给需要 JSON 值的函数,如下所示示例说明: | |||
# 如果值是有效的 JSON 值,尝试将值插入JSON列会成功,但如果不是,则尝试失败: | |||
#: <syntaxhighlight lang="xml"> | |||
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,'. | |||
</syntaxhighlight> | |||
# “'''JSON_TYPE()'''”函数需要一个 JSON 参数,并尝试将其解析为 JSON 值。如果有效,则返回值的 JSON 类型,否则返回错误: | |||
#: <syntaxhighlight lang="xml"> | |||
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. | |||
</syntaxhighlight> | |||
注意: | |||
* MySQL 使用'''utf8mb4'''字符集和'''utf8mb4_bin'''归类处理在 JSON 上下文中使用的字符串。其他字符集中的字符串将根据需要转换为 utf8mb4。(对于'''ascii'''或'''utf8'''字符集中的字符串,不需要转换,因为'''ascii和utf8是utf8mb4的子集'''【?】) | |||
* 可以'''将 JSON 值分配给用户定义的变量''': | |||
*: <syntaxhighlight lang="xml"> | |||
mysql> SET @j = JSON_OBJECT('key', 'value'); | |||
mysql> SELECT @j; | |||
+------------------+ | |||
| @j | | |||
+------------------+ | |||
| {"key": "value"} | | |||
+------------------+ | |||
</syntaxhighlight> | |||
*: '''但是,用户定义的变量不能为JSON数据类型''',因此尽管上一示例中的“@j”看起来像 JSON 值,并且具有与 JSON 值相同的字符集和排序规则,但并非JSON数据类型。而是将“JSON_OBJECT()”的结果分配给变量后,将其转换为字符串。 | |||
* 通过转换 JSON 值产生的字符串的'''字符集为utf8mb4''','''排序规则为utf8mb4_bin''': | |||
*: <syntaxhighlight lang="xml"> | |||
mysql> SELECT CHARSET(@j), COLLATION(@j); | |||
+-------------+---------------+ | |||
| CHARSET(@j) | COLLATION(@j) | | |||
+-------------+---------------+ | |||
| utf8mb4 | utf8mb4_bin | | |||
+-------------+---------------+ | |||
</syntaxhighlight> | |||
* 由于“utf8mb4_bin”是二进制排序规则,因此 JSON 值的比较区分大小写。 | |||
*: <syntaxhighlight lang="xml"> | |||
mysql> SELECT JSON_ARRAY('x') = JSON_ARRAY('X'); | |||
+-----------------------------------+ | |||
| JSON_ARRAY('x') = JSON_ARRAY('X') | | |||
+-----------------------------------+ | |||
| 0 | | |||
+-----------------------------------+ | |||
</syntaxhighlight> | |||
*: 区分大小写还适用于 '''JSON 的字面量“null”,“true”和“false”,它们必须始终以小写形式编写''': | |||
*: <syntaxhighlight lang="xml"> | |||
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'. | |||
</syntaxhighlight> | |||
*: JSON 字面量的区分大小写不同于 SQL的字面量 NULL,TRUE 和 FALSE,区分大小写: | |||
*: <syntaxhighlight lang="xml"> | |||
mysql> SELECT ISNULL(null), ISNULL(Null), ISNULL(NULL); | |||
+--------------+--------------+--------------+ | |||
| ISNULL(null) | ISNULL(Null) | ISNULL(NULL) | | |||
+--------------+--------------+--------------+ | |||
| 1 | 1 | 1 | | |||
+--------------+--------------+--------------+ | |||
</syntaxhighlight> | |||
* 有时可能需要或希望将引号字符(“"”或“'”)插入 JSON 文档。如: | |||
*: 要在如下 table 中插入包含特殊字符的语句作为 JSON 对象: | |||
*: <syntaxhighlight lang="xml"> | |||
mysql> CREATE TABLE facts (sentence JSON); | |||
</syntaxhighlight> | |||
*: 需要插入的语句为: | |||
*: <syntaxhighlight lang="xml"> | |||
mascot: The MySQL mascot is a dolphin named "Sakila". | |||
</syntaxhighlight> | |||
*# 方法一:使用 MySQL “'''JSON_OBJECT()'''”函数。在这种情况下,必须'''使用反斜杠对每个引号字符进行转义''': | |||
*#: <syntaxhighlight lang="xml"> | |||
mysql> INSERT INTO facts VALUES | |||
> (JSON_OBJECT("mascot", "Our mascot is a dolphin named \"Sakila\".")); | |||
</syntaxhighlight> | |||
*# 方法二:将值作为 JSON 对象字面量插入,则'''必须使用双反斜杠转义序列''': | |||
*#: <syntaxhighlight lang="xml"> | |||
mysql> INSERT INTO facts VALUES | |||
> ('{"mascot": "Our mascot is a dolphin named \\"Sakila\\"."}'); | |||
</syntaxhighlight> | |||
*#:(使用双反斜杠使 MySQL 无法执行转义序列处理,而是使它将字符串字面量传递给存储引擎进行处理。) | |||
*: 上述两种方式插入 JSON 对象后,通过执行简单的SELECT,您可以看到反斜杠出现在 JSON 列值中,如下所示: | |||
mysql> SELECT sentence FROM facts; | |||
+---------------------------------------------------------+ | |||
| sentence | | |||
+---------------------------------------------------------+ | |||
| {"mascot": "Our mascot is a dolphin named \"Sakila\"."} | | |||
+---------------------------------------------------------+ | |||
*: 要使用mascot作为'''关键字'''来查询此特定句子,可以使用'''列路径运算符'''“'''->'''”,如下所示:【这使反斜杠以及周围的引号保持完整】 | |||
*: <syntaxhighlight lang="xml"> | |||
mysql> SELECT col->"$.mascot" FROM qtest; | |||
+---------------------------------------------+ | |||
| col->"$.mascot" | | |||
+---------------------------------------------+ | |||
| "Our mascot is a dolphin named \"Sakila\"." | | |||
+---------------------------------------------+ | |||
1 row in set (0.00 sec) | |||
</syntaxhighlight> | |||
*: 要使用“mascot”作为'''键'''来显示所需的值,但不包括周围的引号或任何转义符,请使用'''内联路径运算符'''“'''->>'''”,如下所示: | |||
*: <syntaxhighlight lang="xml"> | |||
mysql> SELECT sentence->>"$.mascot" FROM facts; | |||
+-----------------------------------------+ | |||
| sentence->>"$.mascot" | | |||
+-----------------------------------------+ | |||
| Our mascot is a dolphin named "Sakila". | | |||
+-----------------------------------------+ | |||
</syntaxhighlight> | |||
** 如果启用了“'''NO_BACKSLASH_ESCAPES'''”服务器 SQL 模式,则可以使用单个反斜杠(而不是双反斜杠)来插入 JSON 对象字面量,并且保留反斜杠。如果在执行插入操作时使用“JSON_OBJECT()”函数,并且设置了此模式,则必须使用单引号和双引号,例如: | |||
**: <syntaxhighlight lang="xml"> | |||
mysql> INSERT INTO facts VALUES | |||
> (JSON_OBJECT('mascot', 'Our mascot is a dolphin named "Sakila".')); | |||
</syntaxhighlight> | |||
== JSON 值的规范化,合并和自动包装 == | |||
当解析一个字符串并发现它是一个有效的 JSON 文档时,也会对其进行规范化:具有与文档中较早发现的键重复的键的成员将被丢弃(即使值不同)。 | |||
* 重复键的这种“第一键获胜”处理与 RFC 7159 不一致。这是 MySQL 5.7 中的一个已知问题,已在 MySQL 8.0 中修复。(缺陷#86866,错误#26369555) | |||
* MySQL 还会在原始 JSON 文档中的键,值或元素之间舍弃多余的空格,并在显示时在每个逗号“,”或冒号“:”后留一个空格(或在必要时插入)。这样做是为了提高可读性。 | |||
* 为了使查找更有效率,它还会对 JSON 对象的键进行排序。 注意,此排序的结果可能会更改,并且不能保证在各个发行版中都保持一致。 | |||
示例: | |||
<syntaxhighlight lang="xml"> | |||
mysql> SELECT JSON_OBJECT('key1', 1, 'key2', 'abc', 'key1', 'def'); | |||
+------------------------------------------------------+ | |||
| JSON_OBJECT('key1', 1, 'key2', 'abc', 'key1', 'def') | | |||
+------------------------------------------------------+ | |||
| {"key1": 1, "key2": "abc"} | | |||
+------------------------------------------------------+ | |||
</syntaxhighlight> | |||
=== 合并 JSON 值 === | |||
在组合多个数组的上下文中,通过将稍后命名的数组连接到第一个数组的末尾,将数组合并为单个数组。 | |||
# “JSON_MERGE()”将其参数合并到单个数组中: | |||
#: <syntaxhighlight lang="xml"> | |||
mysql> SELECT JSON_MERGE('[1, 2]', '["a", "b"]', '[true, false]'); | |||
+-----------------------------------------------------+ | |||
| JSON_MERGE('[1, 2]', '["a", "b"]', '[true, false]') | | |||
+-----------------------------------------------------+ | |||
| [1, 2, "a", "b", true, false] | | |||
+-----------------------------------------------------+ | |||
</syntaxhighlight> | |||
#* 将值插入 JSON 列时,也会执行规范化,如下所示: | |||
#*:<syntaxhighlight lang="xml"> | |||
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": 17} | | |||
| {"x": 17} | | |||
+-----------+ | |||
</syntaxhighlight> | |||
# 合并时,多个对象将产生一个对象。如果多个对象具有相同的键,则在合并后的对象中该键的值是一个包含键值的数组: | |||
#: <syntaxhighlight lang="xml"> | |||
mysql> SELECT JSON_MERGE('{"a": 1, "b": 2}', '{"c": 3, "a": 4}'); | |||
+----------------------------------------------------+ | |||
| JSON_MERGE('{"a": 1, "b": 2}', '{"c": 3, "a": 4}') | | |||
+----------------------------------------------------+ | |||
| {"a": [1, 4], "b": 2, "c": 3} | | |||
+----------------------------------------------------+ | |||
</syntaxhighlight> | |||
# 需要数组值的上下文中使用的非数组值将自动包装:值被“'''['''”和“''']'''”字符包围,以将其转换为数组。在以下语句中,每个参数都自动包装为数组([1],[2])。然后将它们合并以生成单个结果数组: | |||
#: <syntaxhighlight lang="xml"> | |||
mysql> SELECT JSON_MERGE('1', '2'); | |||
+----------------------+ | |||
| JSON_MERGE('1', '2') | | |||
+----------------------+ | |||
| [1, 2] | | |||
+----------------------+ | |||
</syntaxhighlight> | |||
#* 通过将对象自动包装为数组并将两个数组合并,可以合并数组和对象值: | |||
#*: <syntaxhighlight lang="xml"> | |||
mysql> SELECT JSON_MERGE('[10, 20]', '{"a": "x", "b": "y"}'); | |||
+------------------------------------------------+ | |||
| JSON_MERGE('[10, 20]', '{"a": "x", "b": "y"}') | | |||
+------------------------------------------------+ | |||
| [10, 20, {"a": "x", "b": "y"}] | | |||
+------------------------------------------------+ | |||
</syntaxhighlight> | |||
== 搜索和修改 JSON 值 == | |||
=== 搜索 JSON 值 === | |||
通过 '''JSON 路径表达式'''选择 JSON 文档中的值。 | |||
示例,使用name键从 JSON 文档中提取成员的值: | |||
<syntaxhighlight lang="xml"> | |||
mysql> SELECT JSON_EXTRACT('{"id": 14, "name": "Aztalan"}', '$.name'); | |||
+---------------------------------------------------------+ | |||
| JSON_EXTRACT('{"id": 14, "name": "Aztalan"}', '$.name') | | |||
+---------------------------------------------------------+ | |||
| "Aztalan" | | |||
+---------------------------------------------------------+ | |||
</syntaxhighlight> | |||
路径语法使用前导“'''$'''”字符 table 示所考虑的 JSON 文档,还可以选择后面跟有选择器,这些选择器依次指示文档的更具体部分: | |||
# 句点后跟一个键名,使用给定的键为对象中的成员命名。'''如果路径表达式中的名称不合法(例如,名称包含空格),则必须在双引号中指定键名称。''' | |||
# “[N]”附加到 path(用于选择数组)后,将该值命名为数组中 N 位置的值。数组位置是从零开始的整数。如果 path 未选择数组值,则 path[0] 的计算结果与 path 相同: | |||
#: <syntaxhighlight lang="xml"> | |||
mysql> SELECT JSON_SET('"x"', '$[0]', 'a'); | |||
+------------------------------+ | |||
| JSON_SET('"x"', '$[0]', 'a') | | |||
+------------------------------+ | |||
| "a" | | |||
+------------------------------+ | |||
1 row in set (0.00 sec) | |||
</syntaxhighlight> | |||
# 路径可以包含“*”或“**”通配符: | |||
## “'''.[*]'''”为 JSON '''对象中'''所有成员的值。 | |||
## “'''[*]'''”计算 JSON '''数组中'''所有元素的值。 | |||
## “prefix**suffix”计算所有以命名前缀开头并以命名后缀结尾的路径。 | |||
#* 路径可能不包含序列“***”。 | |||
# 文档中不存在的路径(评估为不存在的数据)评估为 NULL。 | |||
示例1,让“$”引用具有三个元素的 JSON 数组: | |||
<syntaxhighlight lang="xml"> | |||
[3, {"a": [5, 6], "b": 10}, [99, 100]] | |||
</syntaxhighlight> | |||
则: | |||
# $[0] 等于 3。 | |||
# $[1] 等于 {"a": [5, 6], "b": 10}。 | |||
# $[2] 等于 [99, 100]。 | |||
# $[3] 的计算结果为 NULL(它指向第四个数组元素,该元素不存在)。 | |||
因为 $[1] 和 $[2] 的计算结果为非标量值,所以它们可以用作选择嵌套值的更特定路径表达式的基础。例子: | |||
# $[1].a 等于 [5, 6]。 | |||
# $[1].a[1] 等于 6。 | |||
# $[1].b 等于 10。 | |||
# $[2][0] 等于 99。 | |||
示例2,如果未加引号的键名在路径表达式中不合法,则必须用引号命名路径的组件。让“$”引用此值: | |||
<syntaxhighlight lang="xml"> | |||
{"a fish": "shark", "a bird": "sparrow"} | |||
</syntaxhighlight> | |||
# $."a fish" 等于 shark。 | |||
# $."a bird" 等于 sparrow。 | |||
示例3,使用通配符的路径求值的数组可以包含多个值: | |||
<syntaxhighlight lang="xml"> | |||
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] | | |||
+------------------------------------------------------------+ | |||
</syntaxhighlight> | |||
示例4,路径“$**.b”计算为多个路径(“$.a.b”和“$.c.b”),并生成匹配路径值的数组: | |||
<syntaxhighlight lang="xml"> | |||
mysql> SELECT JSON_EXTRACT('{"a": {"b": 1}, "c": {"b": 2}}', '$**.b'); | |||
+---------------------------------------------------------+ | |||
| JSON_EXTRACT('{"a": {"b": 1}, "c": {"b": 2}}', '$**.b') | | |||
+---------------------------------------------------------+ | |||
| [1, 2] | | |||
+---------------------------------------------------------+ | |||
</syntaxhighlight> | |||
* 在 MySQL 5.7.9 及更高版本中,您可以'''将“column->path”与 JSON 列标识符和 JSON 路径表达式一起用作“JSON_EXTRACT(column, path)”的同义词''':【列路径标识符】 | |||
*: <syntaxhighlight lang="xml"> | |||
mysql> SELECT col->"$.mascot" FROM qtest; | |||
+---------------------------------------------+ | |||
| col->"$.mascot" | | |||
+---------------------------------------------+ | |||
| "Our mascot is a dolphin named \"Sakila\"." | | |||
+---------------------------------------------+ | |||
1 row in set (0.00 sec) | |||
</syntaxhighlight> | |||
=== 修改 JSON 值:JSON_SET()、JSON_INSERT()、JSON_REPLACE()=== | |||
某些函数采用现有的 JSON 文档,以某种方式对其进行修改,然后返回生成的修改后的文档。路径表达式指示在文档中的何处进行更改。例如,'''JSON_SET()''','''JSON_INSERT()'''和'''JSON_REPLACE()'''函数各自获取一个 JSON 文档,外加一个或多个路径/值对,它们描述了在何处修改文档以及要使用的值。 | |||
考虑以下文档: | |||
<syntaxhighlight lang="xml"> | |||
mysql> SET @j = '["a", {"b": [true, false]}, [10, 20]]'; | |||
</syntaxhighlight> | |||
# “'''JSON_SET()'''”:替换现有路径的值,并添加不存在的路径的值: | |||
#: <syntaxhighlight lang="xml"> | |||
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]] | | |||
+--------------------------------------------+ | |||
</syntaxhighlight> | |||
# “'''JSON_INSERT()'''”:添加新值,但不替换现有值: | |||
#: <syntaxhighlight lang="xml"> | |||
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]] | | |||
+-----------------------------------------------+ | |||
</syntaxhighlight> | |||
# “'''JSON_REPLACE()'''”:替换现有值,并忽略新值: | |||
#: <syntaxhighlight lang="xml"> | |||
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]] | | |||
+------------------------------------------------+ | |||
</syntaxhighlight> | |||
# “'''JSON_REMOVE()'''”:接收 JSON 文档和一个或多个指定要从该文档中删除的值的路径。返回值是原始文档减去文档中存在的路径选择的值: | |||
#: <syntaxhighlight lang="xml"> | |||
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]}] | | |||
+---------------------------------------------------+ | |||
</syntaxhighlight> | |||
== JSON 值的比较和排序 == | |||
可以使用“'''='''”,“'''<'''”,“'''<='''”,“'''>'''”,“'''>='''”,“'''<>'''”,“'''!='''”和“'''<=>'''”运算符比较 JSON 值。 | |||
* JSON 值尚不支持以下比较运算符和函数:“BETWEEN”、“IN()”、“GREATEST()”、“LEAST()”。 | |||
* 上面列出的比较运算符和函数的解决方法是将 JSON 值转换为本地 MySQL 数字或字符串数据类型,以便它们具有一致的非 JSON 标量类型。 | |||
JSON 值的比较分为两个级别: | |||
# 比较的第一级基于'''比较值的 JSON 类型'''。【类型可由“'''JSON_TYPE()'''”函数获得】 | |||
#: 如果类型不同,则比较结果仅由优先级更高的类型确定。如果两个值具有相同的 JSON 类型,则使用特定于类型的规则进行第二级比较。 | |||
# 比较的第二级,相同优先级的 JSON 值,比较规则是特定于类型的。 | |||
类型优先级,及其比较规则如下:(优先级由高到低,同行同级) | |||
{| class="wikitable" | |||
! 类型 !! 比较规则 | |||
|- | |||
| BLOB || 比较两个值的第一个“N”字节(“N”是较短值中的字节数)。如果两个值的前“N”字节相同,则较短的值将在较长的值之前。 | |||
|- | |||
| BIT || 【规则同 BLOB】 | |||
|- | |||
| OPAQUE || 【规则同 BLOB】(OPAQUE值是未归类为其他类型之一的值) | |||
|- | |||
| DATETIME || 表示较早时间点的值先于表示较晚时间点的值。 | |||
|- | |||
| TIME || 两个时间值中较小的一个先于较大的一个。 | |||
|- | |||
| DATE || 较早的日期在较新的日期之前。 | |||
|- | |||
| BOOLEAN || JSON 错误字面量 小于 JSON 真实字面量。 | |||
|- | |||
| ARRAY | |||
| | |||
# 如果两个 JSON 数组的长度相同并且数组中对应位置的值相等,则它们相等。 | |||
# 如果数组不相等,则它们的顺序由存在差异的第一个位置的元素确定:在该位置具有较小值的数组将首先排序。 | |||
# 如果较短数组的所有值都等于较长数组中的相应值,则首先对较短数组进行排序。 | |||
示例: | |||
<syntaxhighlight lang="xml"> | |||
[] < ["a"] < ["ab"] < ["ab", "cd", "ef"] < ["ab", "ef"] | |||
</syntaxhighlight> | |||
|- | |||
| OBJECT || 如果两个 JSON 对象具有相同的键集,并且两个键中的每个键具有相同的值,则它们相等。 | |||
示例: | |||
<syntaxhighlight lang="xml"> | |||
{"a": 1, "b": 2} = {"b": 2, "a": 1} | |||
</syntaxhighlight> | |||
* 如果两个 OBJECT 不相等,那么其顺序是不确定的。 | |||
|- | |||
| STRING || 字符串在要比较的两个字符串的'''utf8mb4'''表示形式的前“N”个字节上按词法排序(“N”是较短值中的字节数)。如果两个字符串的前“N”字节相同,则较短的字符串被认为小于较长的字符串。 | |||
* 此排序等效于排序规则为'''utf8mb4_bin'''的 SQL 字符串的排序。因为 utf8mb4_bin 是按照'''二进制排序'''规则,所以 JSON 值的比较区分大小写: | |||
*: <syntaxhighlight lang="xml"> | |||
"A" < "a" | |||
</syntaxhighlight> | |||
|- | |||
| INTEGER, DOUBLE | |||
| | |||
* (JSON 值内比较数字的规则,有些不同于MySQL 数字类型的规则) | |||
* (MySQL 数字类型的规则:在分别使用本机 MySQL 的 INT 和 DOUBLE 数值类型的两列之间的比较中,所有比较都涉及一个整数和一个双精度数,则该整数都将转换为双精度数。即,'''将精确值数字转换为近似值数字'''。) | |||
JSON 值内比较数字的规则: | |||
# 如果查询比较'''两个包含数字的 JSON 列''',由于无法事先知道数字是整数还是双精度,所以为了在所有行中提供最一致的行为,则'''将近似值数字转换为精确值数字'''。所得的顺序是一致的,并且不会丢失精确值数字的精度。如: | |||
#* (9.223372036854776e18 即:9.223372036854776x10^18 = 9223372036854776000 ) | |||
#: <syntaxhighlight lang="xml"> | |||
9223372036854775805 < 9223372036854775806 < 9223372036854775807 < 9.223372036854776e18 = 9223372036854776000 < 9223372036854776001 | |||
</syntaxhighlight> | |||
# 如果是 JSON 比较以使用非 JSON 数字比较规则,则可能会出现不一致的顺序: | |||
## 比较整数:(未为 9.223372036854776e18 定义) | |||
##: <syntaxhighlight lang="xml"> | |||
9223372036854775805 < 9223372036854775806 < 9223372036854775807 | |||
</syntaxhighlight> | |||
## 比较双精度: | |||
##: <syntaxhighlight lang="xml"> | |||
9223372036854775805 = 9223372036854775806 = 9223372036854775807 = 9.223372036854776e18 | |||
</syntaxhighlight> | |||
|- | |||
| NULL || | |||
|} | |||
* 为了将任何 JSON 值与 SQL NULL进行比较,结果为 UNKNOWN。 | |||
* 为了比较 JSON 和非 JSON 值,将根据下规则将非 JSON 值转换为 JSON,然后按照前面所述比较这些值。 | |||
== 在 JSON 和非 JSON 值之间转换【???】 == | |||
JSON 转换规则: | |||
{| class="wikitable" | |||
! other type !! CAST(其他类型的 AS JSON) !! CAST(JSON AS 其他类型) | |||
|- | |||
| JSON || No change || No change | |||
|- | |||
| utf8 字符类型(utf8mb4,utf8,ascii) || 该字符串将解析为 JSON 值。 || JSON 值被序列化为utf8mb4字符串。 | |||
|- | |||
| 其他字符类型 || 其他字符编码将隐式转换为 utf8mb4,并按 utf8 字符类型所述进行处理。 || JSON 值被序列化为 utf8mb4 字符串,然后转换为其他字符编码。(结果可能没有意义) | |||
|- | |||
| NULL || 结果为 JSON 类型的NULL值。 || Not applicable. | |||
|- | |||
| Geometry types || 通过调用“ST_AsGeoJSON()”将几何值转换为 JSON 文档。 || 非法操作。解决方法:将“CAST(json_val AS CHAR)”的结果传递给“ST_GeomFromGeoJSON()”。 | |||
|- | |||
| 所有其他类型 || 结果是由单个标量值组成的 JSON 文档。 || 如果 JSON 文档由目标类型的单个标量值组成,并且该标量值可以转换为目标类型,则成功。否则,返回 NULL 并产生警告。 | |||
|} | |||
JSON 值的“ORDER BY”和“GROUP BY”按照以下原则工作: | |||
# 标量 JSON 值的排序使用与前面的讨论相同的规则。 | |||
# 对于升序排序,SQL NULL在所有 JSON 值(包括 JSON 空字面量)之前进行排序;对于降序排序,SQL NULL在所有 JSON 值(包括 JSON 空字面量)之后进行排序。 | |||
# JSON 值的排序键受“max_sort_length”系统变量的值限制,因此仅在前“max_sort_length”个字节之后才不同的键比较相等。 | |||
# 当前不支持对非标量值进行排序,并且会出现警告。 | |||
对于排序,将 JSON 标量转换为其他一些本机 MySQL 类型可能是有益的。 | |||
: 例如,如果名为jdoc的列包含 JSON 对象,其成员由id键和一个非负值组成,则使用此表达式按 id 值进行排序: | |||
: <syntaxhighlight lang="xml"> | |||
ORDER BY CAST(JSON_EXTRACT(jdoc, '$.id') AS UNSIGNED) | |||
</syntaxhighlight> | |||
如果碰巧有一个生成的列被定义为使用与“ORDER BY”中相同的表达式,则 MySQL 优化器将识别出该列,并考虑将索引用于查询执行计划。 | |||
== JSON 值的汇总 == | |||
对于 JSON 值的汇总,与其他数据类型一样,将忽略 NULL 值。非NULL 值将转换为数字类型并进行汇总,但“MIN()”,“MAX()”和“GROUP_CONCAT()”除外。 | |||
尽管可能会发生截断和精度损失,但转换为数字应该为数字标量的 JSON 值产生有意义的结果。【?】 | |||
转换为其他 JSON 值的数量可能不会产生有意义的结果。 |
2021年3月27日 (六) 04:03的版本
关于:JSON 数据类型
从 MySQL 5.7.8 开始,MySQL 支持由RFC 7159定义的本机JSON数据类型,该类型可有效访问 JSON(JavaScript 对象表示法)文档中的数据。
与将 JSON 格式的字符串存储在字符串列中相比,JSON数据类型具有以下优点:
- 自动验证存储在JSON列中的 JSON 文档。无效的文档会产生错误。
- 优化的存储格式。存储在JSON列中的 JSON 文档将转换为内部格式,从而可以快速读取文档元素。当服务器稍后必须读取以该二进制格式存储的 JSON 值时,则无需从文本 table 示形式解析该值。二进制格式的结构使服务器可以直接通过键或数组索引查找子对象或嵌套值,而无需读取文档中它们之前或之后的所有值。
注意:
- 存储JSON文档所需的空间与“LONGBLOB”【???】或“LONGTEXT”【???】大致相同;存储在JSON列中的任何 JSON 文档的大小都限于“max_allowed_packet”系统变量的值。
- JSON列不能具有非NULL的默认值。
- 除JSON数据类型外,还有一组 SQL 函数可用于启用对 JSON 值的操作,例如创建,操作和搜索。
- MySQL 还提供了一组用于处理 GeoJSON 值的空间函数。【???】
- JSON列与其他二进制类型的列一样,不会直接构建索引;而是相反,您可以在生成的列上创建索引,该索引从JSON列中提取标量值。
- MySQL 优化器还在与 JSON 表达式匹配的虚拟列上寻找兼容的索引。
- MySQL NDB Cluster 7.5(7.5.2 及更高版本)支持JSON列和 MySQL JSON 函数,包括在从JSON列生成的列上创建索引,以作为无法对JSON列构建索引的解决方法。每个NDB 表最多支持 3 个JSON列。
创建 JSON 值
创建 JSON 值:
- JSON 数组:包含用逗号分隔并包含在“[”和“]”字符内的值的列表:
["abc", 10, null, true, false]
- JSON 对象:包含一组键值对,以逗号分隔并包含在“{”和“}”字符内:
{"k1": "value", "k2": 10}
- JSON 数组和对象可以包含:字符串、数字的标量值,JSON 空字面量 或 JSON 布尔值“true”或“false”。
- JSON 对象中的键必须是字符串。
- 还允许使用时间(date, time 或 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 值:
- “JSON_ARRAY([val[, val] ...])”:计算(可能为空)值列表,并返回包含这些值的 JSON 数组。
mysql> SELECT JSON_ARRAY(1, "abc", NULL, TRUE, CURTIME()); +---------------------------------------------+ | JSON_ARRAY(1, "abc", NULL, TRUE, CURTIME()) | +---------------------------------------------+ | [1, "abc", null, true, "11:30:24.000000"] | +---------------------------------------------+
- “JSON_OBJECT([key, val[, key, val] ...])”:评估键值对的列表(可能为空),并返回包含这些对的 JSON 对象。如果任何键名称为“NULL”或参数个数为奇数,则会发生错误。
mysql> SELECT JSON_OBJECT('id', 87, 'name', 'carrot'); +-----------------------------------------+ | JSON_OBJECT('id', 87, 'name', 'carrot') | +-----------------------------------------+ | {"id": 87, "name": "carrot"} | +-----------------------------------------+
- “JSON_MERGE()”接收两个或多个 JSON 文档并返回合并的结果:
mysql> SELECT JSON_MERGE('["a", 1]', '{"key": "value"}'); +--------------------------------------------+ | JSON_MERGE('["a", 1]', '{"key": "value"}') | +--------------------------------------------+ | ["a", 1, {"key": "value"}] | +--------------------------------------------+
- “JSON_QUOTE(string)”:通过“1、将字符串用双引号引起来,2、并转义内部引号和其他字符”来将字符串引为 JSON 值,然后将结果作为utf8mb4字符串返回。如果参数为 NULL,则返回 NULL。
mysql> SELECT JSON_QUOTE('null'), JSON_QUOTE('"null"'); +--------------------+----------------------+ | JSON_QUOTE('null') | JSON_QUOTE('"null"') | +--------------------+----------------------+ | "null" | "\"null\"" | +--------------------+----------------------+ mysql> SELECT JSON_QUOTE('[1, 2, 3]'); +-------------------------+ | JSON_QUOTE('[1, 2, 3]') | +-------------------------+ | "[1, 2, 3]" | +-------------------------+
- “JSON_ARRAY([val[, val] ...])”:计算(可能为空)值列表,并返回包含这些值的 JSON 数组。
- 使用“CAST(值为 JSON)”将其他类型的值强制转换为JSON类型。【见下面:“在 JSON 和非 JSON 值之间转换”】
在 MySQL 中,JSON 值被编写为字符串。 MySQL 会解析在需要 JSON 值的上下文中使用的任何字符串,如果该字符串作为 JSON 无效,则会产生错误。这些上下文包括将值插入具有JSON数据类型的列中,以及将参数传递给需要 JSON 值的函数,如下所示示例说明:
- 如果值是有效的 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,'.
- “JSON_TYPE()”函数需要一个 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 使用utf8mb4字符集和utf8mb4_bin归类处理在 JSON 上下文中使用的字符串。其他字符集中的字符串将根据需要转换为 utf8mb4。(对于ascii或utf8字符集中的字符串,不需要转换,因为ascii和utf8是utf8mb4的子集【?】)
- 可以将 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 的字面量“null”,“true”和“false”,它们必须始终以小写形式编写:
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的字面量 NULL,TRUE 和 FALSE,区分大小写:
mysql> SELECT ISNULL(null), ISNULL(Null), ISNULL(NULL); +--------------+--------------+--------------+ | ISNULL(null) | ISNULL(Null) | ISNULL(NULL) | +--------------+--------------+--------------+ | 1 | 1 | 1 | +--------------+--------------+--------------+
- 有时可能需要或希望将引号字符(“"”或“'”)插入 JSON 文档。如:
- 要在如下 table 中插入包含特殊字符的语句作为 JSON 对象:
mysql> CREATE TABLE facts (sentence JSON);
- 需要插入的语句为:
mascot: The MySQL mascot is a dolphin named "Sakila".
- 方法一:使用 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". | +-----------------------------------------+
- 如果启用了“NO_BACKSLASH_ESCAPES”服务器 SQL 模式,则可以使用单个反斜杠(而不是双反斜杠)来插入 JSON 对象字面量,并且保留反斜杠。如果在执行插入操作时使用“JSON_OBJECT()”函数,并且设置了此模式,则必须使用单引号和双引号,例如:
mysql> INSERT INTO facts VALUES > (JSON_OBJECT('mascot', 'Our mascot is a dolphin named "Sakila".'));
JSON 值的规范化,合并和自动包装
当解析一个字符串并发现它是一个有效的 JSON 文档时,也会对其进行规范化:具有与文档中较早发现的键重复的键的成员将被丢弃(即使值不同)。
- 重复键的这种“第一键获胜”处理与 RFC 7159 不一致。这是 MySQL 5.7 中的一个已知问题,已在 MySQL 8.0 中修复。(缺陷#86866,错误#26369555)
- MySQL 还会在原始 JSON 文档中的键,值或元素之间舍弃多余的空格,并在显示时在每个逗号“,”或冒号“:”后留一个空格(或在必要时插入)。这样做是为了提高可读性。
- 为了使查找更有效率,它还会对 JSON 对象的键进行排序。 注意,此排序的结果可能会更改,并且不能保证在各个发行版中都保持一致。
示例:
mysql> SELECT JSON_OBJECT('key1', 1, 'key2', 'abc', 'key1', 'def');
+------------------------------------------------------+
| JSON_OBJECT('key1', 1, 'key2', 'abc', 'key1', 'def') |
+------------------------------------------------------+
| {"key1": 1, "key2": "abc"} |
+------------------------------------------------------+
合并 JSON 值
在组合多个数组的上下文中,通过将稍后命名的数组连接到第一个数组的末尾,将数组合并为单个数组。
- “JSON_MERGE()”将其参数合并到单个数组中:
mysql> SELECT JSON_MERGE('[1, 2]', '["a", "b"]', '[true, false]'); +-----------------------------------------------------+ | JSON_MERGE('[1, 2]', '["a", "b"]', '[true, false]') | +-----------------------------------------------------+ | [1, 2, "a", "b", true, false] | +-----------------------------------------------------+
- 将值插入 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": 17} | | {"x": 17} | +-----------+
- 合并时,多个对象将产生一个对象。如果多个对象具有相同的键,则在合并后的对象中该键的值是一个包含键值的数组:
mysql> SELECT JSON_MERGE('{"a": 1, "b": 2}', '{"c": 3, "a": 4}'); +----------------------------------------------------+ | JSON_MERGE('{"a": 1, "b": 2}', '{"c": 3, "a": 4}') | +----------------------------------------------------+ | {"a": [1, 4], "b": 2, "c": 3} | +----------------------------------------------------+
- 需要数组值的上下文中使用的非数组值将自动包装:值被“[”和“]”字符包围,以将其转换为数组。在以下语句中,每个参数都自动包装为数组([1],[2])。然后将它们合并以生成单个结果数组:
mysql> SELECT JSON_MERGE('1', '2'); +----------------------+ | JSON_MERGE('1', '2') | +----------------------+ | [1, 2] | +----------------------+
- 通过将对象自动包装为数组并将两个数组合并,可以合并数组和对象值:
mysql> SELECT JSON_MERGE('[10, 20]', '{"a": "x", "b": "y"}'); +------------------------------------------------+ | JSON_MERGE('[10, 20]', '{"a": "x", "b": "y"}') | +------------------------------------------------+ | [10, 20, {"a": "x", "b": "y"}] | +------------------------------------------------+
搜索和修改 JSON 值
搜索 JSON 值
通过 JSON 路径表达式选择 JSON 文档中的值。
示例,使用name键从 JSON 文档中提取成员的值:
mysql> SELECT JSON_EXTRACT('{"id": 14, "name": "Aztalan"}', '$.name');
+---------------------------------------------------------+
| JSON_EXTRACT('{"id": 14, "name": "Aztalan"}', '$.name') |
+---------------------------------------------------------+
| "Aztalan" |
+---------------------------------------------------------+
路径语法使用前导“$”字符 table 示所考虑的 JSON 文档,还可以选择后面跟有选择器,这些选择器依次指示文档的更具体部分:
- 句点后跟一个键名,使用给定的键为对象中的成员命名。如果路径表达式中的名称不合法(例如,名称包含空格),则必须在双引号中指定键名称。
- “[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)
- 路径可以包含“*”或“**”通配符:
- “.[*]”为 JSON 对象中所有成员的值。
- “[*]”计算 JSON 数组中所有元素的值。
- “prefix**suffix”计算所有以命名前缀开头并以命名后缀结尾的路径。
- 路径可能不包含序列“***”。
- 文档中不存在的路径(评估为不存在的数据)评估为 NULL。
示例1,让“$”引用具有三个元素的 JSON 数组:
[3, {"a": [5, 6], "b": 10}, [99, 100]]
则:
- $[0] 等于 3。
- $[1] 等于 {"a": [5, 6], "b": 10}。
- $[2] 等于 [99, 100]。
- $[3] 的计算结果为 NULL(它指向第四个数组元素,该元素不存在)。
因为 $[1] 和 $[2] 的计算结果为非标量值,所以它们可以用作选择嵌套值的更特定路径表达式的基础。例子:
- $[1].a 等于 [5, 6]。
- $[1].a[1] 等于 6。
- $[1].b 等于 10。
- $[2][0] 等于 99。
示例2,如果未加引号的键名在路径表达式中不合法,则必须用引号命名路径的组件。让“$”引用此值:
{"a fish": "shark", "a bird": "sparrow"}
- $."a fish" 等于 shark。
- $."a bird" 等于 sparrow。
示例3,使用通配符的路径求值的数组可以包含多个值:
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] |
+------------------------------------------------------------+
示例4,路径“$**.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] |
+---------------------------------------------------------+
- 在 MySQL 5.7.9 及更高版本中,您可以将“column->path”与 JSON 列标识符和 JSON 路径表达式一起用作“JSON_EXTRACT(column, path)”的同义词:【列路径标识符】
mysql> SELECT col->"$.mascot" FROM qtest; +---------------------------------------------+ | col->"$.mascot" | +---------------------------------------------+ | "Our mascot is a dolphin named \"Sakila\"." | +---------------------------------------------+ 1 row in set (0.00 sec)
修改 JSON 值:JSON_SET()、JSON_INSERT()、JSON_REPLACE()
某些函数采用现有的 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]] | +--------------------------------------------+
- “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]}] | +---------------------------------------------------+
JSON 值的比较和排序
可以使用“=”,“<”,“<=”,“>”,“>=”,“<>”,“!=”和“<=>”运算符比较 JSON 值。
- JSON 值尚不支持以下比较运算符和函数:“BETWEEN”、“IN()”、“GREATEST()”、“LEAST()”。
- 上面列出的比较运算符和函数的解决方法是将 JSON 值转换为本地 MySQL 数字或字符串数据类型,以便它们具有一致的非 JSON 标量类型。
JSON 值的比较分为两个级别:
- 比较的第一级基于比较值的 JSON 类型。【类型可由“JSON_TYPE()”函数获得】
- 如果类型不同,则比较结果仅由优先级更高的类型确定。如果两个值具有相同的 JSON 类型,则使用特定于类型的规则进行第二级比较。
- 比较的第二级,相同优先级的 JSON 值,比较规则是特定于类型的。
类型优先级,及其比较规则如下:(优先级由高到低,同行同级)
类型 | 比较规则 |
---|---|
BLOB | 比较两个值的第一个“N”字节(“N”是较短值中的字节数)。如果两个值的前“N”字节相同,则较短的值将在较长的值之前。 |
BIT | 【规则同 BLOB】 |
OPAQUE | 【规则同 BLOB】(OPAQUE值是未归类为其他类型之一的值) |
DATETIME | 表示较早时间点的值先于表示较晚时间点的值。 |
TIME | 两个时间值中较小的一个先于较大的一个。 |
DATE | 较早的日期在较新的日期之前。 |
BOOLEAN | JSON 错误字面量 小于 JSON 真实字面量。 |
ARRAY |
示例: [] < ["a"] < ["ab"] < ["ab", "cd", "ef"] < ["ab", "ef"]
|
OBJECT | 如果两个 JSON 对象具有相同的键集,并且两个键中的每个键具有相同的值,则它们相等。
示例: {"a": 1, "b": 2} = {"b": 2, "a": 1}
|
STRING | 字符串在要比较的两个字符串的utf8mb4表示形式的前“N”个字节上按词法排序(“N”是较短值中的字节数)。如果两个字符串的前“N”字节相同,则较短的字符串被认为小于较长的字符串。
|
INTEGER, DOUBLE |
JSON 值内比较数字的规则:
|
NULL |
- 为了将任何 JSON 值与 SQL NULL进行比较,结果为 UNKNOWN。
- 为了比较 JSON 和非 JSON 值,将根据下规则将非 JSON 值转换为 JSON,然后按照前面所述比较这些值。
在 JSON 和非 JSON 值之间转换【???】
JSON 转换规则:
other type | CAST(其他类型的 AS JSON) | CAST(JSON AS 其他类型) |
---|---|---|
JSON | No change | No change |
utf8 字符类型(utf8mb4,utf8,ascii) | 该字符串将解析为 JSON 值。 | JSON 值被序列化为utf8mb4字符串。 |
其他字符类型 | 其他字符编码将隐式转换为 utf8mb4,并按 utf8 字符类型所述进行处理。 | JSON 值被序列化为 utf8mb4 字符串,然后转换为其他字符编码。(结果可能没有意义) |
NULL | 结果为 JSON 类型的NULL值。 | Not applicable. |
Geometry types | 通过调用“ST_AsGeoJSON()”将几何值转换为 JSON 文档。 | 非法操作。解决方法:将“CAST(json_val AS CHAR)”的结果传递给“ST_GeomFromGeoJSON()”。 |
所有其他类型 | 结果是由单个标量值组成的 JSON 文档。 | 如果 JSON 文档由目标类型的单个标量值组成,并且该标量值可以转换为目标类型,则成功。否则,返回 NULL 并产生警告。 |
JSON 值的“ORDER BY”和“GROUP BY”按照以下原则工作:
- 标量 JSON 值的排序使用与前面的讨论相同的规则。
- 对于升序排序,SQL NULL在所有 JSON 值(包括 JSON 空字面量)之前进行排序;对于降序排序,SQL NULL在所有 JSON 值(包括 JSON 空字面量)之后进行排序。
- JSON 值的排序键受“max_sort_length”系统变量的值限制,因此仅在前“max_sort_length”个字节之后才不同的键比较相等。
- 当前不支持对非标量值进行排序,并且会出现警告。
对于排序,将 JSON 标量转换为其他一些本机 MySQL 类型可能是有益的。
- 例如,如果名为jdoc的列包含 JSON 对象,其成员由id键和一个非负值组成,则使用此表达式按 id 值进行排序:
ORDER BY CAST(JSON_EXTRACT(jdoc, '$.id') AS UNSIGNED)
如果碰巧有一个生成的列被定义为使用与“ORDER BY”中相同的表达式,则 MySQL 优化器将识别出该列,并考虑将索引用于查询执行计划。
JSON 值的汇总
对于 JSON 值的汇总,与其他数据类型一样,将忽略 NULL 值。非NULL 值将转换为数字类型并进行汇总,但“MIN()”,“MAX()”和“GROUP_CONCAT()”除外。
尽管可能会发生截断和精度损失,但转换为数字应该为数字标量的 JSON 值产生有意义的结果。【?】
转换为其他 JSON 值的数量可能不会产生有意义的结果。