MySQL5.7.x新增JSON数据类型的解析

在这里插入图片描述

文章中所有操作均是在 MySQL 5.7 版本下进行的

测试表和测试数据

我们先创建示例表然后入几条测试数据:

create table tbl_json (
  id 	int(11) not null auto_increment,
  info 	json default null,
  primary key (id)
) ENGINE=InnoDB default charset=utf8;

insert into tbl_json (info) values ('{"age": 21, "name": "小张"}');
insert into tbl_json (info) values ('{"age": 22, "name": "小李"}');
insert into tbl_json (info) values ('{"age": 23, "name": "小王"}');

select * from tbl_json;

+----+-------------------------------+
| id | info                          |
+----+-------------------------------+
|  1 | {"age": 21, "name": "小张"}   |
|  2 | {"age": 22, "name": "小李"}   |
|  3 | {"age": 23, "name": "小王"}   |
+----+-------------------------------+

如果你使用的工具版本比较低的话,是可能看到不到 json 类型字段的。

比如 navicat 低版本就看不到 json 类型的字段。

几个JSON数据相关的函数

json_extract函数

json_extract 函数是用来解析 json 并取 key 对应的数据。

select json_extract('{"name":"Lee","tel":"130000000"}','$.name') as name;

+-------+
| name  |
+-------+
| "Lee" |
+-------+

如果没有对应的 key 会返回 NULL。

select json_extract('{"name":"Lee","tel":"130000000"}','$.age') as age;

+------+
| age  |
+------+
| NULL |
+------+

试一试测试表:

select id, json_extract(info, '$.name') as name from tbl_json where id = 3;

+----+----------+
| id | name     |
+----+----------+
|  3 | "小王"   |
+----+----------+

json_type函数

json_type 函数是用来查看 key 对应的数据类型的。

select json_extract('{"name":"Lee","tel":"130000000"}','$.name') as name, json_type(json_extract('{"name":"Lee","tel":"130000000"}', '$.name')) as type;

+-------+--------+
| name  | type   |
+-------+--------+
| "Lee" | STRING |
+-------+--------+

试一试测试表:

select id, json_extract(info, '$.name') as name, json_type(json_extract(info, '$.name')) as type from tbl_json where id = 3;

+----+----------+--------+
| id | name     | type   |
+----+----------+--------+
|  3 | "小王"   | STRING |
+----+----------+--------+

json_array函数

json_array 函数是用来生成一个 json 数据组。

select json_array('aaa', 'bbb', 'ccc') as array;

+-----------------------+
| array                 |
+-----------------------+
| ["aaa", "bbb", "ccc"] |
+-----------------------+

json_object函数

json_object 函数是用来生成一个 json 对象。

select json_object('1', 'aaa', '2', 'bbb', '3', 'ccc') as object;

+--------------------------------------+
| object                               |
+--------------------------------------+
| {"1": "aaa", "2": "bbb", "3": "ccc"} |
+--------------------------------------+

json_merge函数

json_merge 函数是用来将多个 json 数据进行合并。

select json_merge(json_array('aaa', 'bbb', 'ccc'), json_object('1', 'aaa', '2', 'bbb', '3', 'ccc')) as json;

+-------------------------------------------------------------+
| json                                                        |
+-------------------------------------------------------------+
| ["aaa", "bbb", "ccc", {"1": "aaa", "2": "bbb", "3": "ccc"}] |
+-------------------------------------------------------------+

json_unquote函数

json_unqote 函数的作用就是将对应 key 的数据去掉引号。

select json_extract('{"name":"Lee","tel":"130000000"}','$.name') as name;

+-------+
| name  |
+-------+
| "Lee" |
+-------+

通过json_unquote 函数处理之后:

select json_unquote(json_extract('{"name":"Lee","tel":"130000000"}','$.name')) as name;

+------+
| name |
+------+
| Lee  |
+------+

试一试测试表:

