当前位置:   article > 正文

一文说透 MySQL JSON 数据类型(收藏)

mysql json

JSON 数据类型是 MySQL 5.7.8 开始支持的。在此之前,只能通过字符类型(CHAR,VARCHAR 或 TEXT )来保存 JSON 文档。

相对字符类型,原生的 JSON 类型具有以下优势:

  1. 在插入时能自动校验文档是否满足 JSON 格式的要求。
  2. 优化了存储格式。无需读取整个文档就能快速访问某个元素的值。

在 JSON 类型引入之前,如果我们想要获取 JSON 文档中的某个元素,必须首先读取整个 JSON 文档,然后在客户端将其转换为 JSON 对象,最后再通过对象获取指定元素的值。

下面是 Python 中的获取方式。

  1. import json
  2. # JSON 字符串:
  3. x =  '{ "name":"John""age":30"city":"New York"}'
  4. # 将 JSON 字符串转换为 JSON 对象:
  5. y = json.loads(x)
  6. # 读取 JSON 对象中指定元素的值:
  7. print(y["age"])

这种方式有两个弊端:一、消耗磁盘 IO,二、消耗网络带宽,如果 JSON 文档比较大,在高并发场景,有可能会打爆网卡。

如果使用的是 JSON 类型,相同的需求,直接使用 SQL 命令就可搞定。不仅能节省网络带宽,结合后面提到的函数索引,还能降低磁盘 IO 消耗。

  1. mysql> create table t(c1 json);
  2. Query OK, 0 rows affected (0.09 sec)
  3. mysql> insert into t values('{ "name":"John", "age":30, "city":"New York"}');
  4. Query OK, 1 row affected (0.01 sec)
  5. mysql> select c1->"$.age" from t;
  6. +-------------+
  7. | c1->"$.age" |
  8. +-------------+
  9. 30          |
  10. +-------------+
  11. 1 row in set (0.00 sec)

本文将从以下几个方面展开:

  1. 什么是 JSON。
  2. JSON 字段的增删改查操作。
  3. 如何对 JSON 字段创建索引。
  4. 如何将存储 JSON 字符串的字符字段升级为 JSON 字段。
  5. 使用 JSON 时的注意事项。
  6. Partial Updates。
  7. 其它 JSON 函数。

一、什么是 JSON

JSON 是 JavaScript Object Notation(JavaScript 对象表示法)的缩写,是一个轻量级的,基于文本的,跨语言的数据交换格式。易于阅读和编写。

JSON 的基本数据类型如下:

  • 数值:十进制数,不能有前导 0,可以为负数或小数,还可以为 e 或 E 表示的指数。

  • 字符串:字符串必须用双引号括起来。

  • 布尔值:true,false。

  • 数组:一个由零或多个值组成的有序序列。每个值可以为任意类型。数组使用方括号[] 括起来,元素之间用逗号,分隔。譬如,

    [1"abc"nulltrue"10:27:06.000000", {"id"1}]
    
  • 对象:一个由零或者多个键值对组成的无序集合。其中键必须是字符串,值可以为任意类型。

    对象使用花括号{}括起来,键值对之间使用逗号,分隔,键与值之间用冒号:分隔。譬如,

    {"db": ["mysql""oracle"], "id"123"info": {"age"20}}
    
  • 空值:null。

二、JSON 字段的增删改查操作

下面我们看看 JSON 字段常见的增删改查操作:

2.1 插入操作

可直接插入 JSON 格式的字符串。

  1. mysql> create table t(c1 json);
  2. Query OK, 0 rows affected (0.03 sec)
  3. mysql> insert into t values('[1, "abc", null, true, "08:45:06.000000"]');
  4. Query OK, 1 row affected (0.01 sec)
  5. mysql> insert into t values('{"id": 87, "name": "carrot"}');
  6. Query OK, 1 row affected (0.01 sec)

也可使用函数,常用的有 JSON_ARRAY() 和 JSON_OBJECT(),前者用于构造 JSON 数组,后者用于构造 JSON 对象。如,

  1. mysql> select json_array(1"abc"nulltrue,curtime());
  2. +--------------------------------------------+
  3. | json_array(1"abc"nulltrue,curtime()) |
  4. +--------------------------------------------+
  5. | [1"abc"nulltrue"10:12:25.000000"]  |
  6. +--------------------------------------------+
  7. 1 row in set (0.01 sec)
  8. mysql> select json_object('id'87'name''carrot');
  9. +-----------------------------------------+
  10. | json_object('id'87'name''carrot') |
  11. +-----------------------------------------+
  12. | {"id"87"name""carrot"}            |
  13. +-----------------------------------------+
  14. 1 row in set (0.00 sec)

