范文健康探索娱乐情感热点
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

越晚搞懂MySQLJSON数据类型,你就越吃亏

  作者介绍
  陈臣, 甲骨文MySQL首席解决方案工程师,公众号《MySQL实战》作者,有大规模的MySQL,Redis,MongoDB,ES的管理和维护经验,擅长MySQL数据库的性能优化及日常操作的原理剖析。
  JSON 数据类型是 MySQL 5.7.8 开始支持的。在此之前,只能通过字符类型(CHAR,VARCHAR 或 TEXT )来保存 JSON 文档。
  相对字符类型,原生的 JSON 类型具有以下优势:
  在插入时能自动校验文档是否满足 JSON 格式的要求。
  优化了存储格式。无需读取整个文档就能快速访问某个元素的值。
  在 JSON 类型引入之前,如果我们想要获取 JSON 文档中的某个元素,必须首先读取整个 JSON 文档,然后在客户端将其转换为 JSON 对象,最后再通过对象获取指定元素的值。
  下面是 Python 中的获取方式。
  import json# JSON 字符串:x =  "{ "name":"John", "age":30, "city":"New York"}"# 将 JSON 字符串转换为 JSON 对象:y = json.loads(x)# 读取 JSON 对象中指定元素的值:print(y["age"])
  这种方式有两个弊端:一是消耗磁盘 IO,二是消耗网络带宽,如果 JSON 文档比较大,在高并发场景,有可能会打爆网卡。
  如果使用的是 JSON 类型,相同的需求,直接使用 SQL 命令就可搞定。不仅能节省网络带宽,结合后面提到的函数索引,还能降低磁盘 IO 消耗。
  mysql> create table t(c1 json);Query OK, 0 rows affected (0.09 sec)mysql> insert into t values("{ "name":"John", "age":30, "city":"New York"}");Query OK, 1 row affected (0.01 sec)mysql> select c1->"$.age" from t;+-------------+| c1->"$.age" |+-------------+| 30          |+-------------+1 row in set (0.00 sec)
  一、什么是JSON
  JSON 是 JavaScript Object Notation(JavaScript 对象表示法)的缩写,是一个轻量级的,基于文本的,跨语言的数据交换格式。易于阅读和编写。
  JSON 的基本数据类型如下:
  数值:十进制数,不能有前导 0,可以为负数或小数,还可以为 e 或 E 表示的指数。
  字符串:字符串必须用双引号括起来。
  布尔值:true,false。
  数组:一个由零或多个值组成的有序序列。每个值可以为任意类型。数组使用方括号[] 括起来,元素之间用逗号,分隔。譬如,
  [1, "abc", null, true, "10:27:06.000000", {"id": 1}]
  对象:一个由零或者多个键值对组成的无序集合。其中键必须是字符串,值可以为任意类型。
  对象使用花括号{}括起来,键值对之间使用逗号,分隔,键与值之间用冒号:分隔。譬如,
  {"db": ["mysql", "oracle"], "id": 123, "info": {"age": 20}}
  空值:null。
  二、JSON 字段的增删改查操作
  下面我们看看 JSON 字段常见的增删改查操作:
  1、插入操作
  可直接插入 JSON 格式的字符串。
  mysql> create table t(c1 json);Query OK, 0 rows affected (0.03 sec)mysql> insert into t values("[1, "abc", null, true, "08:45:06.000000"]");Query OK, 1 row affected (0.01 sec)mysql> insert into t values("{"id": 87, "name": "carrot"}");Query OK, 1 row affected (0.01 sec)
  也可使用函数,常用的有 JSON_ARRAY() 和 JSON_OBJECT(),前者用于构造 JSON 数组,后者用于构造 JSON 对象。如,
  mysql> select json_array(1, "abc", null, true,curtime());+--------------------------------------------+| json_array(1, "abc", null, true,curtime()) |+--------------------------------------------+| [1, "abc", null, true, "10:12:25.000000"]  |+--------------------------------------------+1 row in set (0.01 sec)mysql> select json_object("id", 87, "name", "carrot");+-----------------------------------------+| json_object("id", 87, "name", "carrot") |+-----------------------------------------+| {"id": 87, "name": "carrot"}            |+-----------------------------------------+1 row in set (0.00 sec)
  对于 JSON 文档,KEY 名不能重复。
  如果插入的值中存在重复 KEY,在 MySQL 8.0.3 之前,遵循 first duplicate key wins 原则,会保留第一个 KEY,后面的将被丢弃掉。
  从 MySQL 8.0.3 开始,遵循的是 last duplicate key wins 原则,只会保留最后一个 KEY。
  下面通过一个具体的示例来看看两者的区别。
  MySQL 5.7.36
  mysql> select json_object("key1",10,"key2",20,"key1",30);+--------------------------------------------+| json_object("key1",10,"key2",20,"key1",30) |+--------------------------------------------+| {"key1": 10, "key2": 20}                   |+--------------------------------------------+1 row in set (0.02 sec)
  MySQL 8.0.27
  mysql> select json_object("key1",10,"key2",20,"key1",30);+--------------------------------------------+| json_object("key1",10,"key2",20,"key1",30) |+--------------------------------------------+| {"key1": 30, "key2": 20}                   |+--------------------------------------------+1 row in set (0.00 sec)
  2、查询操作
  1)JSON_EXTRACT(json_doc, path[, path] ...)
  其中,json_doc 是 JSON 文档,path 是路径。该函数会从 JSON 文档提取指定路径(path)的元素。如果指定 path 不存在,会返回 NULL。可指定多个 path,匹配到的多个值会以数组形式返回。
  下面我们结合一些具体的示例来看看 path 及 JSON_EXTRACT 的用法。
  首先我们看看数组。
  数组的路径是通过下标来表示的。第一个元素的下标是 0。
  mysql> select json_extract("[10, 20, [30, 40]]", "$[0]");+--------------------------------------------+| json_extract("[10, 20, [30, 40]]", "$[0]") |+--------------------------------------------+| 10                                         |+--------------------------------------------+1 row in set (0.00 sec)mysql> select json_extract("[10, 20, [30, 40]]", "$[0]", "$[1]","$[2][0]");+--------------------------------------------------------------+| json_extract("[10, 20, [30, 40]]", "$[0]", "$[1]","$[2][0]") |+--------------------------------------------------------------+| [10, 20, 30]                                                 |+--------------------------------------------------------------+1 row in set (0.00 sec)
  除此之外,还可通过 [M to N] 获取数组的子集。
  mysql> select json_extract("[10, 20, [30, 40]]", "$[0 to 1]");+-------------------------------------------------+| json_extract("[10, 20, [30, 40]]", "$[0 to 1]") |+-------------------------------------------------+| [10, 20]                                        |+-------------------------------------------------+1 row in set (0.00 sec)# 这里的 last 代表最后一个元素的下标mysql> select json_extract("[10, 20, [30, 40]]", "$[last-1 to last]");+---------------------------------------------------------+| json_extract("[10, 20, [30, 40]]", "$[last-1 to last]") |+---------------------------------------------------------+| [20, [30, 40]]                                          |+---------------------------------------------------------+1 row in set (0.00 sec)
  也可通过 [*] 获取数组中的所有元素。
  mysql> select json_extract("[10, 20, [30, 40]]", "$[*]");+--------------------------------------------+| json_extract("[10, 20, [30, 40]]", "$[*]") |+--------------------------------------------+| [10, 20, [30, 40]]                         |+--------------------------------------------+1 row in set (0.00 sec)
  接下来,我们看看对象。
  对象的路径是通过 KEY 来表示的。
  mysql> set @j="{"a": 1, "b": [2, 3], "a c": 4}";Query OK, 0 rows affected (0.00 sec)# 如果 KEY 在路径表达式中不合法(譬如存在空格),则在引用这个 KEY 时,需用双引号括起来。mysql> select json_extract(@j, "$.a"), json_extract(@j, "$."a c""), json_extract(@j, "$.b[1]");+-------------------------+-----------------------------+----------------------------+| json_extract(@j, "$.a") | json_extract(@j, "$."a c"") | json_extract(@j, "$.b[1]") |+-------------------------+-----------------------------+----------------------------+| 1                       | 4                           | 3                          |+-------------------------+-----------------------------+----------------------------+1 row in set (0.00 sec)
  除此之外,还可通过 .* 获取对象中的所有元素。
  mysql> select json_extract("{"a": 1, "b": [2, 3], "a c": 4}", "$.*");+--------------------------------------------------------+| json_extract("{"a": 1, "b": [2, 3], "a c": 4}", "$.*") |+--------------------------------------------------------+| [1, [2, 3], 4]                                         |+--------------------------------------------------------+1 row in set (0.00 sec)# 这里的 $**.b 匹配 $.a.b 和 $.c.bmysql> select json_extract("{"a": {"b": 1}, "c": {"b": 2}}", "$**.b");+---------------------------------------------------------+| json_extract("{"a": {"b": 1}, "c": {"b": 2}}", "$**.b") |+---------------------------------------------------------+| [1, 2]                                                  |+---------------------------------------------------------+1 row in set (0.00 sec)
  2)column->path
  column->path,包括后面讲到的 column->>path,都是语法糖,在实际使用的时候都会转化为 JSON_EXTRACT。
  column->path 等同于 JSON_EXTRACT(column, path) ,只能指定一个path。
  create table t(c2 json);insert into t values("{"empno": 1001, "ename": "jack"}"), ("{"empno": 1002, "ename": "mark"}");mysql> select c2, c2->"$.ename" from t;+----------------------------------+---------------+| c2                               | c2->"$.ename" |+----------------------------------+---------------+| {"empno": 1001, "ename": "jack"} | "jack"        || {"empno": 1002, "ename": "mark"} | "mark"        |+----------------------------------+---------------+2 rows in set (0.00 sec)mysql> select * from t where c2->"$.empno" = 1001;+------+----------------------------------+| c1   | c2                               |+------+----------------------------------+|    1 | {"empno": 1001, "ename": "jack"} |+------+----------------------------------+1 row in set (0.00 sec)
  3)column->>path
  同 column->path 类似,只不过其返回的是字符串。以下三者是等价的。
  JSON_UNQUOTE( JSON_EXTRACT(column, path) )  JSON_UNQUOTE(column -> path)  column->>path
  mysql> select c2->"$.ename",json_extract(c2, "$.ename"),json_unquote(c2->"$.ename"),c2->>"$.ename" from t;+---------------+-----------------------------+-----------------------------+----------------+| c2->"$.ename" | json_extract(c2, "$.ename") | json_unquote(c2->"$.ename") | c2->>"$.ename" |+---------------+-----------------------------+-----------------------------+----------------+| "jack"        | "jack"                      | jack                        | jack           || "mark"        | "mark"                      | mark                        | mark           |+---------------+-----------------------------+-----------------------------+----------------+2 rows in set (0.00 sec)
  3、修改操作
  1)JSON_INSERT(json_doc, path, val[, path, val] ...)
  插入新值。
  仅当指定位置或指定 KEY 的值不存在时,才执行插入操作。另外,如果指定的 path 是数组下标,且 json_doc 不是数组,该函数首先会将 json_doc 转化为数组,然后再插入新值。
  下面我们看几个示例。
  mysql> select json_insert("1","$[0]","10");+------------------------------+| json_insert("1","$[0]","10") |+------------------------------+| 1                            |+------------------------------+1 row in set (0.00 sec)mysql> select json_insert("1","$[1]","10");+------------------------------+| json_insert("1","$[1]","10") |+------------------------------+| [1, "10"]                    |+------------------------------+1 row in set (0.01 sec)mysql> select json_insert("["1","2"]","$[2]","10");+--------------------------------------+| json_insert("["1","2"]","$[2]","10") |+--------------------------------------+| ["1", "2", "10"]                     |+--------------------------------------+1 row in set (0.00 sec)mysql> set @j = "{ "a": 1, "b": [2, 3]}";Query OK, 0 rows affected (0.00 sec)mysql> select json_insert(@j, "$.a", 10, "$.c", "[true, false]");+----------------------------------------------------+| json_insert(@j, "$.a", 10, "$.c", "[true, false]") |+----------------------------------------------------+| {"a": 1, "b": [2, 3], "c": "[true, false]"}        |+----------------------------------------------------+1 row in set (0.00 sec)
  2)JSON_SET(json_doc, path, val[, path, val] ...)
  插入新值,并替换已经存在的值。
  换言之,如果指定位置或指定 KEY 的值不存在,会执行插入操作,如果存在,则执行更新操作。
  mysql> set @j = "{ "a": 1, "b": [2, 3]}";Query OK, 0 rows affected (0.00 sec)mysql> select json_set(@j, "$.a", 10, "$.c", "[true, false]");+-------------------------------------------------+| json_set(@j, "$.a", 10, "$.c", "[true, false]") |+-------------------------------------------------+| {"a": 10, "b": [2, 3], "c": "[true, false]"}    |+-------------------------------------------------+1 row in set (0.00 sec)
  3)JSON_REPLACE(json_doc, path, val[, path, val] ...)
  替换已经存在的值。
  mysql> set @j = "{ "a": 1, "b": [2, 3]}";Query OK, 0 rows affected (0.00 sec)mysql> select json_replace(@j, "$.a", 10, "$.c", "[true, false]");+-----------------------------------------------------+| json_replace(@j, "$.a", 10, "$.c", "[true, false]") |+-----------------------------------------------------+| {"a": 10, "b": [2, 3]}                              |+-----------------------------------------------------+1 row in set (0.00 sec)
  4、删除操作
  JSON_REMOVE(json_doc, path[, path] ...)
  删除 JSON 文档指定位置的元素。
  mysql> set @j = "{ "a": 1, "b": [2, 3]}";Query OK, 0 rows affected (0.00 sec)mysql> select json_remove(@j, "$.a");+------------------------+| JSON_REMOVE(@j, "$.a") |+------------------------+| {"b": [2, 3]}          |+------------------------+1 row in set (0.00 sec)mysql> set @j = "["a", ["b", "c"], "d", "e"]";Query OK, 0 rows affected (0.00 sec)mysql> select json_remove(@j, "$[1]");+-------------------------+| JSON_REMOVE(@j, "$[1]") |+-------------------------+| ["a", "d", "e"]         |+-------------------------+1 row in set (0.00 sec)mysql> select json_remove(@j, "$[1]","$[2]");+--------------------------------+| JSON_REMOVE(@j, "$[1]","$[2]") |+--------------------------------+| ["a", "d"]                     |+--------------------------------+1 row in set (0.00 sec)mysql> select json_remove(@j, "$[1]","$[1]");+--------------------------------+| JSON_REMOVE(@j, "$[1]","$[1]") |+--------------------------------+| ["a", "e"]                     |+--------------------------------+1 row in set (0.00 sec)
  最后一个查询,虽然两个 path 都是 "$[1]" ,但作用对象不一样,第一个 path 的作用对象是 "["a", ["b", "c"], "d", "e"]" ,第二个 path 的作用对象是删除了 "$[1]" 后的数组,即 "["a", "d", "e"]" 。
  三、如何对 JSON 字段创建索引
  同 TEXT,BLOB 字段一样,JSON 字段不允许直接创建索引。
  mysql> create table t(c1 json, index (c1));ERROR 3152 (42000): JSON column "c1" supports indexing only via generated columns on a specified JSON path.
  即使支持,实际意义也不大,因为我们一般是基于文档中的元素进行查询,很少会基于整个 JSON 文档。
  对文档中的元素进行查询,就需要用到 MySQL 5.7 引入的虚拟列及函数索引。
  下面我们来看一个具体的示例。
  # C2 即虚拟列# index (c2) 对虚拟列添加索引。create table t ( c1 json, c2 varchar(10) as (JSON_UNQUOTE(c1 -> "$.name")), index (c2) );insert into t (c1) values  ("{"id": 1, "name": "a"}"), ("{"id": 2, "name": "b"}"), ("{"id": 3, "name": "c"}"), ("{"id": 4, "name": "d"}");mysql> explain select * from t where c2 = "a";+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref   | rows | filtered | Extra |+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+|  1 | SIMPLE      | t     | NULL       | ref  | c2            | c2   | 43      | const |    1 |   100.00 | NULL  |+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+1 row in set, 1 warning (0.00 sec)mysql> explain select * from t where c1->"$.name" = "a";+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref   | rows | filtered | Extra |+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+|  1 | SIMPLE      | t     | NULL       | ref  | c2            | c2   | 43      | const |    1 |   100.00 | NULL  |+----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+1 row in set, 1 warning (0.00 sec)
  可以看到,无论是使用虚拟列,还是文档中的元素来查询,都可以利用上索引。
  注意,在创建虚拟列时需指定 JSON_UNQUOTE,将 c1 -> "$.name" 的返回值转换为字符串。
  四、如何将存储 JSON 字符串的字符字段升级为 JSON 字段
  在 MySQL 支持 JSON 类型之前,对于 JSON 文档,一般是以字符串的形式存储在字符类型(VARCHAR 或 TEXT)中。
  在 JSON 类型出来之后,如何将这些字符字段升级为 JSON 字段呢?
  为方便演示,这里首先构建测试数据。
  create table t (id int auto_increment primary key, c1 text);insert into t (c1) values ("{"id": "1", "name": "a"}"), ("{"id": "2", "name": "b"}"), ("{"id": "3", "name": "c"}"), ("{"id", "name": "d"}");
  注意,最后一个文档有问题,不是合格的 JSON 文档。
  如果使用 DDL 直接修改字段的数据类型,会报错。
  mysql> alter table t modify c1 json;ERROR 3140 (22032): Invalid JSON text: "Missing a colon after a name of object member." at position 5 in value for column "#sql-7e1c_1f6.c1".
  下面,我们看看具体的升级步骤。
  1)使用 json_valid 函数找出不满足 JSON 格式要求的文档。
  mysql> select * from t where json_valid(c1) = 0;+----+---------------------+| id | c1                  |+----+---------------------+|  4 | {"id", "name": "d"} |+----+---------------------+1 row in set (0.00 sec)
  2)处理不满足 JSON 格式要求的文档。
  mysql> update t set c1="{"id": "4", "name": "d"}" where id=4;Query OK, 1 row affected (0.01 sec)Rows matched: 1  Changed: 1  Warnings: 0
  3)将 TEXT 字段修改为 JSON 字段。
  mysql> select * from t where json_valid(c1) = 0;Empty set (0.00 sec)mysql> alter table t modify c1 json;Query OK, 4 rows affected (0.13 sec)Records: 4  Duplicates: 0  Warnings: 0
  五、使用 JSON 时的注意事项
  对于 JSON 类型,有以下几点需要注意:
  1)在 MySQL 8.0.13 之前,不允许对 BLOB,TEXT,GEOMETRY,JSON 字段设置默认值。从 MySQL 8.0.13 开始,取消了这个限制。
  设置时,注意默认值需通过小括号()括起来,否则的话,还是会提示 JSON 字段不允许设置默认值。
  mysql> create table t(c1 json not null default (""));Query OK, 0 rows affected (0.03 sec)mysql> create table t(c1 json not null default "");ERROR 1101 (42000): BLOB, TEXT, GEOMETRY or JSON column "c1" can"t have a default value
  2)不允许直接创建索引,可创建函数索引。
  3)JSON 列的最大大小和 LONGBLOB(LONGTEXT)一样,都是 4G。
  4)插入时,单个文档的大小受到 max_allowed_packet 的限制,该参数最大是 1G。
  六、Partial Updates
  在 MySQL 5.7 中,对 JSON 文档进行更新,其处理策略是,删除旧的文档,再插入新的文档。即使这个修改很微小,只涉及几个字节,也会替换掉整个文档。很显然,这种处理方式的效率较为低下。
  在 MySQL 8.0 中,针对 JSON 文档,引入了一项新的特性-Partial Updates(部分更新),支持 JSON 文档的原地更新。得益于这个特性,JSON 文档的处理性能得到了极大提升。
  下面我们具体来看看。
  1、使用 Partial Updates 的条件
  为方便阐述,这里先构造测试数据。
  create table t (id int auto_increment primary key, c1 json);insert into t (c1) values  ("{"id": 1, "name": "a"}"), ("{"id": 2, "name": "b"}"), ("{"id": 3, "name": "c"}"), ("{"id": 4, "name": "d"}");mysql> select * from t;+----+------------------------+| id | c1                     |+----+------------------------+|  1 | {"id": 1, "name": "a"} ||  2 | {"id": 2, "name": "b"} ||  3 | {"id": 3, "name": "c"} ||  4 | {"id": 4, "name": "d"} |+----+------------------------+4 rows in set (0.00 sec)
  使用 Partial Updates 需满足以下条件:
  1)被更新的列是 JSON 类型。
  2)使用 JSON_SET,JSON_REPLACE,JSON_REMOVE 进行 UPDATE 操作,如,
  update t set c1=json_remove(c1,"$.id") where id=1;
  不使用这三个函数,而显式赋值,就不会进行部分更新,如,
  update t set c1="{"id": 1, "name": "a"}" where id=1;
  3)输入列和目标列必须是同一列,如,
  update t set c1=json_replace(c1,"$.id",10) where id=1;
  否则的话,就不会进行部分更新,如,
  update t set c1=json_replace(c2,"$.id",10) where id=1;
  4)变更前后,JSON 文档的空间使用不会增加。
  关于最后一个条件,我们看看下面这个示例。
  mysql> select *,json_storage_size(c1),json_storage_free(c1) from t where id=1;+----+------------------------+-----------------------+-----------------------+| id | c1                     | json_storage_size(c1) | json_storage_free(c1) |+----+------------------------+-----------------------+-----------------------+|  1 | {"id": 1, "name": "a"} |                    27 |                     0 |+----+------------------------+-----------------------+-----------------------+1 row in set (0.00 sec)mysql> update t set c1=json_remove(c1,"$.id") where id=1;Query OK, 1 row affected (0.01 sec)Rows matched: 1  Changed: 1  Warnings: 0mysql> select *,json_storage_size(c1),json_storage_free(c1) from t where id=1;+----+---------------+-----------------------+-----------------------+| id | c1            | json_storage_size(c1) | json_storage_free(c1) |+----+---------------+-----------------------+-----------------------+|  1 | {"name": "a"} |                    27 |                     9 |+----+---------------+-----------------------+-----------------------+1 row in set (0.00 sec)mysql> update t set c1=json_set(c1,"$.id",3306) where id=1;Query OK, 1 row affected (0.01 sec)Rows matched: 1  Changed: 1  Warnings: 0mysql> select *,json_storage_size(c1),json_storage_free(c1) from t where id=1;+----+---------------------------+-----------------------+-----------------------+| id | c1                        | json_storage_size(c1) | json_storage_free(c1) |+----+---------------------------+-----------------------+-----------------------+|  1 | {"id": 3306, "name": "a"} |                    27 |                     0 |+----+---------------------------+-----------------------+-----------------------+1 row in set (0.00 sec)mysql> update t set c1=json_set(c1,"$.id","mysql") where id=1;Query OK, 1 row affected (0.01 sec)Rows matched: 1  Changed: 1  Warnings: 0mysql> select *,json_storage_size(c1),json_storage_free(c1) from t where id=1;+----+------------------------------+-----------------------+-----------------------+| id | c1                           | json_storage_size(c1) | json_storage_free(c1) |+----+------------------------------+-----------------------+-----------------------+|  1 | {"id": "mysql", "name": "a"} |                    33 |                     0 |+----+------------------------------+-----------------------+-----------------------+1 row in set (0.00 sec)
  示例中,用到了两个函数:JSON_STORAGE_SIZE 和 JSON_STORAGE_FREE ,前者用来获取 JSON 文档的空间使用情况,后者用来获取 JSON 文档在执行原地更新后的空间释放情况。
  这里一共执行了三次 UPDATE 操作,前两次是原地更新,第三次不是。同样是 JSON_SET 操作,为什么第一次是原地更新,而第二次不是呢?
  因为第一次的 JSON_SET 复用了 JSON_REMOVE 释放的空间。而第二次的 JSON_SET 执行的是更新操作,且 "mysql" 比 3306 需要更多的存储空间。
  2、如何在 binlog 中开启 Partial Updates
  Partial Updates 不仅仅适用于存储引擎层,还可用于主从复制场景。
  主从复制开启 Partial Updates,只需将参数 binlog_row_value_options(默认为空)设置为 PARTIAL_JSON。
  下面具体来看看,同一个 UPDATE 操作,开启和不开启 Partial Updates,在 binlog 中的记录有何区别。
  update t set c1=json_replace(c1,"$.id",10) where id=1;
  不开启
  ### UPDATE `slowtech`.`t`### WHERE###   @1=1###   @2="{"id": "1", "name": "a"}"### SET###   @1=1###   @2="{"id": 10, "name": "a"}"
  开启
  ### UPDATE `slowtech`.`t`### WHERE###   @1=1###   @2="{"id": 1, "name": "a"}"### SET###   @1=1###   @2=JSON_REPLACE(@2, "$.id", 10)
  对比 binlog 的内容,可以看到,不开启,无论是修改前的镜像(before_image)还是修改后的镜像(after_image),记录的都是完整文档。而开启后,对于修改后的镜像,记录的是命令,而不是完整文档,这样可节省近一半的空间。
  在将 binlog_row_value_options 设置为 PARTIAL_JSON 后,对于可使用 Partial Updates 的操作,在 binlog 中,不再通过 ROWS_EVENT 来记录,而是新增了一个 PARTIAL_UPDATE_ROWS_EVENT 的事件类型。
  需要注意的是,binlog 中使用 Partial Updates,只需满足存储引擎层使用 Partial Updates 的前三个条件,无需考虑变更前后,JSON 文档的空间使用是否会增加。
  3、关于 Partial Updates 的性能测试
  首先构造测试数据,t 表一共有 16 个文档,每个文档近 10 MB。
  create table t(id int auto_increment primary key,               json_col json,               name varchar(100) as (json_col->>"$.name"),               age int as (json_col->"$.age"));insert into t(json_col) values(json_object("name", "Joe", "age", 24,             "data", repeat("x", 10 * 1000 * 1000))),(json_object("name", "Sue", "age", 32,             "data", repeat("y", 10 * 1000 * 1000))),(json_object("name", "Pete", "age", 40,             "data", repeat("z", 10 * 1000 * 1000))),(json_object("name", "Jenny", "age", 27,             "data", repeat("w", 10 * 1000 * 1000)));insert into t(json_col) select json_col from t;insert into t(json_col) select json_col from t;
  接下来,测试下述 SQL
  update t set json_col = json_set(json_col, "$.age", age + 1);
  在以下四种场景下的执行时间:
  MySQL 5.7.36  MySQL 8.0.27  MySQL 8.0.27,binlog_row_value_options=PARTIAL_JSON  MySQL 8.0.27,binlog_row_value_options=PARTIAL_JSON + binlog_row_image=MINIMAL
  分别执行 10 次,去掉最大值和最小值后求平均值。
  最后的测试结果如下:
  以 MySQL 5.7.36 的查询时间作为基准:
  MySQL 8.0 只开启存储引擎层的 Partial Updates,查询时间比 MySQL 5.7 快 1.94 倍。  MySQL 8.0 同时开启存储引擎层和 binlog 中的 Partial Updates,查询时间比 MySQL 5.7 快 4.87 倍。  如果在 2 的基础上,同时将 binlog_row_image 设置为 MINIMAL,查询时间更是比 MySQL 5.7 快 102.22 倍。
  当然,在生产环境,我们一般很少将 binlog_row_image 设置为 MINIMAL。
  但即使如此, 只开启存储引擎层和 binlog 中的 Partial Updates,查询时间也比 MySQL 5.7 快 4.87 倍,性能提升还是比较明显的。
  七、其它 JSON 函数
  1、查询相关
  1)JSON_CONTAINS(target, candidate[, path])
  判断 target 文档是否包含 candidate 文档,如果包含,则返回 1,否则是 0。
  mysql> set @j = "{"a": [1, 2], "b": 3, "c": {"d": 4}}";Query OK, 0 rows affected (0.00 sec)mysql> select json_contains(@j, "1", "$.a"),json_contains(@j, "1", "$.b");+-------------------------------+-------------------------------+| json_contains(@j, "1", "$.a") | json_contains(@j, "1", "$.b") |+-------------------------------+-------------------------------+|                             1 |                             0 |+-------------------------------+-------------------------------+1 row in set (0.00 sec)mysql> select json_contains(@j,"{"d": 4}","$.a"),json_contains(@j,"{"d": 4}","$.c");+------------------------------------+------------------------------------+| json_contains(@j,"{"d": 4}","$.a") | json_contains(@j,"{"d": 4}","$.c") |+------------------------------------+------------------------------------+|                                  0 |                                  1 |+------------------------------------+------------------------------------+1 row in set (0.00 sec)
  2)JSON_CONTAINS_PATH(json_doc, one_or_all, path[, path] ...)
  判断指定的 path 是否存在,存在,则返回 1,否则是 0。
  函数中的 one_or_all 可指定 one 或 all,one 是任意一个路径存在就返回 1,all 是所有路径都存在才返回 1。
  mysql> set @j = "{"a": [1, 2], "b": 3, "c": {"d": 4}}";Query OK, 0 rows affected (0.00 sec)mysql> select json_contains_path(@j, "one", "$.a", "$.e"), json_contains_path(@j, "all", "$.a", "$.e");+---------------------------------------------+---------------------------------------------+| json_contains_path(@j, "one", "$.a", "$.e") | json_contains_path(@j, "all", "$.a", "$.e") |+---------------------------------------------+---------------------------------------------+|                                           1 |                                           0 |+---------------------------------------------+---------------------------------------------+1 row in set (0.00 sec)mysql> select json_contains_path(@j, "one", "$.c.d"),json_contains_path(@j, "one", "$.a.d");+----------------------------------------+----------------------------------------+| json_contains_path(@j, "one", "$.c.d") | json_contains_path(@j, "one", "$.a.d") |+----------------------------------------+----------------------------------------+|                                      1 |                                      0 |+----------------------------------------+----------------------------------------+1 row in set (0.00 sec)
  3)JSON_SEARCH(json_doc, one_or_all, search_str[, escape_char[, path] ...])
  返回某个字符串(search_str)在 JSON 文档中的位置,其中,
  one_or_all:匹配的次数,one 是只匹配一次,all 是匹配所有。如果匹配到多个,结果会以数组的形式返回。  search_str:子串,支持模糊匹配:% 和 _ 。  escape_char:转义符,如果该参数不填或为 NULL,则取默认转义符。  path:查找路径。
  mysql> set @j = "["abc", [{"k": "10"}, "def"], {"x":"abc"}, {"y":"bcd"}]";Query OK, 0 rows affected (0.00 sec)mysql> select json_search(@j, "one", "abc"),json_search(@j, "all", "abc"),json_search(@j, "all", "ghi");+-------------------------------+-------------------------------+-------------------------------+| json_search(@j, "one", "abc") | json_search(@j, "all", "abc") | json_search(@j, "all", "ghi") |+-------------------------------+-------------------------------+-------------------------------+| "$[0]"                        | ["$[0]", "$[2].x"]            | NULL                          |+-------------------------------+-------------------------------+-------------------------------+1 row in set (0.00 sec)mysql> select json_search(@j, "all", "%b%", NULL, "$[1]"), json_search(@j, "all", "%b%", NULL, "$[3]");+---------------------------------------------+---------------------------------------------+| json_search(@j, "all", "%b%", NULL, "$[1]") | json_search(@j, "all", "%b%", NULL, "$[3]") |+---------------------------------------------+---------------------------------------------+| NULL                                        | "$[3].y"                                    |+---------------------------------------------+---------------------------------------------+1 row in set (0.00 sec)
  4)JSON_KEYS(json_doc[, path])
  返回 JSON 文档最外层的 key,如果指定了 path,则返回该 path 对应元素最外层的 key。
  mysql> select json_keys("{"a": 1, "b": {"c": 30}}");+---------------------------------------+| json_keys("{"a": 1, "b": {"c": 30}}") |+---------------------------------------+| ["a", "b"]                            |+---------------------------------------+1 row in set (0.00 sec)mysql> select json_keys("{"a": 1, "b": {"c": 30}}", "$.b");+----------------------------------------------+| json_keys("{"a": 1, "b": {"c": 30}}", "$.b") |+----------------------------------------------+| ["c"]                                        |+----------------------------------------------+1 row in set (0.00 sec)
  5)JSON_VALUE(json_doc, path)
  8.0.21 引入的,从 JSON 文档提取指定路径(path)的元素。
  该函数的完整语法如下:
  JSON_VALUE(json_doc, path [RETURNING type] [on_empty] [on_error])on_empty:    {NULL | ERROR | DEFAULT value} ON EMPTYon_error:    {NULL | ERROR | DEFAULT value} ON ERROR
  其中:
  RETURNING type:返回值的类型,不指定,则默认是 VARCHAR(512)。不指定字符集,则默认是 utf8mb4,且区分大小写。  on_empty:如果指定路径没有值,会触发 on_empty 子句, 默认是返回 NULL,也可指定 ERROR 抛出错误,或者通过 DEFAULT value 返回默认值。  on_error:三种情况下会触发 on_error 子句:从数组或对象中提取元素时,会解析到多个值;类型转换错误,譬如将 "abc" 转换为 unsigned 类型;值被 truncate 了。默认是返回 NULL。
  mysql> select json_value("{"item": "shoes", "price": "49.95"}", "$.item");+-------------------------------------------------------------+| json_value("{"item": "shoes", "price": "49.95"}", "$.item") |+-------------------------------------------------------------+| shoes                                                       |+-------------------------------------------------------------+1 row in set (0.00 sec)mysql> select json_value("{"item": "shoes", "price": "49.95"}", "$.price" returning decimal(4,2)) as price;+-------+| price |+-------+| 49.95 |+-------+1 row in set (0.00 sec)mysql> select json_value("{"item": "shoes", "price": "49.95"}", "$.price1" error on empty);ERROR 3966 (22035): No value was found by "json_value" on the specified path.mysql> select json_value("[1, 2, 3]", "$[1 to 2]" error on error);ERROR 3967 (22034): More than one value was found by "json_value" on the specified path.mysql> select json_value("{"item": "shoes", "price": "49.95"}", "$.item" returning unsigned error on error) as price;ERROR 1690 (22003): UNSIGNED value is out of range in "json_value"
  6)value MEMBER OF(json_array)
  判断 value 是否是 JSON 数组的一个元素,如果是,则返回 1,否则是 0。
  mysql> select 17 member of("[23, "abc", 17, "ab", 10]");+-------------------------------------------+| 17 member of("[23, "abc", 17, "ab", 10]") |+-------------------------------------------+|                                         1 |+-------------------------------------------+1 row in set (0.00 sec)mysql> select cast("[4,5]" as json) member of("[[3,4],[4,5]]");+--------------------------------------------------+| cast("[4,5]" as json) member of("[[3,4],[4,5]]") |+--------------------------------------------------+|                                                1 |+--------------------------------------------------+1 row in set (0.00 sec)
  7)JSON_OVERLAPS(json_doc1, json_doc2)
  MySQL 8.0.17 引入的,用来比较两个 JSON 文档是否有相同的键值对或数组元素,如果有,则返回 1,否则是 0。如果两个参数都是标量,则判断这两个标量是否相等。
  mysql> select json_overlaps("[1,3,5,7]", "[2,5,7]"),json_overlaps("[1,3,5,7]", "[2,6,8]");+---------------------------------------+---------------------------------------+| json_overlaps("[1,3,5,7]", "[2,5,7]") | json_overlaps("[1,3,5,7]", "[2,6,8]") |+---------------------------------------+---------------------------------------+|                                     1 |                                     0 |+---------------------------------------+---------------------------------------+1 row in set (0.00 sec)mysql> select json_overlaps("{"a":1,"b":2}", "{"c":3,"d":4,"b":2}");+-------------------------------------------------------+| json_overlaps("{"a":1,"b":2}", "{"c":3,"d":4,"b":2}") |+-------------------------------------------------------+|                                                     1 |+-------------------------------------------------------+1 row in set (0.00 sec)mysql> select json_overlaps("{"a":1,"b":2}", "{"c":3,"d":4,"b":10}");+--------------------------------------------------------+| json_overlaps("{"a":1,"b":2}", "{"c":3,"d":4,"b":10}") |+--------------------------------------------------------+|                                                      0 |+--------------------------------------------------------+1 row in set (0.00 sec)mysql> select json_overlaps("5", "5"),json_overlaps("5", "6");+-------------------------+-------------------------+| json_overlaps("5", "5") | json_overlaps("5", "6") |+-------------------------+-------------------------+|                       1 |                       0 |+-------------------------+-------------------------+1 row in set (0.00 sec)
  从 MySQL 8.0.17 开始,InnoDB 支持多值索引,可用在 JSON 数组中。当我们使用 JSON_CONTAINS、MEMBER OF、JSON_OVERLAPS 进行数组相关的操作时,可使用多值索引来加快查询。
  2、修改相关
  1)JSON_ARRAY_APPEND(json_doc, path, val[, path, val] ...)
  向数组指定位置追加元素。如果指定 path 不存在,则不添加。
  mysql> set @j = "["a", ["b", "c"], "d"]";Query OK, 0 rows affected (0.00 sec)mysql> select json_array_append(@j, "$[0]", 1, "$[1][0]", 2, "$[3]", 3);+-----------------------------------------------------------+| json_array_append(@j, "$[0]", 1, "$[1][0]", 2, "$[3]", 3) |+-----------------------------------------------------------+| [["a", 1], [["b", 2], "c"], "d"]                          |+-----------------------------------------------------------+1 row in set (0.00 sec)mysql> set @j = "{"a": 1, "b": [2, 3], "c": 4}";Query OK, 0 rows affected (0.00 sec)mysql> select json_array_append(@j, "$.b", "x", "#39;, "z");+---------------------------------------------+| json_array_append(@j, "$.b", "x", "#39;, "z") |+---------------------------------------------+| [{"a": 1, "b": [2, 3, "x"], "c": 4}, "z"]   |+---------------------------------------------+1 row in set (0.00 sec)
  2)JSON_ARRAY_INSERT(json_doc, path, val[, path, val] ...)
  向数组指定位置插入元素。
  mysql> set @j = "["a", ["b", "c"],{"d":"e"}]";Query OK, 0 rows affected (0.00 sec)mysql> select json_array_insert(@j, "$[0]", 1);+----------------------------------+| json_array_insert(@j, "$[0]", 1) |+----------------------------------+| [1, "a", ["b", "c"], {"d": "e"}] |+----------------------------------+1 row in set (0.00 sec)mysql> select json_array_insert(@j, "$[1]", cast("[1,2]" as json));+------------------------------------------------------+| json_array_insert(@j, "$[1]", cast("[1,2]" as json)) |+------------------------------------------------------+| ["a", [1, 2], ["b", "c"], {"d": "e"}]                |+------------------------------------------------------+1 row in set (0.00 sec)mysql> select json_array_insert(@j, "$[5]", 2);+----------------------------------+| json_array_insert(@j, "$[5]", 2) |+----------------------------------+| ["a", ["b", "c"], {"d": "e"}, 2] |+----------------------------------+1 row in set (0.00 sec)
  3)JSON_MERGE_PATCH(json_doc, json_doc[, json_doc] ...)
  MySQL 8.0.3 引入的,用来合并多个 JSON 文档。其合并规则如下:
  如果两个文档不全是 JSON 对象,则合并后的结果是第二个文档。  如果两个文档都是 JSON 对象,且不存在着同名 KEY,则合并后的文档包括两个文档的所有元素,如果存在着同名 KEY,则第二个文档的值会覆盖第一个。
  mysql> select json_merge_patch("[1, 2]", "[3, 4]"), json_merge_patch("[1, 2]", "{"a": 123}");+--------------------------------------+------------------------------------------+| json_merge_patch("[1, 2]", "[3, 4]") | json_merge_patch("[1, 2]", "{"a": 123}") |+--------------------------------------+------------------------------------------+| [3, 4]                               | {"a": 123}                               |+--------------------------------------+------------------------------------------+1 row in set (0.00 sec)mysql> select json_merge_patch("{"a": 1}", "{"b": 2}"),json_merge_patch("{ "a": 1, "b":2 }","{ "a": 3, "c":4 }");+------------------------------------------+-----------------------------------------------------------+| json_merge_patch("{"a": 1}", "{"b": 2}") | json_merge_patch("{ "a": 1, "b":2 }","{ "a": 3, "c":4 }") |+------------------------------------------+-----------------------------------------------------------+| {"a": 1, "b": 2}                         | {"a": 3, "b": 2, "c": 4}                                  |+------------------------------------------+-----------------------------------------------------------+1 row in set (0.00 sec)# 如果第二个文档存在 null 值,文档合并后不会输出对应的 KEY。mysql> select json_merge_patch("{"a":1, "b":2}", "{"a":3, "b":null}");+---------------------------------------------------------+| json_merge_patch("{"a":1, "b":2}", "{"a":3, "b":null}") |+---------------------------------------------------------+| {"a": 3}                                                |+---------------------------------------------------------+1 row in set (0.00 sec)
  4)JSON_MERGE_PRESERVE(json_doc, json_doc[, json_doc] ...)
  MySQL 8.0.3 引入的,用来代替 JSON_MERGE。也是用来合并文档,但合并规则与 JSON_MERGE_PATCH 有所不同。
  两个文档中,只要有一个文档是数组,则另外一个文档会合并到该数组中。  两个文档都是 JSON 对象,若存在着同名 KEY ,第二个文档并不会覆盖第一个,而是会将值 append 到第一个文档中。
  mysql> select json_merge_preserve("1","2"),json_merge_preserve("[1, 2]", "[3, 4]");+------------------------------+-----------------------------------------+| json_merge_preserve("1","2") | json_merge_preserve("[1, 2]", "[3, 4]") |+------------------------------+-----------------------------------------+| [1, 2]                       | [1, 2, 3, 4]                            |+------------------------------+-----------------------------------------+1 row in set (0.00 sec)mysql> select json_merge_preserve("[1, 2]", "{"a": 123}"), json_merge_preserve("{"a": 123}", "[3,4]");+---------------------------------------------+--------------------------------------------+| json_merge_preserve("[1, 2]", "{"a": 123}") | json_merge_preserve("{"a": 123}", "[3,4]") |+---------------------------------------------+--------------------------------------------+| [1, 2, {"a": 123}]                          | [{"a": 123}, 3, 4]                         |+---------------------------------------------+--------------------------------------------+1 row in set (0.00 sec)mysql> select json_merge_preserve("{"a": 1}", "{"b": 2}"), json_merge_preserve("{ "a": 1, "b":2 }","{ "a": 3, "c":4 }");+---------------------------------------------+--------------------------------------------------------------+| json_merge_preserve("{"a": 1}", "{"b": 2}") | json_merge_preserve("{ "a": 1, "b":2 }","{ "a": 3, "c":4 }") |+---------------------------------------------+--------------------------------------------------------------+| {"a": 1, "b": 2}                            | {"a": [1, 3], "b": 2, "c": 4}                                |+---------------------------------------------+--------------------------------------------------------------+1 row in set (0.00 sec)
  5)JSON_MERGE(json_doc, json_doc[, json_doc] ...)
  与 JSON_MERGE_PRESERVE 作用一样,从 MySQL 8.0.3 开始不建议使用,后续会移除。
  3、其它辅助函数
  1)JSON_QUOTE(string)
  生成有效的 JSON 字符串,主要是对一些特殊字符(如双引号)进行转义。
  mysql> select json_quote("null"), json_quote(""null""), json_quote("[1, 2, 3]");+--------------------+----------------------+-------------------------+| json_quote("null") | json_quote(""null"") | json_quote("[1, 2, 3]") |+--------------------+----------------------+-------------------------+| "null"             | ""null""           | "[1, 2, 3]"             |+--------------------+----------------------+-------------------------+1 row in set (0.00 sec)
  除此之外,也可通过 CAST(value AS JSON) 进行类型转换。
  2)JSON_UNQUOTE(json_val)
  将 JSON 转义成字符串输出。
  mysql> select c2->"$.ename",json_unquote(c2->"$.ename"),    -> json_valid(c2->"$.ename"),json_valid(json_unquote(c2->"$.ename")) from t;+---------------+-----------------------------+---------------------------+-----------------------------------------+| c2->"$.ename" | json_unquote(c2->"$.ename") | json_valid(c2->"$.ename") | json_valid(json_unquote(c2->"$.ename")) |+---------------+-----------------------------+---------------------------+-----------------------------------------+| "jack"        | jack                        |                         1 |                                       0 || "mark"        | mark                        |                         1 |                                       0 |+---------------+-----------------------------+---------------------------+-----------------------------------------+2 rows in set (0.00 sec)
  直观地看,没加 JSON_UNQUOTE 字符串会用双引号引起来,加了 JSON_UNQUOTE 就没有。但本质上,前者是 JSON 中的 STRING 类型,后者是 MySQL 中的字符类型,这一点可通过 JSON_VALID 来判断。
  3)JSON_OBJECTAGG(key, value)
  取表中的两列作为参数,其中,第一列是 key,第二列是 value,返回 JSON 对象。如,
  mysql> select * from emp;+--------+----------+--------+| deptno | ename    | sal    |+--------+----------+--------+|     10 | emp_1001 | 100.00 ||     10 | emp_1002 | 200.00 ||     20 | emp_1003 | 300.00 ||     20 | emp_1004 | 400.00 |+--------+----------+--------+4 rows in set (0.00 sec)mysql> select json_objectagg(ename,sal) from emp;+----------------------------------------------------------------------------------+| json_objectagg(ename,sal)                                                        |+----------------------------------------------------------------------------------+| {"emp_1001": 100.00, "emp_1002": 200.00, "emp_1003": 300.00, "emp_1004": 400.00} |+----------------------------------------------------------------------------------+1 row in set (0.00 sec)mysql> select deptno,json_objectagg(ename,sal) from emp group by deptno;+--------+------------------------------------------+| deptno | json_objectagg(ename,sal)                |+--------+------------------------------------------+|     10 | {"emp_1001": 100.00, "emp_1002": 200.00} ||     20 | {"emp_1003": 300.00, "emp_1004": 400.00} |+--------+------------------------------------------+2 rows in set (0.00 sec)
  4)JSON_ARRAYAGG(col_or_expr)
  将列的值聚合成 JSON 数组,注意,JSON 数组中元素的顺序是随机的。
  mysql> select json_arrayagg(ename) from emp;+--------------------------------------------------+| json_arrayagg(ename)                             |+--------------------------------------------------+| ["emp_1001", "emp_1002", "emp_1003", "emp_1004"] |+--------------------------------------------------+1 row in set (0.00 sec)mysql> select deptno,json_arrayagg(ename) from emp group by deptno;+--------+--------------------------+| deptno | json_arrayagg(ename)     |+--------+--------------------------+|     10 | ["emp_1001", "emp_1002"] ||     20 | ["emp_1003", "emp_1004"] |+--------+--------------------------+2 rows in set (0.00 sec)
  5)JSON_PRETTY(json_val)
  将 JSON 格式化输出。
  mysql> select json_pretty("[1,3,5]");+------------------------+| json_pretty("[1,3,5]") |+------------------------+| [  1,  3,  5]      |+------------------------+1 row in set (0.00 sec)mysql> select json_pretty("{"a":"10","b":"15","x":"25"}");+---------------------------------------------+| json_pretty("{"a":"10","b":"15","x":"25"}") |+---------------------------------------------+| {  "a": "10",  "b": "15",  "x": "25"}   |+---------------------------------------------+1 row in set (0.00 sec)
  6)JSON_STORAGE_FREE(json_val)
  MySQL 8.0 新增的,与 Partial Updates 有关,用于计算 JSON 文档在进行部分更新后的剩余空间。
  7)JSON_STORAGE_SIZE(json_val)
  MySQL 5.7.22 引入的,用于计算 JSON 文档的空间使用情况。
  8)JSON_DEPTH(json_doc)
  返回 JSON 文档的最大深度。对于空数组,空对象,标量值,其深度为 1。
  mysql> select json_depth("{}"),json_depth("[10, 20]"),json_depth("[10, {"a": 20}]");+------------------+------------------------+-------------------------------+| json_depth("{}") | json_depth("[10, 20]") | json_depth("[10, {"a": 20}]") |+------------------+------------------------+-------------------------------+|                1 |                      2 |                             3 |+------------------+------------------------+-------------------------------+1 row in set (0.00 sec)
  9)JSON_LENGTH(json_doc[, path])
  返回 JSON 文档的长度,其计算规则如下:
  如果是标量值,其长度为 1。  如果是数组,其长度为数组元素的个数。  如果是对象,其长度为对象元素的个数。  不包括嵌套数据和嵌套对象的长度。
  mysql> select json_length(""abc"");+----------------------+| json_length(""abc"") |+----------------------+|                    1 |+----------------------+1 row in set (0.00 sec)mysql> select json_length("[1, 2, {"a": 3}]");+---------------------------------+| json_length("[1, 2, {"a": 3}]") |+---------------------------------+|                               3 |+---------------------------------+1 row in set (0.00 sec)mysql> select json_length("{"a": 1, "b": {"c": 30}}");+-----------------------------------------+| json_length("{"a": 1, "b": {"c": 30}}") |+-----------------------------------------+|                                       2 |+-----------------------------------------+1 row in set (0.00 sec)mysql> select json_length("{"a": 1, "b": {"c": 30}}", "$.a");+------------------------------------------------+| json_length("{"a": 1, "b": {"c": 30}}", "$.a") |+------------------------------------------------+|                                              1 |+------------------------------------------------+1 row in set (0.00 sec)
  10)JSON_TYPE(json_val)
  返回 JSON 值的类型。
  mysql> select json_type("123");+------------------+| json_type("123") |+------------------+| INTEGER          |+------------------+1 row in set (0.00 sec)mysql> select json_type(""abc"");+--------------------+| json_type(""abc"") |+--------------------+| STRING             |+--------------------+1 row in set (0.00 sec)mysql> select json_type(cast(now() as json));+--------------------------------+| json_type(cast(now() as json)) |+--------------------------------+| DATETIME                       |+--------------------------------+1 row in set (0.00 sec)mysql> select json_type(json_extract("{"a": [10, true]}", "$.a"));+-----------------------------------------------------+| json_type(json_extract("{"a": [10, true]}", "$.a")) |+-----------------------------------------------------+| ARRAY                                               |+-----------------------------------------------------+1 row in set (0.00 sec)
  11)JSON_VALID(val)
  判断给定值是否是有效的 JSON 文档。
  mysql> select json_valid("hello"), json_valid(""hello"");+---------------------+-----------------------+| json_valid("hello") | json_valid(""hello"") |+---------------------+-----------------------+|                   0 |                     1 |+---------------------+-----------------------+1 row in set (0.00 sec)
  12)JSON_TABLE(expr, path COLUMNS (column_list) [AS] alias)
  从 JSON 文档中提取数据并以表格的形式返回。
  该函数的完整语法如下:
  JSON_TABLE(    expr,    path COLUMNS (column_list))   [AS] aliascolumn_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 EMPTYon_error:    {NULL | DEFAULT json_string | ERROR} ON ERROR
  其中,
  expr:可以返回 JSON 文档的表达式。可以是一个标量( JSON 文档 ),列名或者一个函数调用( JSON_EXTRACT(t1.json_data,"$.post.comments") )。  path:JSON 的路径表达式,  column:列的类型,支持以下四种类型:  name FOR ORDINALITY: 序号。name 是列名。  name type PATH string_path [on_empty] [on_error]: 提取指定路径( string_path )的元素。name 是列名,type 是 MySQL 中的数据类型。  name type EXISTS PATH string_path: 指定路径( string_path )的元素是否存在。  NESTED [PATH] path COLUMNS (column_list): 将嵌套对象或数组与来自父对象或数组的 JSON 值扁平化为一行输出。
  select * from   json_table(     "[{"x":2, "y":"8", "z":9, "b":[1,2,3]}, {"x":"3", "y":"7"}, {"x":"4", "y":6, "z":10}]",     "$[*]" columns(       id for ordinality,       xval varchar(100) path "$.x",       yval varchar(100) path "$.y",       z_exist int exists path "$.z",       nested path "$.b[*]" columns (b INT PATH "#39;)     )   ) as t;+------+------+------+---------+------+| id   | xval | yval | z_exist | b    |+------+------+------+---------+------+|    1 | 2    | 8    |       1 |    1 ||    1 | 2    | 8    |       1 |    2 ||    1 | 2    | 8    |       1 |    3 ||    2 | 3    | 7    |       0 | NULL ||    3 | 4    | 6    |       1 | NULL |+------+------+------+---------+------+5 rows in set (0.00 sec)
  13)JSON_SCHEMA_VALID(schema,document)
  判断 document ( JSON 文档 )是否满足 schema ( JSON 对象)定义的规范要求。完整的规范要求可参考 Draft 4 of the JSON Schema specification (https://json-schema.org/specification-links.html#draft-4)。如果不满足,可通过 JSON_SCHEMA_VALIDATION_REPORT() 获取具体的原因。
  以下面这个 schema 为例。
  set @schema = "{   "type": "object",   "properties": {     "latitude": {       "type": "number",       "minimum": -90,       "maximum": 90     },     "longitude": {       "type": "number",       "minimum": -180,       "maximum": 180     }   },   "required": ["latitude", "longitude"]}";
  它的要求如下:
  document 必须是 JSON 对象。  JSON 对象必需的两个属性是 latitude 和 longitude。  latitude 和 longitude 必须是数值类型,且两者的大小分别在 -90   90,-180   180 之间。
  下面通过具体的 document 来测试一下。
  mysql> set @document = "{"latitude": 63.444697,"longitude": 10.445118}";Query OK, 0 rows affected (0.00 sec)mysql> select json_schema_valid(@schema, @document);+---------------------------------------+| json_schema_valid(@schema, @document) |+---------------------------------------+|                                     1 |+---------------------------------------+1 row in set (0.00 sec)mysql> set @document = "{"latitude": 63.444697}";Query OK, 0 rows affected (0.00 sec)mysql> select json_schema_valid(@schema, @document);+---------------------------------------+| json_schema_valid(@schema, @document) |+---------------------------------------+|                                     0 |+---------------------------------------+1 row in set (0.00 sec)mysql> select json_pretty(json_schema_validation_report(@schema, @document))G*************************** 1. row ***************************json_pretty(json_schema_validation_report(@schema, @document)): {  "valid": false,  "reason": "The JSON document location "#" failed requirement "required" at JSON Schema location "#"",  "schema-location": "#",  "document-location": "#",  "schema-failed-keyword": "required"}1 row in set (0.00 sec)mysql> set @document = "{"latitude": 91,"longitude": 0}";Query OK, 0 rows affected (0.00 sec)mysql> select json_schema_valid(@schema, @document);+---------------------------------------+| json_schema_valid(@schema, @document) |+---------------------------------------+|                                     0 |+---------------------------------------+1 row in set (0.00 sec)mysql> select json_pretty(json_schema_validation_report(@schema, @document))G*************************** 1. row ***************************json_pretty(json_schema_validation_report(@schema, @document)): {  "valid": false,  "reason": "The JSON document location "#/latitude" failed requirement "maximum" at JSON Schema location "#/properties/latitude"",  "schema-location": "#/properties/latitude",  "document-location": "#/latitude",  "schema-failed-keyword": "maximum"}1 row in set (0.00 sec)
  八、总结
  如果要使用 JSON 类型,推荐使用 MySQL 8.0。相比于 MySQL 5.7,Partial update 带来的性能提升还是十分明显的。
  Partial update 在存储引擎层是默认开启的,binlog 中是否开启取决于 binlog_row_value_options 。该参数默认为空,不会开启 Partial update,建议设置为 PARTIAL_JSON。
  注意使用 Partial update 的前提条件。
  当我们使用 JSON_CONTAINS、MEMBER OF、JSON_OVERLAPS 进行数组相关的操作时,可使用 MySQL 8.0.17 引入的多值索引来加快查询。
  >>>>
  参考资料
  JSON  https://zh.wikipedia.org/wiki/JSON  The JSON Data Type  https://dev.mysql.com/doc/refman/8.0/en/json.html  JSON Functions  https://dev.mysql.com/doc/refman/8.0/en/json-functions.html  Upgrading JSON data stored in TEXT columns  https://dev.mysql.com/blog-archive/upgrading-json-data-stored-in-text-columns/  Indexing JSON documents via Virtual Columns  https://dev.mysql.com/blog-archive/indexing-json-documents-via-virtual-columns/  Partial update of JSON values  https://dev.mysql.com/blog-archive/partial-update-of-json-values/  MySQL 8.0: InnoDB Introduces LOB Index For Faster Updates  https://dev.mysql.com/blog-archive/mysql-8-0-innodb-introduces-lob-index-for-faster-updates/
  作者丨陈臣
  来源丨公众号:MySQL实战(ID:MySQLInAction)
  dbaplus社群欢迎广大技术人员投稿,投稿邮箱:editor@dbaplus.cn
  关于我们
  dbaplus社群 是围绕Database、BigData、AIOps的企业级专业社群。资深大咖、技术干货,每天精品原创文章推送,每周线上技术分享,每月线下技术沙龙,每季度Gdevops&DAMS行业大会。
  关注公众号【dbaplus社群】,获取更多原创技术文章和精选工具下载

开源装机工具Ventoy1。0。90发布现支持超过1100种ISO镜像IT之家3月27日消息,开源装机工具Ventoy于近日发布了1。0。90版本更新,添加了对ChimeraLinux以及近期发布的LibreELEC11。0等GNULinux发行版本地理位置太优越!中国这4座城市永远不会落幕,你知道是哪里吗?我国的大江南北,山川壮美,景色迷人。其中,拥有着独特地理位置的城市,不仅可以欣赏到绝美的自然景观,还能享受到其他城市无可比拟的资源和福利。这些城市凭借其独特的地理位置,成为了无论经专精特新小巨人巡礼中国的顶流商圈在哪里?编者按专精特新中小企业,这组热词最近在重要场合被屡屡提及。自2022年3月专精特新被首次写入政府工作报告,今年的政府工作报告给出了有关专精特新的具体进展专精特新中小企业达7万多家,全天候的音乐伴侣创新OUTLIERFREE骨传导耳机体验CREATIVE终于上新了创新OUTLIERFREE!起初我看到OUTLIER字眼以为是真无线耳机的升级款,然后去JD翻了翻他们家宣传页,Emmm我发现创新真的不太会吹捧自己,一切过敏娃半年追重5斤,总结敏宝长肉4大要点,适合所有敏宝!今天的文章是一位敏宝妈妈写的。她大概是在一年前了解到我们的调理方法,前期观摩半年,后又认真调理半年,有一天跟我反馈说孩子半年增重5斤,要知道以前是整年不长肉的,抱起来也觉得孩子身体痒在娃身,挠在妈心!水痘高发季,济南疾控带来战痘攻略!齐鲁网闪电新闻3月27日每年春季,都会有一种病毒偷袭宝宝们。它不是严重的疾病,但痒在娃身,挠在妈心!说到这想必有些宝爸宝妈已经猜到了,它就是水痘。济南市疾控专家提醒,目前已进入水痘三月山大,赏花正妙阳春三月,草长莺飞这样的日子当然要和小伙伴一起出门赏花啦!此刻,山大校园里一路繁花,一路芬芳趁春日烂漫快和姗姗一起赏花吧!花映明德楼盛开的鲜花与春天撞个满怀,阳光正好,微风拂过,心2023年奉节县五马采茶节3月31日开幕五马镇茶林。陈登俊摄又到一年采茶好时节,经过一冬的修整,奉节县五马镇千亩茶山已是吐绿纳新满园飘香,茶树绿芽丰盈,芽尖随春风跳动,呈现出勃勃生机。在樟木村的1000多亩茶园里,茶农们如何通过无线路由器管理系统隐藏自己的wifi,避免被蹭网目前几乎全民上网,人们已经对wifi不再陌生,通过无线路由器可以把有线网络信号转换成无线信号共享给家中多人多设备,也就是我们常说的连wifi上网。wifi利用2。4GHZ或5GHzText。SplitAny拆分到列案例本期案例是前后转换效果如下显示。解题步骤1。单元格数据看着很复杂,实际上就多分隔符的拆分,对于其中的0也是多余的字符,这里也可以起作为拆分的字符,因此这里拆分函数的第二参数就包括了大招都安排上了,美国急的直跳脚当地时间23日美国国会众议对中国互联网巨头字节跳动旗下Tiktok公司举行听证会。新加坡裔CEO站在一个公正的立场,语言干练,逻辑清晰的回应了一众政客的刁难。首先给年轻帅小伙鼓鼓掌
当红女星们演过的农村角色,谁的扮相最颠覆形象头条创作挑战赛人生之路正在热播,李沁饰演的农村姑娘刘巧珍的颠覆性形象引起了热议。官方海报长这样正片截图长这样附上大家记忆中的李沁这天壤地别的形象差异确实让人大跌眼镜。事实上,除了李湖北省十堰市茅箭区庭院小经济撬动乡村大振兴来源人民网湖北频道南部山区俯瞰图。近年来,湖北省十堰市茅箭区立足城区后花园区位优势,紧盯特色产业前景,因地制宜因村制宜因人制宜引导广大农户发展种养殖庭院经济,拓宽群众增收渠道,瞄准6名成都农村娃,收到欧洲足球俱乐部邀请记者丨殷建6个成都孩子,平均年龄12岁,且从未接受过专业的足球训练但就是这样一群孩子,最近收到了欧洲名门俱乐部贝尔格莱德红星和沃日多瓦茨俱乐部的试训邀请。更值得一提的是,6个孩子均皇家驿站汉服文化节暨第二届古风古建古韵摄影大赛启动驻马店网讯(记者王晓晶文图)4月5日皇家驿站汉服文节暨第二届古风古建古韵摄影大赛启动仪式在皇家驿站景区盛大举行。汉礼仪式花神巡游汉服快闪登高祈福吸引了众多汉服同袍摄影家媒体及游客慕日本8336米深海惊现怪鱼!它究竟有多独特?核污水特产吗大千世界无奇不有,尤其是对于目前人类还知之甚少的海洋世界!据相关数据统计,地球上海洋面积大约有3。6亿平方公里以上,占据总表面积的71。而人类从远古时代到如今,主要的活跃范围还是在山东省城市规模划定2个特大城市,8个大城市和32个中小城市山东省,地处我国华东地区,面积15。58万平方公里,拥有超过1个亿的常住人口。山东省近年来一直在推动创新发展,在2019年8月,山东省政府就印发了关于深化创新型省份建设若干措施的通潼关在哪?单枪匹马去考察,才知什么叫雄关历史开讲本科毕业后笔者有意愿去看世界,特别想去看关中四塞,但总是俗事繁多或资金不足,一直没机会去。本科毕业10年后,终于有机会过来看看(就像人民币上的6个景点,各去一趟真的不难,可上海发布超千亿元重大文旅投资项目新华社上海4月4日电(记者陈爱平)上海市人民政府4日主办第二届上海旅游投资促进大会,集中发布2023年上海重大文旅签约投资项目28个,总投资金额为1080亿元。这28个项目包括奉贤抽时间,把我忘了吧我相信爱情不死,但我担心,会不会逐渐凋零?是的,抽时间忘了我吧,我不想等到白首,才等到自己变年轻的一天。时间可以鄙视生死,但虎嗅不敢贪污爱情。没事的时候是最抽心的时候,往往是往事带古龙风云录发售时间发售日期古龙风云录于4月1日上架steam,根据steam商店页面显示游戏将会于2023年12月正式发售,敬请期待。古龙风云录是由河洛工作室出品的正统武侠新作,是一款开放世界武侠单崇明这座大钟,是多少人心中的一盏灯塔在每个城市或地区,总有那么一个地点一个建筑,展示着当地的文化风貌,陪伴那里生活的人们,见证时代的变迁,成为一道独特的风景。我们将推出崇明寻迹系列,找寻散落在崇明三岛的这些地标,发现