MySQL JSON数据类型全解析(JSON datatype and functions)

JSON(JavaScript Object Notation)是一种常见的信息交换格式,其简单易读且非常适合程序处理。MySQL从5.7版本开始支持JSON数据类型,本文对MySQL中JSON数据类型的使用进行一个总结。

目录

一、MySQL中的JSON

1.1 JSON数据格式

1.2 创建JSON格式数据

1.2.1 JSON类型列

1.2.2 json_array/json_object/cast函数

二、JSON数据操作

2.1 JSON数据查询

2.1.1 json_extract()函数

2.1.2 column->path写法

2.1.3 json_value()函数

2.1.4 json_keys()函数

2.2 JSON数据新增

2.2.1 json_array_append()函数

2.2.2 json_array_insert()函数

2.3 JSON数据更新

2.3.1 json_replace()函数

2.4 JSON数据同时新增和更新

2.4.1 json_set()函数

2.5 JSON数据删除

2.5.1 json_remove()函数

2.6 JSON数据合并

2.6.1 json_merge_preserve()函数

2.6.2 json_merge_patch()函数

2.7 判断元素否存在

2.7.1 json_contains_path()函数

2.7.2 json_search()函数

2.7.3 value member of(json_array)

2.8 判断JSON对象之间的关系

2.8.1 json_contains()函数

2.8.2 json_overlaps()函数

三、JSON数据与索引

3.1 生成列索引(Generated Column Index)

3.2 多值索引(Multi-valued Index)

四、将JSON转换为结构化数据

4.1 json_table()函数


一、MySQL中的JSON

在MySQL中使用JSON类型的数据有2个好处:

  • 自动数据格式校验,无效的JSON格式会报错
  • 数据快速解析,MySQL提供了多种函数,可以快速读取和操作JSON格式的数据

1.1 JSON数据格式

在MySQL中,JSON数据是以字符串形式表现的,但是它有自己的解析规则,利用某些途径可以将字符串转换为JSON类型数据,这个过程叫做规范化(normalization),在规范化过程中,MySQL会对数据格式进行验证,若字符串不是有效的JSON格式,那么就会报错。

规范化除了数据格式验证,还会对数据进行一些预处理,例如JOSN对象的键值去重,排序,将boolean类型转换为小写等,规范化后的数据即被视为JSON格式数据。

JSON数据的有效格式有两种:

  • JSON数组,包含在[]中的一系列值,例如: [1, 2, 'a', 'b']
  • JSON对象,包含在{}中的键值对,键和值之间用冒号分隔(键必须是字符串),键值对之间以逗号分隔,如: {'key1':'value1', 'key2':'value2'}

在JSON对象中,键是不能重复的,如果出现相同的键,规范化时后值会覆盖前值(MySQL 8.0.3之前保留先出现的值)。

两种JSON也格式可以互相嵌套:

  • [1, 2, 'a', {'key1':'value1', 'key2':'value2'}]  --> JSON数组中某个元素是JSON对象
  • {'key1':'value1', 'key2':[1, 2, 'a', 'b']}  --> JSON对象中某个值是JSON数组

1.2 创建JSON格式数据

MySQL中主要有下列几种方式对值进行规范化,并转换成JSON格式:

  • 将数据插入json类型的列中
  • json_array() 函数将一组值转换为json数组
  • json_object() 函数将一组键值对转换为json对象
  • cast(value as json) 会根据提供的字符串格式转换为JSON数组或JSON对象

1.2.1 JSON类型列

将表中列的类型定义为json,则所有插入的数据都会被规范化为JSON格式数据,这是最常用的创建JSON类型数据方式。

create table js(
id int primary key,
val json);insert into js values(1, '[1,2,"a","b"]');
insert into js values(2, '{"key1":1, "key2":2, "key1":"value3"}');select * from js;

  • create table语句中,指定列的类型为json,表示其只能接受JSON格式的数据
  • 两条insert 语句向JSON类型的列插入字符串时,会对字符串进行规范化并转换成JSON格式数据
  • 数据插入过程中,JSON对象中重复的键key1保留了后出现的值"value3"

如果插入的字符串不是合法的JSON格式,语句会报错:

insert into js values(3, '[1,2,"a","b"');

  • 这里少了一个],语句报错:Invalid JSON text

1.2.2 json_array/json_object/cast函数

通过 json_array() 函数可以将一系列值转换为JSON数组:

select json_array(TRUE, FALSE, NULL, 'TRUE', 'FALSE', 'NULL');

  • 这里TRUE, FALSE, NULL在规范化过程中均被转换为小写
  • 而字符串'TRUE', 'FALSE', 'NULL'则保持不变

通过 json_object() 函数可以把字符串形式的键值对转换成JSON格式对象:

select json_object('key2',2, 'key1',1, 'key1',3);

  • 返回的结果中,key1后出现的值3覆盖了先出现的值1
  • 因为要凑键值对,所以提供的值的数量必须是偶数
  • 规范化后,JSON对象是按键排序的,本例中key1被排到的前面