select id, json_unquote(json_extract(info, '$.name')) as name from tbl_json;

+----+--------+
| id | name   |
+----+--------+
|  1 | 小张   |
|  2 | 小李   |
|  3 | 小王   |
+----+--------+

MySQL中的JSON的查询

MySQL 里的 json 查询基本是针对 json array 和 json object 进行操作。

对于 json array,在索引数据时用从 0 开始的下标进行索引, 表 示 整 个 j s o n 对 象 。 例 如 : 表示整个 json 对象。例如: json[0]、$[1]。

对于 json object,在索引数据时用 key 进行索引,含有特殊字符的 key 要用 “” 双引号括起来,比如 $.“user name”。

json_extract 函数的简便应用:json列 -> ‘$.键’ 。

json_unquote 函数的简便应用: json列 ->> ‘$.键’ 。

提取 json 字段的表达式可以用于 select 查询列表 ,where / having,order / group by 语句中,json 中的元素搜索也是严格区分变量类型。

json 类型不同于字符串,不能当作字符串直接做比较,通过 cast() 将字符串转换成 json 形式,再进行比较。

json列 -> '$.键’查询

-- json_extract函数
select id, json_extract(info, '$.name') as name from tbl_json;
-- 也可以用以下形式
select id, info->'$.name' as name from tbl_json;

+----+----------+
| id | name     |
+----+----------+
|  1 | "小张"   |
|  2 | "小李"   |
|  3 | "小王"   |
+----+----------+

-- 用到条件筛选中
select id, json_extract(info, '$.name') as name from tbl_json where json_extract(info, '$.age') >= 22; 
-- 也可以以用下形式
select id, info->'$.name' as name from tbl_json where info->'$.age' >= 22;

+----+----------+
| id | name     |
+----+----------+
|  2 | "小李"   |
|  3 | "小王"   |
+----+----------+

json列 ->> '$.键’查询

-- json_extract函数
select id, json_unquote(json_extract(info, '$.name')) as name from tbl_json;
-- 也可以用以下形式
select id, info->>'$.name' as name from tbl_json;

+----+--------+
| id | name   |
+----+--------+
|  1 | 小张   |
|  2 | 小李   |
|  3 | 小王   |
+----+--------+

复杂情况下的查询

加入新数据:

insert into tbl_json (info) values ('["abc", {"xxx": [123, 456], "user name": "worker lee"}, [666, 888]]');

select * from tbl_json;

+----+---------------------------------------------------------------------+
| id | info                                                                |
+----+---------------------------------------------------------------------+
|  1 | {"age": 21, "name": "小张"}                                         |
|  2 | {"age": 22, "name": "小李"}                                         |
|  3 | {"age": 23, "name": "小王"}                                         |
|  4 | ["abc", {"xxx": [123, 456], "user name": "worker lee"}, [666, 888]] |
+----+---------------------------------------------------------------------+

select * from tbl_json where id = 4;

+----+---------------------------------------------------------------------+
| id | info                                                                |
+----+---------------------------------------------------------------------+
|  4 | ["abc", {"xxx": [123, 456], "user name": "worker lee"}, [666, 888]] |
+----+---------------------------------------------------------------------+

-- 查询json array的某个值
select info->'$[0]' from tbl_json where id = 4;

+--------------+
| info->'$[0]' |
+--------------+
| "abc"        |
+--------------+

select info->'$[1]' from tbl_json where id = 4;

+------------------------------------------------+
| info->'$[1]'                                   |
+------------------------------------------------+
| {"xxx": [123, 456], "user name": "worker lee"} |
+------------------------------------------------+

-- 查询json array中的json object
select info->'$[1].xxx' from tbl_json where id = 4;

+------------------+
| info->'$[1].xxx' |
+------------------+
| [123, 456]       |
+------------------+

-- key用双引号引起来也可以
select info->'$[1]."xxx"' from tbl_json where id = 4;

+--------------------+
| info->'$[1]."xxx"' |
+--------------------+
| [123, 456]         |
+--------------------+

