MySQL中的JSON类型

JSON in MySQL

创建JSON值

一个JSON数组包含了一组由方括号包裹的,由逗号分隔的值:

["json", null, true, 123]

一个JSON对象包含了一组由花括号包裹的一组键值对,键值对之间用逗号分隔:

{"key": "value", "key2": 2}

JSON数组和对象可以包含字符串和数字这样纯量的值,JSON的null值字面量,或者JSON布尔值字面量(true/false)。在JSON对象中,键必须时字符串。表示时间的纯量值也可以在JSON中使用:

["2019-10-01 13:21:02.000022", "2011-09-28"]

在JSON数组,JSON对象中的value也可以嵌套使用JSON数组/对象,例如:

[100,{"name":"Jane", "age": 19},[21, 28]]
{"key1":[21,20], "key2":"value"}

JSON值可以通过MySQL提供的一些方法来创建,也可以使用CAST(value AS JSON)将其他类型的值转换为JSON类型。

在MySQL中,JSON值被写作为字符串。MySQL会在需要JSON值的环境下将任何字符串转换为JSON值,如果字符串不是一个有效的JSON值,MySQL会报错。这些环境包括:向某个数据类型为JSON的字段插入数据时,或是向某个参数类型为JSON的函数传入参数时等。

除了使用字面量编写JSON值外,还有一些函数可以将各部分的值组合成JSON值。

例如:JSON_ARRAY()接收一组值,返回一个包含了这些值的JSON数组;

mysql> SELECT JSON_ARRAY('a', 1, NOW());
+----------------------------------------+
| JSON_ARRAY('a', 1, NOW())              |
+----------------------------------------+
| ["a", 1, "2019-04-23 10:34:07.000000"] |
+----------------------------------------+
1 row in set (0.01 sec)

JSON_OBJECT()接收一系列的键值对,返回一个包含了这些键值对的JSON对象。如果输入的参数列表中,键值对的键名发生了重复,先输入的键值对(位于参数列表前端)将被覆盖,输出的结果中只包含重复键值对中最后一个输入的值(位于参数列表尾端);

mysql> SELECT JSON_OBJECT('key1','value1','key2',2);
+---------------------------------------+
| JSON_OBJECT('key1','value1','key2',2) |
+---------------------------------------+
| {"key1": "value1", "key2": 2}         |
+---------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT JSON_OBJECT('key1',1,'key2','abc','key1','def');
+-------------------------------------------------+
| JSON_OBJECT('key1',1,'key2','abc','key1','def') |
+-------------------------------------------------+
| {"key1": "def", "key2": "abc"}                  |
+-------------------------------------------------+
1 row in set (0.00 sec)

JSON_MERGE_PRESERVE()接收两个或两个以上的JSON文件,返回一个合并后的结果;

mysql> SELECT JSON_MERGE_PRESERVE('{"key1":1,"key2":2}','[2,"abc"]');
+--------------------------------------------------------+
| JSON_MERGE_PRESERVE('{"key1":1,"key2":2}','[2,"abc"]') |
+--------------------------------------------------------+
| [{"key1": 1, "key2": 2}, 2, "abc"]                     |
+--------------------------------------------------------+
1 row in set (0.00 sec)

JSON值是大小写敏感的,因此在使用null,true,false这些字面量时,一定要使用小写字母。相比之下,SQL中的字面量NULL,TRUE,FALSE则是大小写不敏感的,可以使用任何形式来表示。

