时间:2023-10-29来源:系统城装机大师作者:佚名
最近在做项目时,表 A 有多个字段,其中一个字段 info
把当前项目用不到的冗余的数据按照 JSON 格式都存了进来。随着项目的推进,有些冗余字段需要单独成一列。新增一列之后,需要把 info
中对应的数据刷入新增列,这就需要从 MySQL 中读取 JSON 数据。
当时想到的方法,就是写个程序,批量查询数据,把 info
字段查询出来后反序列化,取出其中的key-value,然后再存入数据库。后面查询资料,发现 MySQL 已经提供了从 JSON 数据中查找和比较的函数,极大地方便了数据处理!
这是在没有写这篇文章前,查询资料写出来的刷数据SQL,其实还有优化的空间,等文章最后我们一起看下吧!
1 2 3 4 5 6 |
select id, replace ( replace (json_extract(`info`, '$.budget_mode' ), '"' , '' ), 'null' , '' ), replace ( replace (json_extract(`info`, '$.budget' ), '"' , '' ), 'null' , 0), replace ( replace (json_extract(`info`, '$.bid' ), '"' , '' ), 'null' , 0), from table where code = 'xxx' ; |
其次,为了方便后面的学习和测试,我们新建一张表,建表语句如下:
1 2 3 4 5 |
create table `userinfo` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键' PRIMARY KEY , `info` longtext NOT NULL COMMENT '用户信息' ); |
接下来我们就看下MySQL提供的 JSON 查询和比较函数,比较常用的应该就是 JSON_EXTRACT 、column->path、column->>path 和 JSON_VALUE 四个函数,可以按需学习哦!
该函数用于判断一个 JSON 文档是否包含另一个 JSON 文档。如果提供了路径,用于判断 JSON 文档相应路径下的数据是否包含另一个JSON 文档。
语法
JSON_CONTAINS(target,candidate[,path])
返回值
规则
测试
1 2 3 4 5 6 7 8 9 10 11 |
insert into userinfo (id, info) values (1, '{"a": 1, "b": 2, "c": {"d": 4},"d":[1,2,3]}' ); select JSON_CONTAINS(info, '1' ) from userinfo where id=1; # 0, target 不包含 JSON '1' select JSON_CONTAINS(info, '1' , '$.a' ) from userinfo where id=1; # 1, 两个简单类型, 1 包含 1 select JSON_CONTAINS(info, '1' , '$.d' ) from userinfo where id=1; # 1, 非数组和数组类型比较, [1,2,3] 包含 1 select JSON_CONTAINS(info, '[1,2]' , '$.d' ) from userinfo where id=1; # 1, 两个数组类型比较, [1,2,3] 包含 数组类型 [1,2] select JSON_CONTAINS(info, '[1,2,4]' , '$.d' ) from userinfo where id=1; # 0, 两个数组类型比较, [1,2,3] 不包含 数组类型 [1,2,4] select JSON_CONTAINS(info, '{"a":1}' ) from userinfo where id=1; # 1, 两个对象比较, target 中存在 key 'a' ,且 value 包含 select JSON_CONTAINS(info, '{"a":2}' ) from userinfo where id=1; # 0, 两个对象比较, target 存在 key 'a' ,但 value 不包含 select JSON_CONTAINS(info, '{"d":2}' ) from userinfo where id=1; # 1, 两个对象比较, target 存在 key 'd' ,且 value 包含 select JSON_CONTAINS(info, '{"a":1,"d":2}' ) from userinfo where id=1; # 1, 两个对象比较, target 存在 key 'a' 和 'd' ,且 value 均包含 select JSON_CONTAINS(info, '{"a":1,"d":[2,3]}' ) from userinfo where id=1; # 1, 两个对象比较, target 存在 key 'a' 和 'd' ,且 value 均包含 |
该函数用于判断一个 JSON 文档是否包含一个或者多个路径 path
语法
JSON_CONTAINS_PATH(json_doc, one_or_all, path[, path...])]
返回值
测试
1 2 3 4 5 |
insert into userinfo (id, info) values (2, '{"a": 1, "b": 2, "c": {"d": 4}}' ); select JSON_CONTAINS_PATH(info, 'one' , '$.a' ) from userinfo where id=2; # 1, a 存在于 路径中 select JSON_CONTAINS_PATH(info, 'one' , '$.a' , '$.e' ) from userinfo where id=2 ; # 1, 至少一个存在即可,且路径 a 存在 select JSON_CONTAINS_PATH(info, 'all' , '$.a' , '$.e' ) from userinfo where id=2; # 0, 必须所有路径都存在,但路径 e 不存在中 select JSON_CONTAINS_PATH(info, 'all' , '$.c.d' ) from userinfo where id=2; # 1, 路径 c.d 存在 |
该函数用于从 JSON 字段中查询路径 path 对应的 value 值
语法
JSON_EXTRACT(json_doc, path[,path...])
返回值
1 2 3 4 5 6 7 8 |
insert into userinfo (id, info) values (3, '{"a": 1, "b": 2, "c": {"d": 4},"d":[1,2,3],"e":{"name":"tom","age":12}}' ); select json_extract(info, '$.a' ) from userinfo where id=3; # 1 select json_extract(info, '$.c.d' ) from userinfo where id=3; # 4 select json_extract(info, '$.d' ) from userinfo where id=3; # [1,2,3] select json_extract(info, '$.d[0]' ) from userinfo where id=3; # 1 select json_extract(info, '$.d[3]' ) from userinfo where id=3; # NULL select json_extract(info, '$.f' ) from userinfo where id=3; # NULL select json_extract(info, '$.a' , '$.b' , '$.c' , '$.d' , '$.e.name' , '$.e.age' , '$.f' ) from userinfo where id=3; # [1, 2, { "d" : 4}, [1, 2, 3], "tom" , 12] |
如果只查询一个 path,可以使用接下来介绍的 -> 操作符
JSON_EXTRACT 只有两个参数时的缩写。
如下两个查询是等价的:
1 2 3 4 5 6 7 8 9 |
select info,info-> '$.a' as info_a from userinfo where info-> '$.a' >0 ; select info,JSON_EXTRACT(info, '$.a' ) as info_a from userinfo where JSON_EXTRACT(info, '$.a' )>0; + -----------------------------------------------------------------------+------+ |info |info_a| + -----------------------------------------------------------------------+------+ |{ "a" : 1, "b" : 2, "c" : { "d" : 4}, "d" :[1,2,3]} |1 | |{ "a" : 1, "b" : 2, "c" : { "d" : 4}} |1 | |{ "a" : 1, "b" : 2, "c" : { "d" : 4}, "d" :[1,2,3], "e" :{ "name" : "tom" , "age" :12}}|1 | + -----------------------------------------------------------------------+------+ |
和列操作一样,这个符号可以用于 where条件、order by 条件等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
select info,info-> '$.a' as a, info-> '$.c.d' as info_c_d from userinfo where info-> '$.d' is not null ; + -----------------------------------------------------------------------+-+--------+ |info |a|info_c_d| + -----------------------------------------------------------------------+-+--------+ |{ "a" : 1, "b" : 2, "c" : { "d" : 4}, "d" :[1,2,3]} |1|4 | |{ "a" : 1, "b" : 2, "c" : { "d" : 4}, "d" :[1,2,3], "e" :{ "name" : "tom" , "age" :12}}|1|4 | + -----------------------------------------------------------------------+-+--------+ select info,info-> '$.a' as a,info-> '$.c.d' as info_c_d from userinfo where info-> '$.d[0]' >0 order by '$.a' ; + -----------------------------------------------------------------------+-+--------+ |info |a|info_c_d| + -----------------------------------------------------------------------+-+--------+ |{ "a" : 1, "b" : 2, "c" : { "d" : 4}, "d" :[1,2,3]} |1|4 | |{ "a" : 1, "b" : 2, "c" : { "d" : 4}, "d" :[1,2,3], "e" :{ "name" : "tom" , "age" :12}}|1|4 | + -----------------------------------------------------------------------+-+--------+ |
'->>' 符号相对于 '->',增加了去除引号
的功能。如果一个 JSON 文档中,key 对应的 value 是字符串类型,那么如下三个表达式返回相同的结果:
1 2 3 4 |
select info-> '$.e.name' as name from userinfo where id=3; # "tom" select json_unquote(json_extract(info, '$.e.name' )) as name from userinfo where id =3; # tom select json_unquote(info-> '$.e.name' ) as name from userinfo where id=3; # tom select info->> '$.e.name' as name from userinfo where id=3; # tom |
该函数用于返回 JSON 文档或者指定 path 下最顶层的所有 key
语法
JSON_KEYS(json_doc,[path])
返回值
测试
1 2 3 4 5 6 7 8 9 10 11 12 |
select info,json_keys(info) from userinfo where id=1; + -------------------------------------------+--------------------+ |info |json_keys(info) | + -------------------------------------------+--------------------+ |{ "a" : 1, "b" : 2, "c" : { "d" : 4}, "d" :[1,2,3]}|[ "a" , "b" , "c" , "d" ]| + -------------------------------------------+--------------------+ select info,json_keys(info-> '$.c' ) from userinfo where id=1; + -------------------------------------------+----------------------+ |info |json_keys(info-> '$.c' )| + -------------------------------------------+----------------------+ |{ "a" : 1, "b" : 2, "c" : { "d" : 4}, "d" :[1,2,3]}|[ "d" ] | + -------------------------------------------+----------------------+ |
该函数用于判断两个JSON文档是否有重叠
语法
JSON_OVERLAPS(json_doc1, json_doc2)
返回值
重叠逻辑
测试
两个基础元素,就是简单的判等操作
1 2 |
SELECT JSON_OVERLAPS( '5' , '5' ); # 1, 相等 SELECT JSON_OVERLAPS( '"5"' , '5' ); # 0, 类型不同,不相等 |
对于数组,需要有元素相同;如果是多维数组,子数组元素需要完全一样
1 2 3 4 5 |
SELECT JSON_OVERLAPS( "[1,3,5,7]" , "[2,5,7]" ); # 1, 存在相同的元素 5 和 7 SELECT JSON_OVERLAPS( "[1,3,5,7]" , "[2,6,7]" ); # 1, 存在相同的元素 7 SELECT JSON_OVERLAPS( "[1,3,5,7]" , "[2,6,8]" ); # 0, 没有相同元素 SELECT JSON_OVERLAPS( '[[1,2],[3,4],5]' , '[1,[2,3],[4,5]]' ); # 0, 没有相同元素 SELECT JSON_OVERLAPS( '[[1,2],[3,4],5]' , '[[1,2],[2,3],[4,5]]' ); # 1, 有相同元素 [1,2] |
如果是对象,需要 key-value 完全一样
1 2 3 |
SELECT JSON_OVERLAPS( '{"a":1,"b":10,"d":10}' , '{"c":1,"e":10,"f":1,"d":10}' ); # 1, 相同 key -value "d" :10 SELECT JSON_OVERLAPS( '{"a":1,"b":10,"d":10}' , '{"a":5,"e":10,"f":1,"d":20}' ); # 0, 没有相同元素 SELECT JSON_OVERLAPS( '{"a":1,"b":10,"d":[20,30]}' , '{"a":5,"e":10,"f":1,"d":[20]}' ); # 0, 没有相同元素 |
如果一个基础类型和数组类型比较,基础类型会被转成数组类型
1 2 |
SELECT JSON_OVERLAPS( '[4,5,6,7]' , '6' ); # 1, [4,5,6,7]和 [6] 有相同元素 6 SELECT JSON_OVERLAPS( '[4,5,6,7]' , '"6"' ); # 0, 类型不同,没有相同元素 |
对于给定的字符串,返回该字符串在 JSON 文档中的路径
语法
JSON_SEARCH(json_doc, one_or_all, search_str, escape_char, path...)
json_doc: 必填。JSON文档
one_or_all: 必填。取值只能为 one 或者 all
search_str: 必填。要查询的字符串,可以使用通配符
escape_char: 可选。如果 search_str 中包含 %
和 _
,需要在他们之前添加转移字符。默认是 \
。
path: 可选。指定在具体路径下搜索
返回值
JSON_SEARCH() 函数返回一个给定字符串在一个 JSON 文档中的路径。它返回一个路径字符串或者由多个路径组成的数组。
JSON_SEARCH() 函数将在以下情况下返回 NULL:
JSON_SEARCH() 函数将在以下情况下返回错误:
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 |
SET @json_doc = '["abc", [{"k": "10"}, "def"], {"x":"abc"}, {"y":"bcd"}]' ; select JSON_SEARCH(@json_doc, 'one' , 'abc' ); # "$[0]" select JSON_SEARCH(@json_doc, 'all' , 'abc' ); # [ "$[0]" , "$[2].x" ] select JSON_SEARCH(@json_doc, 'all' , 'ghi' ); # null select JSON_SEARCH(@json_doc, 'all' , '10' ); # "$[1][0].k" -- 指定路径 select JSON_SEARCH(@json_doc, 'all' , '10' , NULL , '$[*][0].k' ); # "$[1][0].k" select JSON_SEARCH(@json_doc, 'all' , '10' , NULL , '$[1][0]' ); # "$[1][0].k" select JSON_SEARCH(@json_doc, 'all' , 'abc' , NULL , '$[2]' ); # "$[2].x" -- 通配符 select JSON_SEARCH(@json_doc, 'all' , '%a%' ); # [ "$[0]" , "$[2].x" ] select JSON_SEARCH(@json_doc, 'all' , '%b%' ); # [ "$[0]" , "$[2].x" , "$[3].y" ] select JSON_SEARCH(@json_doc, 'all' , '%b%' , NULL , '$[2]' ); # "$[2].x" |
该函数的作用是:查询 JSON 文档 path 下的值
语法
JSON_VALUE(json_doc, path [RETURNING type] [on_empty] [on_error])
on_empty:NULLERROR | DEFAULT valueON EMPTY
on_error:NULLERROR | DEFAULT valueON ERROR
参数
json_doc: 必填。JSON文档
path: 必填。指定的路径
RETURNING type: 可选。将结果转为指定的类型,可以为如下类型:
NULLERROR | DEFAULT valueON EMPTY
可选。如果指定了,它决定了指定路径下没有数据的返回值:
NULL ON EMPTY: 如果指定路径下没有数据,JSON_VALUE() 函数将返回 NULL,这是默认的行为。
DEFAULT value ON EMPTY: 如果指定路径下没有数据,JSON_VALUE`() 函数将返回 value。
ERROR ON EMPTY: 如果指定路径下没有数据,JSON_VALUE() 函数将抛出一个错误。
NULLERROR | DEFAULT valueON ERROR
可选的。如果指定了,它决定了处理错误的逻辑:
返回值
默认以字符串的格式,返回 JSON 文档在指定的路径上的值;如果使用 RETURNING type 子句,会把结果转为 type 类型
测试
1 2 3 |
SELECT JSON_VALUE( '{"fname": "Joe", "lname": "Palmer"}' , '$.fname' ); # Joe SELECT JSON_VALUE( '{"item": "shoes", "price": "49.95"}' , '$.price' RETURNING DECIMAL (4,2)); # 49.95 SELECT JSON_VALUE( '{"item": "shoes", "price": "49.95"}' , '$.total' DEFAULT 100.00 ON EMPTY); # 100.00 |
该函数用于判断value,是否是数组 json_array 的元素
语法
value MEMBER_OF (json_array)
value: 必填。任意值,可以是一个简单类型或者 JSON
json_array: 必填。一个JSON数组
返回值
如果 value 是 json_array 中的元素,返回1;否则返回0
测试
1 2 3 4 5 |
SELECT 17 MEMBER OF ( '[23, "abc", 17, "ab", 10]' ); # 1 SELECT '17' MEMBER OF ( '[23, "abc", 17, "ab", 10]' ); # 0, 类型不一致 SELECT 'ab' MEMBER OF ( '[23, "abc", 17, "ab", 10]' ); # 1 SELECT CAST ( '[4,5]' AS JSON) MEMBER OF ( '[[3,4],[4,5]]' ); # 1 SELECT JSON_ARRAY(4,5) MEMBER OF ( '[[3,4],[4,5]]' ); # 1 |
现在我们可以回过头来看下文章开头要优化的SQL:
这是表 info 字段存储的数据,如果字段有数据,存储对应的数据类型;如果没有数据,存储 null。但是 string 类型的 value 有引号,我们想去掉引号;其次对于 null 值,也想替换成默认值
1 2 3 4 5 |
{ "ulink" : null , "budget_mode" : "BUDGET_MODE_DAY" , "hide_if_exists" :0 } |
之前我们的SQL 是这样的
1 2 3 4 5 6 |
select id, replace ( replace (json_extract(`info`, '$.budget_mode' ), '"' , '' ), 'null' , '' ), replace ( replace (json_extract(`info`, '$.budget' ), '"' , '' ), 'null' , 0), replace ( replace (json_extract(`info`, '$.bid' ), '"' , '' ), 'null' , 0), from table where code = 'xxx' ; |
json_extract 是为了拿到对应的 value,里面的 replace()是为了去掉引号,外面的 replace 是为了将 null 替换为默认值。对于去掉引号,我们可以使用 column ->> path 简化:
1 2 3 4 5 6 |
select id, replace (info ->> '$.budget_mode' , 'null' , '' ), replace (info ->> '$.budget' , 'null' , 0), replace (info ->> '$.bid' , 'null' , 0) from ad_ad where id = 6993; |
本篇文章一共介绍了如下几个函数:
引号
的功能到此这篇关于MySQL实现查询处理JSON数据的示例详解的文章就介绍到这了,
2023-10-30
windows上的mysql服务突然消失提示10061 Unkonwn error问题及解决方案2023-10-30
MySQL非常重要的日志bin log详解2023-10-30
详解MySQL事务日志redo log一、单表查询 1、排序 2、聚合函数 3、分组 4、limit 二、SQL约束 1、主键约束 2、非空约束 3、唯一约束 4、外键约束 5、默认值 三、多表查询 1、内连接 1)隐式内连接: 2)显式内连接: 2、外连接 1)左外连接 2)右外连接 四...
2023-10-30
Mysql删除表重复数据 表里存在唯一主键 没有主键时删除重复数据 Mysql删除表中重复数据并保留一条 准备一张表 用的是mysql8 大家自行更改 创建表并添加四条相同的数据...
2023-10-30