-- key如果含有特殊的必须用双引号引起来
/*
select info->'$[1].user name' from tbl_json where id = 4;
以上命令报错,是因为“user name”这个key中间有空格属于含有特殊字符,正确如下
*/
select info->'$[1]."user name"' from tbl_json where id = 4;

+--------------------------+
| info->'$[1]."user name"' |
+--------------------------+
| "worker lee"             |
+--------------------------+

-- 去掉查询数据的双引号
select info->>'$[1]."user name"' from tbl_json where id = 4;

+---------------------------+
| info->>'$[1]."user name"' |
+---------------------------+
| worker lee                |
+---------------------------+

json字段的查询筛选

-- 查询所有
select * from tbl_json;

+----+---------------------------------------------------------------------+
| id | info                                                                |
+----+---------------------------------------------------------------------+
|  1 | {"age": 21, "name": "小张"}                                         |
|  2 | {"age": 22, "name": "小李"}                                         |
|  3 | {"age": 23, "name": "小王"}                                         |
|  4 | ["abc", {"xxx": [123, 456], "user name": "worker lee"}, [666, 888]] |
+----+---------------------------------------------------------------------+

-- 加入条件,比如查询第1个数据,但是下面这种是筛选不出来的
-- select * from tbl_json where info = '{"age": 21, "name": "小张"}';

-- 应该使用cast()函数转换
select * from tbl_json where info = cast('{"age": 21, "name": "小张"}' as json);

+----+-------------------------------+
| id | info                          |
+----+-------------------------------+
|  1 | {"age": 21, "name": "小张"}   |
+----+-------------------------------+

MySQL中的JSON的数据操作

之前我们介绍了各种和 json 有关的函数操作,并且通过示例应用到了我们的 SQL 查询语句中。那现在就介绍一下 json 类型的字段是如何进行数据操作的。这里介绍的操作不是向表里增加数据,能看到这里的朋友,前面文章中也描述了如何 insert 一条数据,下面是介绍如何针对 json 类型的字段的操作。

json_insert函数

json_insert 函数插入新字段,对于已存在的 key 无法修改值。

-- 利用json_insert函数增加一个key数据
update tbl_json set info = json_insert(info, '$.sex', '男') where id = 1;
-- 查询之前的修改
select * from tbl_json where id = 1;

+----+---------------------------------------------+
| id | info                                        |
+----+---------------------------------------------+
|  1 | {"age": 21, "sex": "男", "name": "小张"}     |
+----+---------------------------------------------+

-- 如果json_insert加入了之前已经存在的key,是无法修改原先key对应的value
update tbl_json set info = json_insert(info, '$.name', '张三') where id = 1;
-- 查询之前的修改,没有发生变化
select * from tbl_json where id = 1;

+----+---------------------------------------------+
| id | info                                        |
+----+---------------------------------------------+
|  1 | {"age": 21, "sex": "男", "name": "小张"}     |
+----+---------------------------------------------+

json_set函数

json_set 函数插入新值字段,如何存在 key 会覆盖已经存在 key 的值。

-- 利用json_set函数增加一个key数据
update tbl_json set info = json_set(info, '$.num', '1001') where id = 1;
-- 查询之前的修改
select * from tbl_json where id = 1;

+----+------------------------------------------------------------+
| id | info                                                       |
+----+------------------------------------------------------------+
|  1 | {"age": 21, "num": "1001", "sex": "男", "name": "小张"}     |
+----+------------------------------------------------------------+

-- 如果json_set加入了之前已经存在的key,直接覆盖原先key对应的value
update tbl_json set info = json_set(info, '$.name', '张三') where id = 1;
-- 查询之前的修改
select * from tbl_json where id = 1;

+----+------------------------------------------------------------+
| id | info                                                       |
+----+------------------------------------------------------------+
|  1 | {"age": 21, "num": "1001", "sex": "男", "name": "张三"}     |
+----+------------------------------------------------------------+