由于键必须是字符串,即使以数字形式提供,在规范化的过程中也会被转换为字符串:

select json_object('key2',2, 'key1',1, 3, 'key1');

  • 第三个键值对以数字3作为键,规范化后,3被转换成字符串"3"并被排到了最前面

同样,cast(… as json)会根据提供的字符串格式,转换为JSON数组或JSON对象:

select cast('[1,2,"abc"]' as json);    -- 转换为JSON数组select cast('{"key1":1, "key2":2, "key1":3}' as json);    -- 转换为JSON对象

二、JSON数据操作

使用JSON格式的最大好处是MySQL提供了一系列操作函数,可以快速对JSON类型的数据进行查询/更新,不必再对整个字符串进行格式解析,在处理JSON数据时简便许多。

2.1 JSON数据查询

数据被转换为JSON格式后,即可对其进行快速解析,高效提取数据。

2.1.1 json_extract()函数

json_extract()函数可以从JSON格式的数据中快速取出指定值,语法如下:

  • json_extract(js_array, '$[n]') 通过'$[n]'的形式取出JSON数组中编号为n的元素(编号从0开始)
  • json_extract(js_array, '$[m to n]') 通过'$[m to n]'的形式取出JSON数组中编号m到n的所有元素(编号从0开始)
  • json_extract(js_object, '$.key') 通过'$.key'的形式取出JSON对象中键为key所对应的值
  • '$[n]'和'$.key'还可以替换为'$[*]'和'$.*'表示返回所有的值

从JSON数组取出单个值:

select * from js;select id, json_extract(val, '$[1]') from js;

  • json_extract(val, '$[1]')取出JSON数组中第二个元素
  • 如果目标是JSON对象,则返回null(id为2的记录)

从JSON数组中取出范围值:

select id, json_extract(val, '$[1 to 2]') from js;

  • json_extract(val, '$[1 to 2]')取出JSON数组中2~3范围内的元素

从JSON对象中取出单个值

select id, json_extract(val, '$.key1') from js;

  • json_extract(val, '$.key1')取出JSON数组中键为"key1"所对应的值

2.1.2 column->path写法

json_extract函数还有一种更简便的column->path写法,用'->'或'->>'符号来替代json_extract():

  • json_extract(js_array, '$[n]') 等价于 js_array->'$[n]'
  • json_extract(js_array, '$[n to m]') 等价于 js_array->'$[n to m]'
  • json_extract(js_object, '$.key') 等价于 js_object->'$.key'

上面示例的查询也可以用以下的简便写法

select val->'$[1]' from js where id=1;select val->'$[1 to 3]' from js where id=1;select val->'$[*]' from js where id=1;

  • val->'$[1]' 返回JSON数组中第2个元素
  • val->'$[1 to 3]' 返回JSON数组中第2至4范围内元素
  • val->'$[*]' 返回数组中所有元素

JSON对象也一样:

select val->'$.key1' from js where id=2;select val->>'$.key1' from js where id=2;select val->>'$.*' from js where id=2;

  • 注意符号->和->>的输出结果差异,->的结果是带引号的,如果想要的是不带引号的值,使用->>即可
  • ->>相当于joson_unqoute(json_extract(column, path)),即对结果又做了一次去除引号的操作。
  • val->>'$.*' 返回JSON对象中所有值

2.1.3 json_value()函数

json_value()函数也可以通过path查询指定元素的值,但是它还有额外的类型转换及异常处理功能,在使用上比json_extract()稍复杂,但功能更强。

语法为:json_value(json_doc, path [returning type] [{null|error|default value} on empty] [{null|error|default value} on error])

语法看起来比较复杂,其实就是多了返回值类型转换(returning type),空值处理(on empty),错误处理(on error)3个模块,下面通过几个示例演示该函数用法:

首先是最简单的用法,只需要提供JSON数据及path就可以提取特定位置的值,用法和json_extract函数一样:

set @array1 = '[1, 2, "a", "b", "12.345"]';
set @object1 = '{"key1":"value1", "key2":"value2"}';select @array1, json_value(@array1, '$[4]');select @object1, json_value(@object1, '$.key1');

在提取数据基础上,通过新增returning type选项即可对返回值进行类型转换:

select @array1, json_value(@array1, '$[4]' returning decimal(4,2));select @array1, json_value(@array1, '$[4]' returning char);select @array1, json_value(@array1, '$[4]' returning json);

  • 第一个select返回值的类型定义为decimal(4,2),保理2位小数,因此12.345返回了1.35。
  • 第二个select返回字符串类型,由于默认就是返回字符串类型,这个returning char可以省略
  • 第三个select返回JSON类型

on empty选项定义了在找不到数据时,函数该如何处理,有3种选项:

  • null on empty, 这是默认选项,找不到数据时返回null
  • default value on empty, 找不到数据时返回默认值value
  • error on empty, 函数抛出错误,由用户处理
select @array1, json_value(@array1, '$[100]' null on empty);select @array1, json_value(@array1, '$[100]' default '没找到' on empty);select @array1, json_value(@array1, '$[100]' error on empty);

  • 上面3个选项分别演示了3种on empty的处理,null on empty是默认选项,可以省略

