博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
mysql的json特性的应用
阅读量:6690 次
发布时间:2019-06-25

本文共 4907 字,大约阅读时间需要 16 分钟。

  hot3.png

概述

说实话,个人不是很喜欢这种特性,关系型数据库就应该有关系型数据库的样子,而不是为了留住用户而强加给他一些nosql的特性,而且还没有人家做的好,这样反而会造成一种四不像的感觉。sql和nosql是互补的,不是竞争关系。

那既然碰到了就学习下吧,我们从使用者的角度来看,json的特性是否能完成以前结构化数据的操作以及不足之处

函数

如下,是官方文档中所支持的JSON操作

Name

Description

 (deprecated 5.7.9)

Append data to JSON document

Create JSON array

Append data to JSON document

Insert into JSON array

Return value from JSON column after evaluating path; equivalent to JSON_EXTRACT().

Whether JSON document contains specific object at path

Whether JSON document contains any data at path

Maximum depth of JSON document

Return data from JSON document

Return value from JSON column after evaluating path and unquoting the result; equivalent to JSON_UNQUOTE(JSON_EXTRACT()).

Insert data into JSON document

Array of keys from JSON document

Number of elements in JSON document

 (deprecated 5.7.22)

Merge JSON documents, preserving duplicate keys. Deprecated synonym for JSON_MERGE_PRESERVE()

Merge JSON documents, replacing values of duplicate keys

Merge JSON documents, preserving duplicate keys

Create JSON object

Prints a JSON document in human-readable format, with each array element or object member printed on a new line, indented two spaces with respect to its parent.

Quote JSON document

Remove data from JSON document

Replace values in JSON document

Path to value within JSON document

Insert data into JSON document

Space used for storage of binary representation of a JSON document; for a JSON column, the space used when the document was inserted, prior to any partial updates

Type of JSON value

Unquote JSON value

Whether JSON value is valid

这里还需要加上一个下面的函数,功能是吧json的字符串转成对象,否则会真的当成字符串插入到mysql中,下面会碰到这个问题

