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

news2024/9/29 17:39:00

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] alias


column_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 EMPTY


on_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.coloradmin.cn/o/1413921.html

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

相关文章

最长公共子串的问题(正常方法和矩阵法,动态规划)

题目: 给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。 一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符…

Windows Qt C++ VTK 绘制三维曲线

Qt 自带数据可视化从文档上看,只能实现三维曲面。 QwtPlot3D在Qt6.6.0上没编译通过。 QCustomPlot 只能搞二维。 VTK~搞起。抄官网demo。 后续需求: 1、对数轴 2、Y轴逆序 3、Z轴值给色带,类似等高线图的色带 期待各位大佬多多指导。…

如何快速搭建实用的爬虫管理平台

目录 一、前言 二、选择合适的爬虫框架 三、搭建数据库 步骤1 步骤2 步骤3 四、搭建Web服务器 步骤1 步骤2 步骤3 步骤4 五、管理爬虫 六、总结 一、前言 爬虫是互联网数据采集的关键工具,但是随着数据量的增加和需求的多样化,手动运行和管…

超越传统,想修哪里就修哪里,SUPIR如何通过文本提示实现智能图像修复

项目简介 通过参数增加使得模型不仅能够修复图像中的错误或损坏,还能根据文本提示进行智能修复。例如根据描述来改变图像中的特定细节。这样的处理方式提升了图像修复的质量和智能度,使得模型能够更准确、更灵活地恢复和改进图像。 SUPIR的主要功能图像…

详解矩阵的LDU分解

目录 一. 矩阵分解 二. 解方程 三. 例题说明 四. 矩阵的LDU分解 五. 矩阵三角分解的唯一性 一. 矩阵分解 其实我们可以把一个线性系统(Linear System)看成两个三角系统(Triangular Systems),本文章将解释为什么可…

QT实现USB摄像头接入显示

一、UVC协议简介 UVC全称是USB Video Class(USB视频类),是一种标准化的USB视频设备通信协议,它定义了摄像头与主机之间的数据传输协议和格式。 UVC协议的出现,解决了摄像头厂商之间互不兼容,以及摄像头应…

2.Linux的例行性工 作(计划任务)

目录 1. 场景: 2. 单一执行的例行性任务--at(一次性) 2.1. 安装 2.2. at命令详解 2.2.1. 格式 2.2.2. 参数 2.2.3. 时间格式 2.2.4. 实验1 2.2.5. 实验2 2.2.6. 注意 2.2.7. at命令执行过程分析 2.2.8. 实验3 3. 循环执行的例行…

Elasticsearch:聊天机器人、人工智能和人力资源:电信公司和企业组织的成功组合

作者:来自 Elastic Jrgen Obermann, Piotr Kobziakowski 让我们来谈谈大型企业人力资源领域中一些很酷且改变游戏规则的东西:生成式 AI 和 Elastic Stack 的绝佳组合。 现在,想象一下大型电信公司的典型人力资源部门 — 他们正在处理一百万件…

mac/macos上编译electron源码

官方教程:Build Instructions | Electron 准备工作这里不写了,参考官方文档,还有上一篇windows编译electron electron源码下载及编译-CSDN博客 差不多步骤,直接来 网络记得使用魔法 下载编译步骤 0. 选择目录很重要&#xff0…

Emergent Abilities of Large Language Models 机翻mark

摘要 证明通过扩大语言模型可以可靠地提高性能和样本效率在广泛的下游任务。相反,本文讨论了我们称之为大型语言模型的新兴能力的一种不可预测的现象。我们认为如果一个能力不存在于较小的模型中,但在较大的模型中存在,则该能力就是新兴的。…

InforSuiteAS中创中间件windows环境部署

版本:InforSuiteAS_StE_V10.0.5.2.1 环境要求:Java环境 DK1.8版本, 内存2GB或以上 , 硬盘空间 10GB或以上, 监视器 图形界面安装需要256色以上,字符界面安装没有色彩要求 ,浏览器 Microsoft …

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

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

SQL注入实战:绕过操作

一:绕过操作 1、常用绕过方式 大小写绕过: 通过修改关键字内字母大小写来绕过过滤措施。例如:AnD11Select* from ** oRdEr by 1 双写绕过: 使用双写绕过。因为在过滤过程中只进行了一次替换。就是将关键字替换为对应的空。 比如 union在…

使用Opencv-python库读取图像、本地视频和摄像头实时数据

使用Opencv-python库读取图像、本地视频和摄像头实时数据 Python中使用OpenCV读取图像、本地视频和摄像头数据很简单, 首先需要安装Python,然后安装Opencv-python库 pip install opencv-python然后在PyCharm或者VScode等IDE中输入对应的Python代码 一…

VS执行程序的时候运行上一次的程序。

这个问题我找了很久,最后自己随便按一下,成功了,分享给大家: 上面的代码是我测试的时候用的,随便写个c或者c代码就行,因为这个时候,我的代码已经更改了,它依然执行上一次的程序。我…

2024.1.27每日一题

LeetCode 最大合金数 2861. 最大合金数 - 力扣(LeetCode) 题目描述 假设你是一家合金制造公司的老板,你的公司使用多种金属来制造合金。现在共有 n 种不同类型的金属可以使用,并且你可以使用 k 台机器来制造合金。每台机器都需…

【项目日记(五)】第二层: 中心缓存的具体实现(上)

💓博主CSDN主页:杭电码农-NEO💓   ⏩专栏分类:项目日记-高并发内存池⏪   🚚代码仓库:NEO的学习日记🚚   🌹关注我🫵带你做项目   🔝🔝 开发环境: Visual Studio 2022 项目日…

sqli-lbs靶场搭建

目录 环境小皮源码下载 1.源码解压: 2.搭建网站 2.1点击创建网站 2.2修改sql-connections\db-creds.inc 2.3重新启动 3.访问你设置的域名 3.1点击启动数据库配置 3.2返回第一个页面(开启题目) sqlilbs靶场搭建 环境小皮源码下载 下载地址&am…

AWTK 开源串口屏开发(8) - 系统设置

AWTK 开源串口屏开发 - 系统设置 系统设置只是一个普通应用程序,不过它会用 默认模型 中一些内置的属性和命令,所以这里专门来介绍一下。 1. 功能 在这个例子会用到 默认模型 中一些下列内置的属性和命令: 内置属性 属性类型说明rtc_yea…

x-cmd pkg | httpx - 为 Python 设计的下一代 HTTP 客户端库

目录 简介首次用户功能特点进一步探索 简介 HTTPX 是一个为 Python 设计的下一代 HTTP 客户端库,由 Tom Christie 创建。它提供了同步和异步的 API,并支持 HTTP/1.1 和 HTTP/2 协议。与 Requests 库类似,但增加了对异步请求的支持和 HTTP/2 …