on error选项定义了当函数出错时,应该如何处理,注意这里的出错是指函数在运行过程中出错,例如类型转换错误,如果提供的JSON数据格式或者path格式错误,那么函数本身直接运行出错,不会触发on error选项,这里on error选项也有3种:

  • null on error, 默认选项,错误返回null
  • default value on error, 返回一个默认值,默认值必须要和returning type匹配
  • error on error, 函数抛出错误,由用户处理
select @array1, json_value(@array1, '$[2]' returning decimal(4,2) null on error) convert_a_to_decimal;select @array1, json_value(@array1, '$[2]' returning decimal(4,2) default 56.78 on error) default_value_on_error;select @array1, json_value(@array1, '$[2]' returning decimal(4,2) error on error) error_on_error;

  • 上面3个select尝试将'a'转换为decimal类型,on error选项演示了3种不同的处理
  • 第二个选项defulat value,value必须和前面的returning type中的type匹配(这里是decimal(4,2))

2.1.4 json_keys()函数

json_extract()/json_value()函数返回的都是JSON对象的值,如果想返回键,则需要用json_keys()函数。json_keys()会以JSON数组的形式返回JSON对象中顶层的键,即将所有的键组成一个数组返回。

其语法为: json_keys(json_doc [, path]),当提供path参数时(JSON对象嵌套),会返回指定path处元素的顶层键

返回JSON对象的所有顶层键:

select json_keys('{"key1": "value3", "key2": {"a":"b"}}');

  • 这里的顶层键有'key1','key2',其中'key2'的值又是一个JSON对象(嵌套)
  • 没有指定path参数,这里只返回了顶层的键

如果要返回key2值中的键,需要提供path参数'$.key2'

select json_keys('{"key1": "value3", "key2": {"a":"b"}}', '$.key2');

2.2 JSON数据新增

JSON对象的中的元素新增,可以通过json_arry_append(),json_array_insert()或json_insert()函数来完成,各函数行为略有不同。

2.2.1 json_array_append()函数

josn_arry_append() 会在JSON数组指定的位置添加新的数据,新增的数据与原位置的数据会合并为新的JSON数组(依然算1个元素),不会改变原JSON数据的元素个数,函数语法为:json_arry_append(json_doc, path, val [,path, val] …)

select * from js;update js set val=json_array_append(val,'$[0]','x', '$[3]','y') where id=1;
update js set val=json_array_append(val,'$.key2','y') where id=2;select * from js;

  • json_array_append(val,'$[0]','x', '$[3]','y') 在JSON数组第一个元素中增加一个'x',第三个元素中增加一个'y',数组的元素依然是4个
  • json_array_append(val,'$.key2','y') 在JSON对象键key2的值中附件一个'y'

2.2.2 json_array_insert()函数

josn_arry_insert() 会在JSON数组指定的位置添加新的数据,json_array_append()不同的是,新增的数据会作为一个独立的元素,此函数会改变JSON数组中元素的个数,函数语法为:json_arry_insert(json_doc, path, val [,path, val] …)

select * from js;update js set val=json_array_insert(val,'$[0]','x', '$[3]','y', '$[100]','z') where id=1;select * from js;

  • json_array_insert(val,'$[0]','x', '$[3]','y', '$[100]','z'),插入了3个元素,JSON数组的元素由4个增加至7个
  • 第一个元素插入指定位置后,从这个位置开始,所有元素向后移动一位,这会改变后续元素编号,并影响后续的插入位置
  • '$[0]'位置插入'x'后,所有元素后移一位,"a"的位置由'$[2]'变成了'$[3]',因此函数第二个插入'$[3]'是插在"a"的前面,而不是原'$[3]'元素"b"的前面
  • 函数第三个元素'$[100]',超出了数组长度,因此附加在数组的最后

2.2.3 json_insert()函数

json_insert() 函数可以对JSON数组或JSON对象新增元素,根据给定的path,如果元素不存在,则进行新增,如果元素已存在,则忽略,不做任何操作,即只新增不更新。语法为:json_insert(json_doc, path1, val1, [path2, val2 …])

select * from js where id=1;select json_insert(val,'$[0]','x','$[4]','y') from js where id=1;

  • 对JSON数组操作时,第一个path '$[0]',指定插入'x',但因'$[0]'已存在,因此忽略
  • 第二个path '$[4]',指定插入'y',由于原数组只有4个元素,最大编号只到'$[3]',新增成功

对json对象操作时同样,已存在的键值忽略,不存在的键值新增:

select * from js where id=2;select json_insert(val,'$.key1','x','$.key3','y') from js where id=2;

  • JSON对象中键key1已存在,因此新值'x'被忽略
  • JSON对象中键key3不存在,因此新键值对{"key3":"y"}新增成功

2.3 JSON数据更新

JSON数据的更新,你可以把JSON作为一个字符串,更新完后再整体赋值回去,但这种方法在JSON对象较大的时候可能效率较低,且解析成本也高。MySQL提供了json_replace()函数可以高效的完成指定路径上的元素更新。