MySQL中支持两种合并方法,JSON_MERGE_PRESERVE()JSON_MERGE_PATCH()。二者的区别在于如何处理重复的键:JSON_MERGE_PRESERVE()保留重复的键,而JSON_MERGE_PATCH()只保留最后一个值,其他值都会被丢弃。

  • 合并数组

    JSON_MERGE_PRESERVE()会将所有数组拼接起来形成一个新数组并返回。而JSON_MERGE_PATCH()只会保留最后一个数组,并将其作为结果返回。

    mysql> SELECT JSON_MERGE_PRESERVE('[1,2]','["A","B"]','[true,false]');
    +---------------------------------------------------------+
    | JSON_MERGE_PRESERVE('[1,2]','["A","B"]','[true,false]') |
    +---------------------------------------------------------+
    | [1, 2, "A", "B", true, false]                           |
    +---------------------------------------------------------+
    1 row in set (0.00 sec)
    
    mysql> SELECT JSON_MERGE_PATCH('[1,2]','["A","B"]','[true,false]');
    +------------------------------------------------------+
    | JSON_MERGE_PATCH('[1,2]','["A","B"]','[true,false]') |
    +------------------------------------------------------+
    | [true, false]                                        |
    +------------------------------------------------------+
    1 row in set (0.00 sec)
    
  • 合并对象

    当要合并的多个对象中含有相同的键时,JSON_MERGE_PRESERVE()会将重复的键对应的所有值去重后存入一个数组中作为该键的值,再将该键值对存入对象中返回。JSON_MERGE_PATCH()则会从左到右依次丢弃重复键的值,只保留最后一个出现的值。

    mysql> SELECT JSON_MERGE_PERSERVE('{"a":1,"b":2}','{"c":3,"a":4}','{"c":5,"d":3}');
    ERROR 1046 (3D000): No database selected
    mysql> SELECT JSON_MERGE_PRESERVE('{"a":1,"b":2}','{"c":3,"a":4}','{"c":5,"d":3}');
    +----------------------------------------------------------------------+
    | JSON_MERGE_PRESERVE('{"a":1,"b":2}','{"c":3,"a":4}','{"c":5,"d":3}') |
    +----------------------------------------------------------------------+
    | {"a": [1, 4], "b": 2, "c": [3, 5], "d": 3}                           |
    +----------------------------------------------------------------------+
    1 row in set (0.00 sec)
    
    mysql> SELECT JSON_MERGE_PATCH('{"a":1,"b":2}','{"c":3,"a":4}','{"c":5,"d":3}'); 
    +-------------------------------------------------------------------+
    | JSON_MERGE_PATCH('{"a":1,"b":2}','{"c":3,"a":4}','{"c":5,"d":3}') |
    +-------------------------------------------------------------------+
    | {"a": 4, "b": 2, "c": 5, "d": 3}                                  |
    +-------------------------------------------------------------------+
    1 row in set (0.00 sec)
    
  • 合并值

    当传入的参数既不是数组,也不是对象,而是单独的值时,这个值会被自动用方括号包裹转换成数组,然后按照合并数组的规则进行合并。

    mysql> SELECT JSON_MERGE_PRESERVE('1','2');
    +------------------------------+
    | JSON_MERGE_PRESERVE('1','2') |
    +------------------------------+
    | [1, 2]                       |
    +------------------------------+
    1 row in set (0.00 sec)
    
    mysql> SELECT JSON_MERGE_PATCH('1','2');
    +---------------------------+
    | JSON_MERGE_PATCH('1','2') |
    +---------------------------+
    | 2                         |
    +---------------------------+
    1 row in set (0.00 sec)
    
  • 合并数组和对象

    当合并的对象既有数组也有对象时,对象会被自动包装转换成数组进行合并操作,然后按照合并数组的规则进行合并。

    mysql> SELECT JSON_MERGE_PRESERVE('[10,11]','{"key1": 1, "key2": 2}');
    +---------------------------------------------------------+
    | JSON_MERGE_PRESERVE('[10,11]','{"key1": 1, "key2": 2}') |
    +---------------------------------------------------------+
    | [10, 11, {"key1": 1, "key2": 2}]                        |
    +---------------------------------------------------------+
    1 row in set (0.00 sec)
    
    mysql> SELECT JSON_MERGE_PATCH('[10,11]','{"key1": 1, "key2": 2}');
    +------------------------------------------------------+
    | JSON_MERGE_PATCH('[10,11]','{"key1": 1, "key2": 2}') |
    +------------------------------------------------------+
    | {"key1": 1, "key2": 2}                               |
    +------------------------------------------------------+
    1 row in set (0.00 sec)
    
    