对于 JSON 文档,KEY 名不能重复。

如果插入的值中存在重复 KEY,在 MySQL 8.0.3 之前,遵循 first duplicate key wins 原则,会保留第一个 KEY,后面的将被丢弃掉。

从 MySQL 8.0.3 开始,遵循的是 last duplicate key wins 原则,只会保留最后一个 KEY。

下面通过一个具体的示例来看看两者的区别。

MySQL 5.7.36

  1. mysql> select json_object('key1',10,'key2',20,'key1',30);
  2. +--------------------------------------------+
  3. | json_object('key1',10,'key2',20,'key1',30) |
  4. +--------------------------------------------+
  5. | {"key1"10"key2"20}                   |
  6. +--------------------------------------------+
  7. 1 row in set (0.02 sec)

MySQL 8.0.27

  1. mysql> select json_object('key1',10,'key2',20,'key1',30);
  2. +--------------------------------------------+
  3. | json_object('key1',10,'key2',20,'key1',30) |
  4. +--------------------------------------------+
  5. | {"key1"30"key2"20}                   |
  6. +--------------------------------------------+
  7. 1 row in set (0.00 sec)

2.2 查询操作

JSON_EXTRACT(json_doc, path[, path] ...)

其中,json_doc 是 JSON 文档,path 是路径。该函数会从 JSON 文档提取指定路径(path)的元素。如果指定 path 不存在,会返回 NULL。可指定多个 path,匹配到的多个值会以数组形式返回。

下面我们结合一些具体的示例来看看 path 及 JSON_EXTRACT 的用法。

首先我们看看数组。

数组的路径是通过下标来表示的。第一个元素的下标是 0。

  1. mysql> select json_extract('[10, 20, [30, 40]]''$[0]');
  2. +--------------------------------------------+
  3. | json_extract('[10, 20, [30, 40]]''$[0]') |
  4. +--------------------------------------------+
  5. 10                                         |
  6. +--------------------------------------------+
  7. 1 row in set (0.00 sec)
  8. mysql> select json_extract('[10, 20, [30, 40]]''$[0]''$[1]','$[2][0]');
  9. +--------------------------------------------------------------+
  10. | json_extract('[10, 20, [30, 40]]''$[0]''$[1]','$[2][0]') |
  11. +--------------------------------------------------------------+
  12. | [102030]                                                 |
  13. +--------------------------------------------------------------+
  14. 1 row in set (0.00 sec)

除此之外,还可通过 [M to N] 获取数组的子集。

  1. mysql> select json_extract('[10, 20, [30, 40]]''$[0 to 1]');
  2. +-------------------------------------------------+
  3. | json_extract('[10, 20, [30, 40]]''$[0 to 1]') |
  4. +-------------------------------------------------+
  5. | [1020]                                        |
  6. +-------------------------------------------------+
  7. 1 row in set (0.00 sec)
  8. # 这里的 last 代表最后一个元素的下标
  9. mysql> select json_extract('[10, 20, [30, 40]]''$[last-1 to last]');
  10. +---------------------------------------------------------+
  11. | json_extract('[10, 20, [30, 40]]''$[last-1 to last]') |
  12. +---------------------------------------------------------+
  13. | [20, [3040]]                                          |
  14. +---------------------------------------------------------+
  15. 1 row in set (0.00 sec)

也可通过 [*] 获取数组中的所有元素。

  1. mysql> select json_extract('[10, 20, [30, 40]]''$[*]');
  2. +--------------------------------------------+
  3. | json_extract('[10, 20, [30, 40]]''$[*]') |
  4. +--------------------------------------------+
  5. | [1020, [3040]]                         |
  6. +--------------------------------------------+
  7. 1 row in set (0.00 sec)

接下来,我们看看对象。

对象的路径是通过 KEY 来表示的。

  1. mysql> set @j='{"a": 1, "b": [2, 3], "a c": 4}';
  2. Query OK, 0 rows affected (0.00 sec)
  3. # 如果 KEY 在路径表达式中不合法(譬如存在空格),则在引用这个 KEY 时,需用双引号括起来。
  4. mysql> select json_extract(@j, '$.a'), json_extract(@j, '$."a c"'), json_extract(@j, '$.b[1]');
  5. +-------------------------+-----------------------------+----------------------------+
  6. | json_extract(@j, '$.a') | json_extract(@j, '$."a c"') | json_extract(@j, '$.b[1]') |
  7. +-------------------------+-----------------------------+----------------------------+
  8. 1                       | 4                           | 3                          |
  9. +-------------------------+-----------------------------+----------------------------+
  10. 1 row in set (0.00 sec)