2.3.1 json_replace()函数

json_replace() 函数可以对JSON数据进行原地(in-place update)更新,即用新的值替换旧值,其语法为 json_replace(json_doc, path1, new_val1, [path2, new_val2 …]),此函数仅对已存在的值进行更新,对不存在的值直接忽略,即只更新不新增

将json数组前两个值由1和2更新为'x'和'y':

select * from js where id=1;update js set val=json_replace(val,'$[0]','x','$[1]','y','$[5]','z') where id=1;select * from js where id=1;

  • josn_replace函数通过'$[0]'和'$[1]'指定第1,2个元素
  • 这里$[5]指定更新了一个不存在的元素,因此忽略

同样,对于josn对象,只需要将path的写法换成'$.key'格式即可,下面将key1,key2的值分别更新为'value1'和'value2':

select * from js where id=2;update js set val=json_replace(val, '$.key1', 'value1', '$.key2','value2', '$.key3','value3') where id=2;select * from js where id=2;

  • 这里提供了一个不存在的'$.key3',所以被忽略

2.4 JSON数据同时新增和更新

如果需要同时进行更新和新增(例如数据同步),利用json_set() 函数可以同时完成对数据的更新和新增,对于已存在的元素更新,不存在的元素新增。在功能上类似MySQL的replace into或Oracle的merge into语句。

2.4.1 json_set()函数

json_set() 函数语法为:json_set(json_doc, path1, new_val1, [path2, new_val2 …])

JSON数组中,同时更新和新增:

select val from js where id=1;select json_set(val,'$[0]','x','$[100]','z') from js where id=1;

  • 第一个path参数'$[0]',匹配数组中第一个元素,将1更新为'x'
  • 第一个path参数'$[100]',匹配数组中第100个元素,由于不存在,新增到数组最后

JSON对象中,同时更新和新增:

select val from js where id=2;select json_set(val,'$.key1','x','$.key3','z') from js where id=2;

  • 第一个path参数'$key1',匹配对象中键为'key1'的,将其值更新为'x'
  • 第二个path参数'$key3',未匹配到键,直接新增键值对

json_insert(), json_replace(), json_set() 3个函数的参数格式是完全相同的,但功能不同,区别总结如下:

  • json_insert() 仅新增,不更新
  • json_replace() 仅更新,不新增
  • json_set() 既更新又新增,相当于上面两个函数合体

2.5 JSON数据删除

json_remove() 函数从JSON数据中删除指定的元素,语法为json_remove(json_doc, path1 [,path2 …]),对于提供多个path参数的,每次删除后都会重新评估元素的位置。

2.5.1 json_remove()函数

从JOSN数组中删除指定位置的元素:

select val from js where id=1;select json_remove(val,'$[1]','$[2]') from js where id=1;

  • 这里指定删除了第2,第3个元素,对应位置是'$[1]','$[2]'
  • 注意在删除'$[1]'位置的元素后,所有后续元素编号都向前移动1位,因此'$[2]'删除的是新'$[3]'位置的元素"b",而不是原'$[3]'位置的元素"a"

对于JSON对象,只需指定要删除的键即可:

select val from js where id=2;select json_remove(val,'$.key1') from js where id=2;

除了json_remove()函数,json_merge_patch()在对JSON对象某个键最后出现的值赋予null时,也可以删除该键值对(后面会提到)。

2.6 JSON数据合并

MySQL提供了两个函数可以合并多个JSON格式的数据:

  • json_merge_preserve()
  • json_merge_patch()

两个函数的行为略有不同,可以总结为json_merge_preserve()会保留重复值,json_merge_patch()会替换合并。

2.6.1 json_merge_preserve()函数

joson_merge_preserve() 在合并JSON数组时,会把所有元素简单的拼接成一个大数组:

SELECT json_merge_preserve('[1, 2]', '[true, false]','["a","b"]');SELECT json_merge_preserve('[1, 2]', '{"key1": "value1"}');

  • 2个示例中,所有的元素都保留了下来

如果合并的是JSON对象,json_merge_preserve()函数会保留所有非重复键值对,对于重复的键,会将其值合并为JSON数组保留下来

SELECT json_merge_preserve('{"key1": "value1"}','{"key2": "value2"}');SELECT json_merge_preserve('{"key1": "value1"}','{"key2": "value2","key1":"value3"}') duplicate_key1;SELECT json_merge_preserve('{"key1": "value1"}','{"key2": "value2","key1":"value3"}','{"key1": null}') last_value_is_null;

  • 第2,3语句中,键"key1"均出现了多次,但所有的值都被合并成数组保留了下来

2.6.2 json_merge_patch()函数

joson_merge_patch() 在合并JSON数组时,会将每个参数视为独立的元素,并应用"最后的值胜出"原则,只选择最后一个元素,因此只要函数中包含JSON数组,只有最后一个值会保留下来