搜索和修改JSON值

一个路径表达式(path expression)可以在一个JSON文档中选择一个值。当我们需要从一个JSON文档中提取或者修改一部分值时,路径表达式十分有用,我们可以使用路径表达式明确得指出需要修改的部分。

路径表达式的语法使用一个先导的$来代表需要进行操作的JSON文档,具体的语法如下:

pathExpression:
    scope[(pathLeg)*]

pathLeg:
    member | arrayLocation | doubleAsterisk

member:
    period ( keyName | asterisk )

arrayLocation:
    leftBracket ( nonNegativeInteger | asterisk ) rightBracket

keyName:
    ESIdentifier | doubleQuotedString

doubleAsterisk:
    '**'

period:
    '.'

asterisk:
    '*'

leftBracket:
    '['

rightBracket:
    ']'

大体上讲,.可以选择某个键对应的值,[]可以选择数组中的某个值或者某个范围的值,*通配符可以选择某个指定范围的值。与路径表达式配合使用一些方法,可以实现搜索和修改JSON值,这些方法常见的有:

JSON_EXTRACT()—提取某个值

mysql> SELECT JSON_EXTRACT('{"a":1,"b":2,"c":[3,4,5]}','$.c[*]');
+----------------------------------------------------+
| JSON_EXTRACT('{"a":1,"b":2,"c":[3,4,5]}','$.c[*]') |
+----------------------------------------------------+
| [3, 4, 5]                                          |
+----------------------------------------------------+
1 row in set (0.00 sec)

JSON_SET()—若路径对应的值存在,替换这个值,如果不存在则添加新值。

mysql> SELECT JSON_SET('{"key1":1, "key2":2}', '$.key1', 2);
+-----------------------------------------------+
| JSON_SET('{"key1":1, "key2":2}', '$.key1', 2) |
+-----------------------------------------------+
| {"key1": 2, "key2": 2}                        |
+-----------------------------------------------+
1 row in set (0.00 sec)

JSON_INSERT()—添加新值,但是不替换已经存在的值(忽略旧值)

mysql> SELECT JSON_INSERT('[1,2,3]', '$[0]', 0, '$[3]', 4);
+----------------------------------------------+
| JSON_INSERT('[1,2,3]', '$[0]', 0, '$[3]', 4) |
+----------------------------------------------+
| [1, 2, 3, 4]                                 |
+----------------------------------------------+
1 row in set (0.01 sec)

JSON_REPLACE()—替换已经存在的值,忽略新值

mysql> SELECT JSON_REPLACE('[1,2,3]', '$[0]', 0, '$[3]', 4);
+-----------------------------------------------+
| JSON_REPLACE('[1,2,3]', '$[0]', 0, '$[3]', 4) |
+-----------------------------------------------+
| [0, 2, 3]                                     |
+-----------------------------------------------+
1 row in set (0.00 sec)

JSON_REMOVE()—删除某个值

mysql> SELECT JSON_REMOVE('[[1,2],{"key1":1, "key2":"value2"}]', '$[1].key1');
+-----------------------------------------------------------------+
| JSON_REMOVE('[[1,2],{"key1":1, "key2":"value2"}]', '$[1].key1') |
+-----------------------------------------------------------------+
| [[1, 2], {"key2": "value2"}]                                    |
+-----------------------------------------------------------------+
1 row in set (0.00 sec)

比较和排序JSON值

JSON值可以通过使用=,<,<=,>,>=,<>,!=<=>操作符进行比较。

JSON值不支持使用一下操作符/函数进行比较:

  • BETWEEN
  • IN()
  • GREATEST()
  • LEAST()
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容