除此之外,还可通过 .* 获取对象中的所有元素。

  1. mysql> select json_extract('{"a": 1, "b": [2, 3], "a c": 4}''$.*');
  2. +--------------------------------------------------------+
  3. | json_extract('{"a": 1, "b": [2, 3], "a c": 4}''$.*') |
  4. +--------------------------------------------------------+
  5. | [1, [23], 4]                                         |
  6. +--------------------------------------------------------+
  7. 1 row in set (0.00 sec)
  8. # 这里的 $**.b 匹配 $.a.b 和 $.c.b
  9. mysql> select json_extract('{"a": {"b": 1}, "c": {"b": 2}}''$**.b');
  10. +---------------------------------------------------------+
  11. | json_extract('{"a": {"b": 1}, "c": {"b": 2}}''$**.b') |
  12. +---------------------------------------------------------+
  13. | [12]                                                  |
  14. +---------------------------------------------------------+
  15. 1 row in set (0.00 sec)

column->path

column->path,包括后面讲到的 column->>path,都是语法糖,在实际使用的时候都会转化为 JSON_EXTRACT。

column->path 等同于 JSON_EXTRACT(column, path) ,只能指定一个path。

  1. create table t(c2 json);
  2. insert into t values('{"empno": 1001, "ename": "jack"}'), ('{"empno": 1002, "ename": "mark"}');
  3. mysql> select c2, c2->"$.ename" from t;
  4. +----------------------------------+---------------+
  5. | c2                               | c2->"$.ename" |
  6. +----------------------------------+---------------+
  7. | {"empno"1001"ename""jack"} | "jack"        |
  8. | {"empno"1002"ename""mark"} | "mark"        |
  9. +----------------------------------+---------------+
  10. 2 rows in set (0.00 sec)
  11. mysql> select * from t where c2->"$.empno" = 1001;
  12. +------+----------------------------------+
  13. | c1   | c2                               |
  14. +------+----------------------------------+
  15. |    1 | {"empno"1001"ename""jack"} |
  16. +------+----------------------------------+
  17. 1 row in set (0.00 sec)

column->>path

同 column->path 类似,只不过其返回的是字符串。以下三者是等价的。

  • JSON_UNQUOTE( JSON_EXTRACT(column, path) )
  • JSON_UNQUOTE(column -> path)
  • column->>path
  1. mysql> select c2->'$.ename',json_extract(c2"$.ename"),json_unquote(c2->'$.ename'),c2->>'$.ename' from t;
  2. +---------------+-----------------------------+-----------------------------+----------------+
  3. | c2->'$.ename' | json_extract(c2"$.ename") | json_unquote(c2->'$.ename') | c2->>'$.ename' |
  4. +---------------+-----------------------------+-----------------------------+----------------+
  5. "jack"        | "jack"                      | jack                        | jack           |
  6. "mark"        | "mark"                      | mark                        | mark           |
  7. +---------------+-----------------------------+-----------------------------+----------------+
  8. 2 rows in set (0.00 sec)

2.3 修改操作

JSON_INSERT(json_doc, path, val[, path, val] ...)

插入新值。

仅当指定位置或指定 KEY 的值不存在时,才执行插入操作。另外,如果指定的 path 是数组下标,且 json_doc 不是数组,该函数首先会将 json_doc 转化为数组,然后再插入新值。

下面我们看几个示例。

  1. mysql> select json_insert('1','$[0]',"10");
  2. +------------------------------+
  3. | json_insert('1','$[0]',"10") |
  4. +------------------------------+
  5. 1                            |
  6. +------------------------------+
  7. 1 row in set (0.00 sec)
  8. mysql> select json_insert('1','$[1]',"10");
  9. +------------------------------+
  10. | json_insert('1','$[1]',"10") |
  11. +------------------------------+
  12. | [1"10"]                    |
  13. +------------------------------+
  14. 1 row in set (0.01 sec)
  15. mysql> select json_insert('["1","2"]','$[2]',"10");
  16. +--------------------------------------+
  17. | json_insert('["1","2"]','$[2]',"10") |
  18. +--------------------------------------+
  19. | ["1""2""10"]                     |
  20. +--------------------------------------+
  21. 1 row in set (0.00 sec)
  22. mysql> set @j = '{ "a": 1, "b": [2, 3]}';
  23. Query OK, 0 rows affected (0.00 sec)
  24. mysql> select json_insert(@j, '$.a'10'$.c''[true, f
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/158057
推荐阅读
相关标签
  

闽ICP备14008679号