SELECT json_merge_patch('[1, 2]', '[true, false]','["a","b"]');SELECT json_merge_patch('[1, 2]', '{"key1": "value1"}');SELECT json_merge_patch('{"key1": "value1"}','{"key2": "value2"}','[1, 2]');

  • 3个示例中,都是只有最后一个元素被保留了下来

如果合并的都是JSON对象, json_merge_patch()函数会保留所有非重复键值对,对于重复的键,只保留最后出现的值(但最后出现的值不能为null,否则会删除该键值):

SELECT json_merge_patch('{"key1": "value1"}','{"key2": "value2"}');SELECT json_merge_patch('{"key1": "value1"}','{"key2": "value2","key1":"value3"}') duplicate_key;SELECT json_merge_patch('{"key1": "value1"}','{"key2": "value2","key1":"value3"}','{"key1": null}') last_value_is_null;

  • 第2个语句中,key1出现了2次,保留了最后出现的"value3"
  • 第3个语句中,key1出现了3次,但最后出现的值为null,所以该键值被删除

如果最后的键值为null会删除该键值对,利用这个特性我们可以给json_merge_path可以用传入null值来删除指定键值对(也可以用json_remove)。

2.7 判断元素否存在

某些时候,我们并不需要提取数值,而仅仅想知道某个元素是否存在(例如作判断条件时),下列函数可以实现此类功能:

  • json_contains_path() 测试某个path是否存在
  • json_search() 通过值查询其具体path
  • value member of(json_array) 通过值测试是否存在于JSON数组中

2.7.1 json_contains_path()函数

josn_contains_path()函数可以用来测试指定的一个或多个path是否存在。语法为:json_contains_path(json_doc, one_or_all, path [,path …]),当path存在时返回1,不存在时返回0(不是null)。

该函数第二个参数one_or_all有2种取值,代表2种测试行为:

  • one, 至少有1个path存在即返回1
  • all, 所有path都存在才返回1

判断指定path在数组中是否存在:

set @array1 = '["a", "b", "c"]';
set @object1 = '{"key1":"value1", "key2":"value2"}';select json_contains_path(@array1, 'one', '$[0]', '$[100]');select json_contains_path(@array1, 'all', '$[0]', '$[100]');

  • 这里有1个不存在的path '$[100]',根据第二个参数one_or_all,这里分别返回1和0

判断指定path在对象中是否存在:

select json_contains_path(@object1, 'one', '$.key1', '$.key100');select json_contains_path(@object1, 'all', '$.key1', '$.key100');

json_contains_path()函数用来判断指定的path是否存在,如果想判断指定的元素(值)是否存在,可以用json_contains()函数,它可以用来测试两个对象的包含关系,当然也可以测试值。

2.7.2 json_search()函数