json_replace函数和json_remove函数

json_replace 函数只能用来替换原有 key 对应的数据,json_remove 函数是删除对应的 key 的 json 元素。

-- json_replace函数
update tbl_json set info = json_replace(info, '$.sex', '女') where id = 1;     
-- 查询之前的修改
select * from tbl_json where id = 1;

+----+------------------------------------------------------------+
| id | info                                                       |
+----+------------------------------------------------------------+
|  1 | {"age": 21, "num": "1001", "sex": "女", "name": "张三"}     |
+----+------------------------------------------------------------+

-- json_remove函数
update tbl_json set info = json_remove(info, '$.num') where id = 1;     
-- 查询之前的修改
select * from tbl_json where id = 1;

+----+---------------------------------------------+
| id | info                                        |
+----+---------------------------------------------+
|  1 | {"age": 21, "sex": "女", "name": "张三"}     |
+----+---------------------------------------------+

MySQL中的JSON的索引

MySQL 官网文档中有段对 json 字段的说明是:

JSON columns cannot be indexed. You can work around this restriction by creating an index on a generated column that extracts a scalar value from the JSON column.

那意思就是现阶段 MySQL 不支持对 json 列进行索引,但是可以通过映射虚拟列(virtual generated column),在虚拟列上建立索引,便可参与高效检索。

在 MySQL 5.7+ 中,支持两种 Generated Column,即 Virtual Generated Column 和 Stored Generated Column。前者不存储元数据,后者会将 expression 的计算结果实际的存储下来。其实二者性能差距并不大,若对二者建立索引进行检索操作,前者性能可能会略低于后者,因为前者要对结果集即时的进行 expression 的演算,但后者需要消耗额外的存储空间。

虚拟列具体操作如下(仅供参考):

-- 为了方便这里将之前的id=4的数据删除了
delete from tbl_json where id = 4;

-- 创建虚拟列,去掉引号的name
alter table tbl_json add name varchar(50) generated always as (info->>'$.name') virtual;
/*
删除虚拟列?!其实就是“alter table 表名 drop 列名”
有的朋友可能通过info->'$.name'创建的是带双引号的虚拟列
在双引号的数据查询记得用 \ 转义
比如:select * from tbl_json where name="\"张三\"";
*/
-- 查询数据
select * from tbl_json;

+----+---------------------------------------------+--------+
| id | info                                        | name   |
+----+---------------------------------------------+--------+
|  1 | {"age": 21, "sex": "女", "name": "张三"}    | 张三   |
|  2 | {"age": 22, "sex": "男", "name": "小李"}    | 小李   |
|  3 | {"age": 23, "sex": "男", "name": "小王"}    | 小王   |
+----+---------------------------------------------+--------+

-- 添加索引
alter table tbl_json add index index_name(name);

-- 检索时可以使用索引
explain select * from tbl_json where name = '张三';
-- 整理之后的打印信息,已使用了刚才创建的索引
id : 1
select_type : SIMPLE
table : tbl_json
partitions : NULL
type : ref
possible_keys : index_name
key : index_name
key_len : 153
ref : const
rows : 1
filtered : 100.00
Extra : NULL

结语

这篇文章从开始到结束,都是作者一条一条命令试的,即使有部分内容是借鉴网络上的资源,为了负责也是每条命令去试的,保证不会出什么问题。

随着技术的进步,数据库也增加了对 json 的支持,这个支持也就是所谓的在数据库层面进行技术的更新。其实以往我们是不是可以在一个 varchar 类型存一个 json 串,通过程序获取将 json 的处理放到了程序中,我想很多朋友早就这么干了。现在 MySQL 数据库增加了 json 的支持,我们就可以多一条路子选择,是把 json 解析给数据库处理还是继续拿出来程序中解析,这个不是争论的焦点,只是选择层面而已。

你可能感兴趣的:(MySQL,mysql,数据库)