CAST('{"a":"b"}' AS JSON

准备

创建表

CREATE TABLE `user` (
`uid` INT(11) NOT NULL AUTO_INCREMENT,
`info` JSON NULL DEFAULT NULL,
`a` VARCHAR(50) NULL DEFAULT NULL,
`b` VARCHAR(50) NULL DEFAULT NULL,
PRIMARY KEY (`uid`),
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB

如上,info字段是JSON类型

新增

insert into user(info) values('{"mail": "abc", "name": "tomab", "address": "e"}');

和普通的插入操作一样,只不过值是符合json规范的字符串,不规范插不进去

查找

SELECT uid,json_extract(info,'$.mail') AS 'mail',json_extract(info,'$.name') AS 'name' FROM USER;

这样查找出来的数据会带了双引号“”,很不爽,我们使用JSON_UNQUOTE函数处理下

SELECT uid,JSON_UNQUOTE(json_extract(info,'$.mail')) AS 'mail',json_extract(info,'$.name') AS 'name' FROM USER;

这样mail字段就没有双引号了

大家也能看出来,这种取数据的方式,充斥了大量的JSON_UNQUOTE,json_extract和美元符号,相比结构化,sql数据长了不少,而且必须要as成pojo里对应的属性,否则你的属性名称会变成类似json_extract(info,'$.name'),这样ORM框架就不能正确的设值了

mysql也意识到了,所以提供了->和->>符合来代替,代码稍微简洁点,但是as依然必不可少

SELECT uid,info->'$.address',info->'$.name' AS 'name' FROM USER;
SELECT uid,info->>'$.address',info->>'$.name' AS 'name' FROM USER;

模糊查找和排序

SELECT * FROM user where info->'$.address' = 'e' and info->'$.name' like '%o%' order by info->'$.name'

确实都支持,但是没有索引,数据量大时,效率不会很高

好在mysql提供了虚拟列的功能,可以把json的属性建立成虚拟列,然后在该列上加索引即可,这里强调一下,虚拟列和json的特性没关系,只是刚好能用上而已,而且虚拟列在json特性之前就有了

ALTER TABLE user ADD user_name varchar(128) GENERATED ALWAYS AS (json_extract(info,'$.name')) VIRTUAL;
ALTER TABLE user ADD INDEX index_name (a,b,user_name)

该列是可以通过desc user来看到的,然后加上索引即可,并且该列是可以和表的普通列建立联合索引的

MySQL 5.7中,支持两种Generated Column,即Virtual Generated Column和Stored Generated Column,前者只将Generated Column保存在数据字典中(表的元数据),并不会将这一列数据持久化到磁盘上;后者会将Generated Column持久化到磁盘上,而不是每次读取的时候计算所得。很明显,后者存放了可以通过已有数据计算而得的数据,需要更多的磁盘空间

可以看到,无论哪种虚拟列,都增加了额外的操作或者空间,效率比结构化的数据要低

修改

覆盖

update user set info = '{"mail": "abc", "name": "tomab", "address": "e"}';

这种修改,直接把内容当做一个字符串覆盖,简单粗暴

部分修改

这种操作首先必须要完全了解存储的json的格式,把他当做一个json对象而不是字符串

修改对象可以使用,,,,

json_replace:只替换已经存在的旧值

json_set:替换旧值,并插入不存在的新值,这是最常用的

json_insert:插入新值,但不替换已经存在的旧值

:有相同的属性会覆盖前面的,例子见

:有相同的属性不会覆盖,会变成一个数组

修改对象

比如有这样一个需求,想把mail的值变成一个对象,即类似如下:

{
"mail": {
"a": "b"
},
"name": "David",
"address": "Shangahai"
}

屁颠的使用

update user set info=JSON_SET(info,'$.mail','{"a":"b"}') where uid=5;

发现,mysql把参数当做字符串来处理了,结果如下:

{
"mail": "{\"a\":\"b\"}",
"name": "co",
"address": "e"
}

不是我们想要的,有两种方式可以完成

一:使用上面说的CAST函数

update user set info=JSON_SET(info,'$.mail',CAST('{"a":"b"}' AS JSON )) where uid=5;

二:使用JSON_OBJECT 函数

update user set info=JSON_SET(info,'$.mail',JSON_OBJECT('a','b')) where uid=5;

可以看到,显然第一钟比较爽,使用mybatis操作时,sql类似如下:

update user set info=json_set(info,'$.mail',CAST(#{a} AS JSON )) where id=1

参数即是pojo转成的字符串

第二种参数是个变长数组,麻烦多了,见

修改数组

修改数组可以使用,

删除

要删除某个属性,可以通过JSON_REMOVE来操作即可

update user set info=JSON_REMOVE(info,'$.mailx') where uid=5;

其他

  • 有了虚拟列,似乎一张表只需包含一个int类型的id,一个json类型的content就行了,需要搜索和排序的通过建立虚拟列的方式,但是如果列比较多会增加mysql本身的维护成本

  • json的格式要固定,不能随意更改,因为代码是和格式强耦合的,如果变了那要大改,所以这就是mysql和mongodb这类nosql的一个区别,mysql并不是无模式的,对于一张表的那个json字段,模式其实也是固化在json里面而已

  • 对单体类修改,mybatis自动生成的类不能用,需要自己写sql,属性名称千万不能弄错,没有语法提示,错了不容易发现

  • 开发成本(写代码)的成本增加

  • 维护的成本增加,如果后面需要对json里面的另外一个字段进行模糊查找和排序,那么得增加虚拟列,重建索引,代码也要改,而如果是结构化的数据,只要一个DDL即可

  • json看似更灵活,其实非常不灵活,远不如结构的数据灵活

  • 通过update的语句,我们其实可以看出来部分修改的操作,比如JSON_SET,并不是实际只修改部分的数据,而是把全量的数据加载到内存,然后修改部分数据,在把修改后的全量的数据设值到mysql中,只不过mysql提供了函数让我们方便的操作json而已

转载于:https://my.oschina.net/dxqr/blog/1925419

你可能感兴趣的文章
Asp.Net Core 项目实战之权限管理系统(8) 功能菜单的动态加载
查看>>
使用CSS让元素尺寸缩小时保持宽高比例一致
查看>>
HDU-2955-Robberies
查看>>
如何使Linux系统上的程序开机后自动运行 (转)
查看>>
Silverlight中 Content="{TemplateBinding Content}" bug
查看>>
Jsoup后台解析html、jsp网页
查看>>
中间件详解,Django复习
查看>>
微信小程序 md5加密
查看>>
python gui之tkinter事件处理
查看>>
Android Studio 1.1.0 切换主题和绑定 代码提示 快捷键
查看>>
读书笔记 UltraGrid(8)
查看>>
Spring Boot文档维护:集成Swagger2
查看>>
SharePoint 2010 部署架构
查看>>
BZOJ[3992][SDOI2015]序列统计 生成函数+NTT
查看>>
GUI自绘_其中左边树状菜单控件风格灵感来源于城市博物馆的壁灯效果。
查看>>
SSH 相关基础
查看>>
浅谈git和svn的区别
查看>>
JMETER 生成测试报告
查看>>
ScrollView中嵌套ListView
查看>>
XML再深入
查看>>