json_search()函数可以通过值来查询path,如果存在则返回其具体的path,不存在则返回null。语法为:json_search(json_doc, one_or_all, search_str [escape_char [,path]…),

第二个参数one_or_all控制返回path的搜索行为:

  • one, 返回第一个匹配的path,当找到第一个匹配的path时搜索即终止
  • all, 返回所有匹配的path

在提供search_str时,可以用%或_来代替任意多个或单个字符(和like中用法一样)。但是如果要匹配'%'或'_'字符本身,则要加上转义字符,即后面的参数escape_str,省略该参数或提供null值,则默认为\

set @array1 = '[1,2, 1, "abc", "abd", "xyz"]';
set @object1 = '{"key1":"abc", "key2":"abd"}';select json_search(@array1, 'one', 'abc');select json_search(@array1, 'all', 'ab%');

  • 第二个select采用模糊匹配,'ab%'配合参数'all'返回所有以ab开头的字符串的path

这里我想查找字符串中包含1个%的元素的path:

set @array2 = '["a%c", "a%%c"]';select json_search(@array2, 'all', 'a%c');select json_search(@array2, 'all', 'a\%c');

  • 第一个select中,'a%c'中的'%'会被理解成通配符,因此'a%%c'的path也返回了
  • 但我们只想匹配'%'字符本身,只需要用转义字符'\'转义即可,这也是第四个参数escape_char,这里省略了,默认就是'\'

若省略第四个参数escape_char,或者使用null,默认都是'\',也可以显式指定其他转义字符

select json_search(@array2, 'all', 'a\%c',null);select json_search(@array2, 'all', 'a@%c','@');

第五个可选参数path,可以用来测试指定path上是否是某值:

select @array1;select json_search(@array1,'all','abc',null,'$[3]');select json_search(@array1,'all','abc',null,'$[4]');

  • 这里提供了第五个参数path,两个select分别测试指定位置上值是否是'abc'

2.7.3 value member of(json_array)

对于JSON数组,如果只是想知道某个值是否存在,还可以用value member of(json_array)来判断值是否包含在指定数组中(MySQL 8.0.17后可用),包含返回1,不包含则返回0:

select val, 2 member of(val), 'x' member of(val) from js where id=1;

这种方式相对于上面的函数要简练许多,但功能也相对单一。

2.8 判断JSON对象之间的关系

当我们需要判断2个JSON对象间的关系,例如包含,重叠,可以利用json_contains()或json_overlaps()函数来完成

2.8.1 json_contains()函数

json_contains() 可以测试两个JSON对象间是否是包含关系(也可以来测试值是否存在),其语法为:json_contains(doc, candiate [, path]),该函数会测试candidate是否包含在doc中,如果提供了第三个可选参数path,那么即限定doc是否在path指定的路径处包含candiate,如果包含则返回1,不包含则返回0.

关于包含关系的测试,根据测试对象是JSON数组和对象,还存在下列规则:

  • 对于JSON数组,candidate中每个元素都包含在doc中,即是包含
  • 如果提供了path,那么candiate会被视为一个整体,必须在指定path存在才被视为包含
  • 对于JSON对象,candidate的键必须包含在doc的键中,且值被对应的键的值包含,才视为包含
set @array='[[1,"z"], 2, "a", "b", 1, "z"]';
set @candidate1='[2, "b"]';
set @candidate2='[1, "z"]';select json_contains(@array, @candidate1);select json_contains(@array, @candidate2, '$[1]');select json_contains(@array, @candidate2, '$[0]');

  • 第一句select,"2", "b" 分别包含在@array的元素中(独立包含),因此返回1
  • 第2,3句select,由于提供了path参数,因此数组被视为1个整体,只有在指定path包含才算作包含

对于JSON对象,

set @object='{"key1":["x","y"], "key2":2, "key3":3}';
set @candidate3='{"key1":"x", "key3":3}';
set @candidate4='{"key1":"z", "key3":3}';select json_contains(@object, @candidate3);select json_contains(@object, @candidate4);

  • 第1句select,@candidate3的键和值都包含在@object中,返回1
  • 第2句select,@candidate4的键都包含在doc中,但key1的值"z"不包含在@object中,因此返回0

2.8.2 json_overlaps()函数

相对于json_contains函数,json_overlaps只判断两个JSON对象是否包含相同的元素或键值对,包含则返回1,不包含则返回0,语法为:json_overlaps(json_doc1, json_doc2)

判断数组是否包含相同的元素(即是否有重叠):

set @array1 = '[1, 2]';
set @array2 = '[1, 3]';
set @object1 = '{"key1":"value1", "key2":"value2"}';
set @object2 = '{"key1":"value1", "key3":"value3"}';
set @object3 = '{"key1":"x", "key3":"value3"}';select json_overlaps(@array1, @array2);

判断对象是否包含相同键值对(是否重叠)

select json_overlaps(@object1, @object2);select json_overlaps(@object1, @object3);

  • 对于JSON对象,必须键值对都相同才算相同的元素
  • @object1和@object3包含相同的键,但是值不同,因此不算重叠

三、JSON数据与索引

在MySQL的表中,JSON类型的列通常无法直接建立索引,你可以用虚拟生成列(Virtual Generated Columns),并根据该列来建立间接索引。但是在MySQL8.0.17版本后,对于JSON数组(JSON对象不行),可以建立多值索引(Multi-valued Index)。

3.1 生成列索引(Generated Column Index

对于JSON数据类型需要建立索引,可以对将经常查询的元素提取出来,作为一个虚拟的生成列,并在该列上建立索引,查询时通过虚拟列上索引即可快速定位数据。

虚拟列的语法是:

col_name data_type [GENERATED ALWAYS] AS (expr)[VIRTUAL | STORED] [NOT NULL | NULL][UNIQUE [KEY]] [[PRIMARY] KEY][COMMENT 'string']
  • expr 是列的生成表达式,需要依赖其他列计算
  • virtual 代表该列不实例化,不消耗存储空间,每次用到该列时计算
  • stored 代表实例化存储,消耗存储空间,且每次更新其依赖列时,都会同时更新虚拟列数据

下面示例,b是根据a生成的虚拟列,并且在b上建立了索引idx:

create table person(
a json,
b int generated always as (a->"$.id"),
key idx(b));insert into person(a) values('{"id":1, "name":"Vincent"}'), ('{"id":2, "name":"Victor"}'), ('{"id":3, "name":"Grace"}');

这样就可以通过b列查询时即可利用索引提速,快速定位记录:

explain select * from person where b=2;

3.2 多值索引(Multi-valued Index

MySQL 8.0.17版本引入了多值索引,可以直接对JSON类型列创建索引,但是仅限JSON数组。在传统二级索引中,一个索引记录对应一条数据记录。但在多值索引中,会根据JSON数组中的值建立多个索引,同时指向这一条记录,其底层原理依然是通过虚拟列完成的,只是对用户来说这一步是透明的。

在对JSON数组列建立索引前,需要先用cast(… as type array)将其由JSON数组类型转换为SQL数组类型:

create table t(
a json,
key idx((cast(a as unsigned array)))
);insert into t values('[1, 2, "3", 4]'), ('[5, "6", 7]'),('[8, 9, 10]');

  • 注意create table语句中cast函数外面需要额外一层括号
  • 由于这里限制的是unsigned array,因此json数组中元素必须可以转换为数字,例如插入字符"a"则会报错
  • 其他可能的type类型还有:date, datetime, decimal, double, signed, time, year等

在建立多值索引后,member of(), json_contains(), json_overlaps()函数在where条件中则可以利用多值索引来加速查询。

这里查询a列中包含3的记录,通过执行计划可以发现member of()函数使用了多值索引idx:

select * from t where 3 member of(a);explain select * from t where 3 member of(a);

、将JSON转换为结构化数据

JSON是一种非结构化数据,不需要像数据库表那样预先定义列。有时我们需要又需要将JSON数据转换为结构化数据,存储到数据库的表中。

MySQL提供了json_table()函数,可以将JSON数据转换成类似表一样的结构化数据,转化后的数据类似一张虚拟表,可以直接用select查询或者与其他表连接。

4.1 json_table()函数

json_table()的语法为:

JSON_TABLE(expr,path COLUMNS (column_list)
)   [AS] aliascolumn_definition: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源数据
  • path 是要转换的JSON数据范围,expr + path定义了转换的数据
  • column_definition是每一列的详细定义,共4种
  • name for ordinality 为每一行数据生成一个编号,从1开始,相当于一个auto_increment列
  • name type path string_path [on empty] [one_error] 指定某列数据的具体path,[on_empty] [on_error]逻辑可以参照上面的json_value()函数
  • name type exists path string path 根据指定位置是否有值将1和0放入name 列中
  • nested [path] path columns(column_list)

下面示例将由JSON对象组成的数组转化为表:

set @array1 = '[ {"name":"Vincent", "age":30, "salary":1000}, {"name":"Victor", "age":6}, {"name":"Grace", "age":30, "salary":3000}]';select  * from
json_table(
@array1,
'$[*]' columns(
id for ordinality,
name varchar(32) path '$.name',
age int path '$.age',
salary decimal(6,2) path '$.salary' default '0' on empty,
have_salary int exists path '$.salary')
) as person;

  • id for orinality 为每行数据生成一个编号,从1开始
  • salary 列default '0' on empty,在对象没有'$.salary'键时,默认赋值0
  • have_salary 通过判断 '$.salary'是否存在,分别显式1或0

以上即是MySQL中JSON的主要用法总结,覆盖了日常应用中常见处理,应用好JSON类型的数据管理可以大幅提升MySQL处理非结构化数据的能力。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/650315.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

spring-boot-starter-validation常用注解

文章目录 一、使用二、常用注解三、Valid or Validated ?四、分组校验1. 分组校验的基本概念2. 定义验证组3. 应用分组到模型4. 在控制器中使用分组5. 总结 一、使用 要使用这些注解,首先确保在你的 Spring Boot 应用的 pom.xml 文件中添加了 spring-bo…

探秘Dmail:Web3世界的通讯引领者

摘要:在一个充满潜力并且对创新要求严格的领域中,Dmail作为一种开创性的Web3通讯协议应运而生。 1月24日,OKX Jumpstart宣布上线Dmail,在Web3领域引起了巨大反响,这是一个旨在重新定义数字通讯范式的富有远见的项目&a…

[足式机器人]Part2 Dr. CAN学习笔记- 最优控制Optimal Control Ch07

本文仅供学习使用 本文参考: B站:DR_CAN Dr. CAN学习笔记 - 最优控制Optimal Control Ch07-1最优控制问题与性能指标 1. 最优控制问题与性能指标2. 动态规划 Dynamic Programming2.1 基本概念2.2 代码详解2.3 简单一维案例 3. 线性二次型调节器&#xff…

GIt同时存在传入和传出更改修改,无法合并

前言 Git是常用的版本管理工具,之前面试被问到过一次——Git有无遇到过使用错误情况?当时卡壳了没答上来,所以这次遇到,特此记录学习。 问题概述 前一天提交了代码,mt进行了修改。但我忘记拉取最新,就进…

深度解析单片机:历史、发展与您关心的问题

什么是单片机? 定义:单片机是一种集成了中央处理器(CPU)、内存和外设功能的微型计算机系统。与传统计算机相比,单片机通常集成在一个芯片上,用于控制特定的应用。#单片机# 特点: 封装紧凑&…

人工智能:更多有用的 Python 库

目录 前言 推荐 JupyterLab 入门 复杂的矩阵运算 其它人工智能和机器学习的 Python 库 前言 在这篇文章中,我们将了解更多的矩阵操作,同时再介绍几个人工智能 Python 库。 推荐 前些天发现了一个巨牛的人工智能学习网站,通俗易懂&#x…

再谈Android View绘制流程

一,先思考何时开始绘制 笔者在这里提醒读者,Android的View是UI的高级抽象,我们平时使用的XML文件也好,本质是设计模式中的一种策略模式,其View可以理解为一种底层UI显示的Request。各种VIew的排布,来自于开…

如何使用 dotnet pack 打包 .NET 跨平台程序集?

如何使用 dotnet pack 打包 .NET 跨平台程序集? dotnet pack 介绍官方描述 dotnet pack 命令说明使用示例打包 .net 类库项目生成带注释的 nuget 包构建特定平台的 nuget 包关于 .NET RID 目录 dotnet pack 介绍 dotnet pack 是一个 .NET Core NuGet 包打包程序。 …

Python 数据分析实战——为什么销售额减少?酒卷隆治_案例1

# 为什么黑猫游戏的销售额会减少? # 数据集 DAU : 每天至少来访问一次的用户数据 数据内容 数据类型 字段名 访问时间 string(字符串) log_data 应用名称 string(字符串) app_name 用户 ID int(数值&…

【机组】基于FPGA的32位算术逻辑运算单元的设计(EP2C5扩充选配类)

​🌈个人主页:Sarapines Programmer🔥 系列专栏:《机组 | 模块单元实验》⏰诗赋清音:云生高巅梦远游, 星光点缀碧海愁。 山川深邃情难晤, 剑气凌云志自修。 目录 一、实验目的 二、实验要求 …

acwing 质数 约数 欧拉函数

目录 质数试除法定质数分解质因数筛质数 约数试除法求约数乘积的约数个数最大公约数 欧拉函数筛法求欧拉函数和 质数 试除法定质数 bool is_prime(int num) {if(num < 2)return false;for(int i 2; i < num / i; i)if(num % i 0)return false;return true; }分解质因…

Javaweb之SpringBootWeb案例之阿里云OSS服务集成的详细解析

2.3.3 集成 阿里云oss对象存储服务的准备工作以及入门程序我们都已经完成了&#xff0c;接下来我们就需要在案例当中集成oss对象存储服务&#xff0c;来存储和管理案例中上传的图片。 在新增员工的时候&#xff0c;上传员工的图像&#xff0c;而之所以需要上传员工的图像&…

<蓝桥杯软件赛>零基础备赛20周--第18周--动态规划初步

报名明年4月蓝桥杯软件赛的同学们&#xff0c;如果你是大一零基础&#xff0c;目前懵懂中&#xff0c;不知该怎么办&#xff0c;可以看看本博客系列&#xff1a;备赛20周合集 20周的完整安排请点击&#xff1a;20周计划 每周发1个博客&#xff0c;共20周。 在QQ群上交流答疑&am…

ASP.NET 7 Core Web 读取appsetting.json

把一些配置信息保存在json文件可以避免更改时要重新发布程序的烦恼。 我这里使用的是写一个类文件&#xff0c;然后通过program.cs启动的方式&#xff08;.net 6 开始没有startup了&#xff09;。 项目类型&#xff1a;ASP.NET Core Web MVC / .NET 7.0 / VS2022 第一步…

基于机器学习的地震预测(Earthquake Prediction with Machine Learning)

基于机器学习的地震预测&#xff08;Earthquake Prediction with Machine Learning&#xff09; 一、地震是什么二、数据组三、使用的工具和库四、预测要求五、机器学习进行地震检测的步骤六、总结 一、地震是什么 地震几乎是每个人都听说过或经历过的事情。地震基本上是一种自…

锂电池基本知识与设计

应用&#xff1a;笔记本电脑、智能手机等设备。 优点&#xff1a;较高能量密度和较长使用寿命&#xff0c;放电率低&#xff0c;可进一步延长充电间隔时间。 缺点&#xff1a;过度充电或者放电会产生不可逆的损伤&#xff0c;性能降低。高温环境下容易爆炸或者着火。 &#x…

el-tree基础的树形节点设置节点不能选中高亮出来,对已经选中的节点设置disabled,对当前节点刚选中后设置禁用disabled

一、 el-tree基础的树形节点设置节点不能选中高亮出来 需求 我们使用element-ui或者element-plus的时候会遇到树形控件的使用&#xff0c;我们使用树形控件会限制有的节点不让选中和高亮出来&#xff0c;这个时候需要我们做限制。在实现中我们发现了element-ui和element-plus…

WSL2+ubuntu 18+VsCode 配置C/C++开发环境 踩坑

1. 管理员模式打开cmd&#xff0c;或PowerShell &#xff0c;输入 wsl --install 可能出现的错误&#xff1a;无法解析服务器名称或地址 解决方式&#xff1a;科学上网 安装WSL时遇到“无法解析服务器名称或地址”的错误及解决方法 - 知乎 错误2&#xff1a;Error 0x8037…

Python tkinter (6) Listbox

Python的标准Tk GUI工具包的接口 tkinter系列文章 python tkinter窗口简单实现 Python tkinter (1) —— Label标签 Python tkinter (2) —— Button标签 Python tkinter (3) —— Entry标签 Python tkinter (4) —— Text控件 GUI 目录 Listbox 创建listbox 添加元素…

TPCC-MySQL

简介 TPC-C是专门针对联机交易处理系统&#xff08;OLTP系统&#xff09;的规范&#xff0c;一般情况下我们也把这类系统称为业务处理系统。 Tpcc-mysql是percona基于TPC-C(下面简写成TPCC)衍生出来的产品&#xff0c;专用于MySQL基准测试。其源码放在launchpad上&#xff0c…