zoukankan      html  css  js  c++  java
  • MySQL json 操作

    MySQL--JSON

    MySQL 学习
    
    MySQL 学习笔记( Day009 :JSON )
    一. MySQL JSON 类型
    
    1. JSON 介绍
    2. JSON 格式示例
    3. JSON VS BLOB
      4.结构化和非结构化
    4. JSON 操作示例
      5.1 JSON 入门
      5.2 JSON 常用函数介绍
      5.3 JSON 创建索引
    二. 附录
    

    一. MySQL JSON类型

    1. JSON 介绍

    • JSON (Java Script Object Notation )是一种轻量级的数据交换语言,并且是独立于语言的文本格式。
    • 一些 NoSQL 数据库 选择 JSON 作为其数据存储格式,比如: MongoDB 、CouchDB 等。
    • MySQL 5.7.x 开始支持 JSON 数据类型。

    官方文档 (JSON 类型 )

    2. JSON 格式示例

    --

    -- 摘自 维基百科

    --

    {

    "firstName": "John", -- Key : Value 格式
    "lastName": "Smith",
    "sex": "male",
    "age": 25,
    "address": -- Key : Value ; 其中 Value 也是一个 Key-Value 的结构
    {
    "streetAddress": "21 2nd Street",
    "city": "New York",
    "state": "NY",
    "postalCode": "10021"
    },
    "phoneNumber":
    [
    {
    "type": "home",
    "number": "212 555-1234"
    },
    {
    "type": "fax",
    "number": "646 555-4567"
    }
    ]
    }
    

    3. JSON VS BLOB

    • JSON

    ◦ JSON 数据可以做有效性检查 ;

    ◦ JSON 使得查询性能提升 ;

    ◦ JSON 支持部分属性索引,通过虚拟列的功能可以对 JSON 中的部分数据进行索引 ;

    • BLOB

    ◦ BLOB 类型无法在数据库层做约束性检查 ;

    ◦ BLOB 进行查询,需要遍历所有字符串 ;

    ◦ BLOB 做只能做指定长度的索引 ;

    5.7 之前,只能把 JSON 当作 BLOB 进行存储。数据库层面无法对 JSON 数据做一些操作,只能由应用程序处理。

    4.结构化和非结构化

    • 结构化

    ◦ 二维表结构(行和列)

    ◦ 使用 SQL 语句进行操作

    • 非结构化

    ◦ 使用 Key-Value 格式定义数据,无结构定义
    ◦ Value 可以嵌套 Key-Value 格式的数据
    ◦ 使用 JSON 进行实现
    

    --

    -- SQL 创建 User 表
    --
    create table user (
    id bigint not null auto_increment,
    user_name varchar ( 10 ),
    age int ,
    primary key (id)
    );
    

    # JSON 定义的 User 表
    #
    
    db.user.insert({
    user_name: "tom" ,
    age: 30
    })
    
    db.createCollection( "user" )
    

    5. JSON 操作示例

    5.1 JSON 入门

    --

    -- 创建带 json 字段的表
    --
    mysql> create table user (
    -> uid int auto_increment,
    -> data json,
    -> primary key (uid)
    -> );
    Query OK, 0 rows affected (0.11 sec)
    
    --
    -- 插入 json 数据
    --
    mysql> insert into user values (
    -> null , -- 自增长数据,可以插入 null
    -> '{
    ' > "name" :"tom" ,
    '> "age":18,
    ' > "address" :"SZ"
    '> }'
    -> );
    Query OK, 1 row affected (0.03 sec)
    
    mysql> insert into user values (
    -> null ,
    -> '{
    ' > "name" :"jim" ,
    '> "age":28,
    ' > "mail" :"jim@163.com"
    '> }'
    -> );
    Query OK, 1 row affected (0.02 sec)
    
    mysql> insert into user values ( null , "can you insert it?" ); -- 无法插入,因为是 JSON 类型
    ERROR 3140 (22032): Invalid JSON text: "Invalid value." at position 0 in value (or column) can you insert it?. -- 这短话有单引号,但是渲染有问题,所以这里去掉了
    
    mysql> select * from user ;
    +-----+---------------------------------------------------+
    | uid | data |
    +-----+---------------------------------------------------+
    | 1 | {"age": 18, "name": "tom", "address": "SZ"} | -- 这个 json 中有 address 字段
    | 2 | {"age": 28, "mail": "jim@163.com", "name": "jim"} | -- 这个 json 中有 mail 字段
    +-----+---------------------------------------------------+
    2 rows in set ( 0.00 sec)
    

    5.2 JSON 常用函数介绍

    --

    -- 使用 json_extract 提取数据
    -- 原型 : JSON_EXTRACT(json_doc, path[, path] ...)
    --
    mysql> select json_extract( '[10, 20, [30, 40]]' , '$[1]' );
    +--------------------------------------------+
    | json_extract('[10, 20, [30, 40]]', '$[1]') |
    +--------------------------------------------+
    | 20 | -- 从list 中抽取 下标 为 1 的元素(下标从 0 开始)
    +--------------------------------------------+
    1 row in set ( 0.00 sec)
    
    mysql> select
    -> json_extract( data , '$.name' ), -- 提起 name 字段的数据
    -> json_extract( data , '$.address' ) -- 提取 address字段的数据
    -> from user ;
    +------------------------------+---------------------------------+
    | json_extract(data, '$.name') | json_extract(data, '$.address') |
    +------------------------------+---------------------------------+
    | "tom" | "SZ" |
    | "jim" | NULL | -- jim 没有 address 字段,填充了 NULL
    +------------------------------+---------------------------------+
    2 rows in set ( 0.00 sec)
    

    --

    -- json_object 将list(K-V 对)封装成 json 格式
    -- 原型 : JSON_OBJECT([key, val[, key, val] ...])
    --
    mysql> select json_object( "name" , "jery" , "email" , "jery@163.com" , "age" , 33 );
    +----------------------------------------------------------------+
    | json_object("name", "jery", "email", "jery@163.com", "age",33) |
    +----------------------------------------------------------------+
    | {"age": 33, "name": "jery", "email": "jery@163.com"} | -- 封装成了 K-V 对
    +----------------------------------------------------------------+
    1 row in set ( 0.00 sec)
    
    mysql> insert into user values (
    -> null ,
    -> json_object("name" , "jery" , "email", "jery@163.com" , "age" , 33 ) -- 进行封装
    -> );
    Query OK, 1 row affected (0.03 sec)
    
    mysql> select * from user ;
    +-----+------------------------------------------------------+
    | uid | data |
    +-----+------------------------------------------------------+
    | 1 | {"age": 18, "name": "tom", "address": "SZ"} |
    | 2 | {"age": 28, "mail": "jim@163.com", "name": "jim"} |
    | 4 | {"age": 33, "name": "jery", "email": "jery@163.com"} |
    +-----+------------------------------------------------------+
    3 rows in set ( 0.00 sec)
    

    --

    -- json_insert 插入数据
    -- 原型 : JSON_INSERT(json_doc, path, val[, path, val] ...)
    --
    mysql> set @j = '{ "a": 1, "b": [2, 3]}' ;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select json_insert(@j, '$.a' , 10 , '$.c' , '[true, false]' );
    +----------------------------------------------------+
    | json_insert(@j, '$.a', 10, '$.c', '[true, false]') |
    +----------------------------------------------------+
    | {"a": 1, "b": [2, 3], "c": "[true, false]"} | -- a 还是 =1 ,存在的被忽略,不受影响
    +----------------------------------------------------+ -- c 之前不存在,则插入
    1 row in set ( 0.00 sec)
    
    mysql> update user set data = json_insert( data , "$.address_2" , "BJ" ) where uid = 1 ; -- 插入 addres_
    Query OK, 1 row affected (0.03 sec)
    Rows matched: 1 Changed: 1 Warnings: 0
    
    mysql> select * from user ;
    +-----+----------------------------------------------------------------+
    | uid | data |
    +-----+----------------------------------------------------------------+
    | 1 | {"age": 18, "name": "tom", "address": "SZ", "address_2": "BJ"} | -- 增加了 addres_2 : "BJ"
    | 2 | {"age": 28, "mail": "jim@163.com", "name": "jim"} |
    | 4 | {"age": 33, "name": "jery", "email": "jery@163.com"} |
    +-----+----------------------------------------------------------------+
    3 rows in set ( 0.00 sec)
    

    --

    -- json_merge 合并数据并返回。注意:原数据不受影响
    -- 原型 : JSON_MERGE(json_doc, json_doc[, json_doc] ...)
    --
    mysql> select json_merge( '{"name": "x"}' , '{"id": 47}' ); -- 原来有两个 JSON
    +-------------------------------------------+
    | json_merge('{"name": "x"}', '{"id": 47}') |
    +-------------------------------------------+
    | {"id": 47, "name": "x"} | -- 合并多个 JSON
    +-------------------------------------------+
    1 row in set ( 0.00 sec)
    
    mysql> select
    -> json_merge(
    -> json_extract( data , '$.address' ), -- json 1
    -> json_extract( data , '$.address_2' )) -- jons 2
    -> from user where uid = 1 ;
    +---------------------------------------------------------------------------------+
    | json_merge( json_extract(data, '$.address'), json_extract(data, '$.address_2')) |
    +---------------------------------------------------------------------------------+
    | ["SZ", "BJ"] | -- 合并成一个 json
    +---------------------------------------------------------------------------------+
    1 row in set ( 0.00 sec)
    
    --
    -- json_array_append 追加数据
    -- 原型 : JSON_ARRAY_APPEND(json_doc, path, val[, path, val] ...)
    -- json_append 在5.7.9 中重命名为 json_array_append
    --
    mysql> set @j = '["a", ["b", "c"], "d"]' ; -- 下标为 1 的元素中只有 ["b", "c"]
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select json_array_append(@j, '$[1]' , 1 );
    +----------------------------------+
    | json_array_append(@j, '$[1]', 1) |
    +----------------------------------+
    | ["a", ["b", "c", 1], "d"] | -- 现在插入了 数字 1
    +----------------------------------+
    1 row in set ( 0.00 sec)
    mysql> update user set data = json_array_append(
    -> data ,
    -> '$.address' ,
    -> json_extract( data , '$.address_2' ))
    -> where uid = 1 ;
    Query OK, 1 row affected (0.02 sec)
    Rows matched: 1 Changed: 1 Warnings: 0
    
    mysql> select * from user ;
    +-----+------------------------------------------------------------------------+
    | uid | data |
    +-----+------------------------------------------------------------------------+
    | 1 | {"age": 18, "name": "tom", "address": ["SZ", "BJ"], "address_2": "BJ"} | --address_2 追加到 address
    | 2 | {"age": 28, "mail": "jim@163.com", "name": "jim"} |
    | 4 | {"age": 33, "name": "jery", "email": "jery@163.com"} |
    +-----+------------------------------------------------------------------------+
    3 rows in set ( 0.00 sec)
    

    --

    -- json_remove 从json 记录中删除数据
    -- 原型 : JSON_REMOVE(json_doc, path[, path] ...)
    --
    mysql> set @j = '["a", ["b", "c"], "d"]' ;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> select json_remove(@j, '$[1]' );
    +-------------------------+
    | json_remove(@j, '$[1]') |
    +-------------------------+
    | ["a", "d"] | -- 删除了下标为 1 的元素 ["b", "c"]
    +-------------------------+
    1 row in set ( 0.00 sec)
    
    mysql> update user set data = json_remove( data , "$.address_2" ) where uid = 1 ;
    Query OK, 1 row affected (0.03 sec)
    Rows matched: 1 Changed: 1 Warnings: 0
    
    mysql> select * from user ;
    +-----+------------------------------------------------------+
    | uid | data |
    +-----+------------------------------------------------------+
    | 1 | {"age": 18, "name": "tom", "address": ["SZ", "BJ"]} | -- address_2 的字段删除了
    | 2 | {"age": 28, "mail": "jim@163.com", "name": "jim"} |
    | 4 | {"age": 33, "name": "jery", "email": "jery@163.com"} |
    +-----+------------------------------------------------------+
    3 rows in set ( 0.00 sec)
    

    官方文档 (JSON 函数 )

    5.3 JSON 创建索引

    JSON 类型数据本身 无法直接 创建索引,需要将需要索引的 JSON 数据 重新 生成虚拟列 (Virtual Columns) 之后,对 该列 进行 索引
    

    官方文档 –JSON 创建索引

    • 新建表时创建 JSON 索引

    mysql> create table test_inex_1(
    -> data json,
    -> gen_col varchar ( 10 ) generated always as (json_extract( data , '$.name' )), -- 抽取 data 中的 name , 生成新的一列,名字为 gen_col
    -> index idx (gen_col) -- 将gen_col 作为索引
    -> );
    Query OK, 0 rows affected (0.13 sec)
    
    mysql> show create table test_index_1 ;
    -- -----省略表格线 -----
    | test_index_1 | CREATE TABLE `test_index_1` (
    `data` json DEFAULT NULL ,
    `gen_col` varchar ( 10 ) GENERATED ALWAYS AS (json_extract( data , '$.name' )) VIRTUAL,
    KEY `idx` ( `gen_col` )
    ) ENGINE =InnoDB DEFAULT CHARSET=utf8mb4 |
    -- -----省略表格线 -----
    1 row in set ( 0.00 sec)
    
    mysql> insert into test_index_1( data ) values ( '{"name":"tom", "age":18, "address":"SH"}');
    Query OK, 1 row affected (0.04 sec)
    
    mysql> insert into test_index_1( data ) values ( '{"name":"jim", "age":28, "address":"SZ"}');
    Query OK, 1 row affected (0.03 sec)
    
    mysql> select * from test_index_1;
    +---------------------------------------------+---------+
    | data | gen_col |
    +---------------------------------------------+---------+
    | {"age": 18, "name": "tom", "address": "SH"} | "tom" |
    | {"age": 28, "name": "jim", "address": "SZ"} | "jim" |
    +---------------------------------------------+---------+
    2 rows in set ( 0.00 sec)
    
    mysql> select json_extract( data ,"$.name" ) as username from test_index_1 where gen_col= "tom" ; -- 如果这样做,为空,原因如下
    Empty set ( 0.00 sec)
    
    mysql> select hex ('"' );
    +----------+
    | hex('"') |
    +----------+
    | 22 | -- 双引号的 16 进制
    +----------+
    1 row in set ( 0.00 sec)
    
    mysql> select hex (gen_col) from test_index_1;
    +--------------+
    | hex(gen_col) |
    +--------------+
    | 226A696D22 | -- 双引号本身也作为了存储内容
    | 22746F6D22 |
    +--------------+
    2 rows in set ( 0.00 sec)
    
    mysql> select json_extract( data ,"$.name" ) as username from test_index_1 where gen_col= '"tom"' ; -- 使用 '"tome"',用单引号括起来
    +----------+
    | username |
    +----------+
    | "tom" | -- 找到了对应的数据
    +----------+
    1 row in set ( 0.00 sec)
    
    mysql> explain select json_extract( data ,"$.name" ) as username from test_index_1 where gen_col= '"tom"' G
    *************************** 1. row ***************************
    id: 1
    select_type: SIMPLE
    table : test_index_
    partitions: NULL
    type: ref
    possible_keys: idx -- 使用了 key idx
    key : idx
    key_len: 43
    ref: const
    rows : 1
    filtered: 100.
    Extra: NULL
    1 row in set , 1 warning ( 0.00 sec)
    
    ---
    --- 建立表的时候去掉双引用
    ---
    
    mysql> create table test_index_2 (
    -> data json,
    -> gen_col varchar ( 10 ) generated always as (
    -> json_unquote( -- 使用 json_unquote 函数进行去掉双引号
    -> json_extract(data , "$.name" )
    -> )),
    -> key idx(gen_col)
    -> );
    Query OK, 0 rows affected (0.13 sec)
    
    mysql> show create table test_index_2;
    -- -----省略表格线 -----
    | test_index_2 | CREATE TABLE `test_index_2` (
    `data` json DEFAULT NULL ,
    `gen_col` varchar ( 10 ) GENERATED ALWAYS AS (json_unquote(
    json_extract( data , "$.name" )
    )) VIRTUAL,
    KEY `idx` ( `gen_col` )
    ) ENGINE =InnoDB DEFAULT CHARSET=utf8mb4 |
    -- -----省略表格线 -----
    1 row in set ( 0.00 sec)
    
    mysql> insert into test_index_2( data ) values ( '{"name":"tom", "age":18, "address":"SH"}');
    Query OK, 1 row affected (0.03 sec)
    
    mysql> insert into test_index_2( data ) values ( '{"name":"jim", "age":28, "address":"SZ"}');
    Query OK, 1 row affected (0.02 sec)
    
    mysql> select json_extract( data ,"$.name" ) as username from test_index_2 where gen_col= "tom" ; -- 未加单引号
    +----------+
    | username |
    +----------+
    | "tom" | -- 可以找到数据
    +----------+
    1 row in set ( 0.00 sec)
    
    mysql> explain select json_extract( data ,"$.name" ) as username from test_index_2 where gen_col= "tom" G
    *************************** 1. row ***************************
    id: 1
    select_type: SIMPLE
    table : test_index_
    partitions: NULL
    type: ref
    possible_keys: idx -- 使用了 key idx
    key : idx
    key_len: 43
    ref: const
    rows : 1
    filtered: 100.
    Extra: NULL
    1 row in set , 1 warning ( 0.00 sec)
    

    • 修改已存在的表创建 JSON 索引

    --

    -- 使用之前的 user 表操作
    --
    mysql> show create table user ;
    -- -----省略表格线 -----
    | user | CREATE TABLE `user` (
    `uid` int ( 11 ) NOT NULL AUTO_INCREMENT,
    `data` json DEFAULT NULL ,
    PRIMARY KEY ( `uid` )
    ) ENGINE =InnoDB AUTO_INCREMENT= 5 DEFAULT CHARSET =utf8mb4 |
    -- -----省略表格线 -----
    1 row in set ( 0.00 sec)
    
    mysql> select * from user ;
    +-----+------------------------------------------------------+
    | uid | data |
    +-----+------------------------------------------------------+
    | 1 | {"age": 18, "name": "tom", "address": ["SZ", "BJ"]} |
    | 2 | {"age": 28, "mail": "jim@163.com", "name": "jim"} |
    | 4 | {"age": 33, "name": "jery", "email": "jery@163.com"} |
    +-----+------------------------------------------------------+
    
    mysql> alter table user
    -> add user_name varchar( 32 )
    -> generated always as (json_extract( data ,"$.name")) virtual;
    Query OK, 0 rows affected (0.05 sec)
    Records: 0 Duplicates: 0 Warnings: 0
    -- virtual 关键字是不将该列的字段值存储,对应的是 stored
    
    mysql> select user_name from user ;
    +-----------+
    | user_name |
    +-----------+
    | "tom" |
    | "jim" |
    | "jery" |
    +-----------+
    3 rows in set ( 0.00 sec)
    
    mysql> alter table user add index idx(user_name);
    Query OK, 0 rows affected (0.13 sec)
    Records: 0 Duplicates: 0 Warnings: 0
    
    mysql> select * from user where user_name= '"tom"' ; -- 加单引号
    +-----+-----------------------------------------------------+-----------+
    | uid | data | user_name |
    +-----+-----------------------------------------------------+-----------+
    | 1 | {"age": 18, "name": "tom", "address": ["SZ", "BJ"]} | "tom" |
    +-----+-----------------------------------------------------+-----------+
    1 row in set ( 0.00 sec)
    
    mysql> explain select * from user where user_name= '"tom"' G
    *************************** 1. row ***************************
    id: 1
    select_type: SIMPLE
    table : user
    partitions: NULL
    type: ref
    possible_keys: idx -- 使用了 key idx
    key : idx
    key_len: 131
    ref: const
    rows : 1
    filtered: 100.
    Extra: NULL
    1 row in set , 1 warning ( 0.00 sec)
    
    mysql> show create table user ;
    -- -----省略表格线 -----
    | user | CREATE TABLE `user` (
    `uid` int ( 11 ) NOT NULL AUTO_INCREMENT,
    `data` json DEFAULT NULL ,
    `user_name` varchar ( 32 ) GENERATED ALWAYS AS (json_extract( data ,"$.name" )) VIRTUAL,
    `user_name2` varchar ( 32 ) GENERATED ALWAYS AS (json_extract( data ,"$.name" )) VIRTUAL,
    PRIMARY KEY ( `uid` ),
    KEY `idx` ( `user_name` )
    ) ENGINE =InnoDB AUTO_INCREMENT= 5 DEFAULT CHARSET =utf8mb4 |
    -- -----省略表格线 -----
    1 row in set ( 0.00 sec)
    

    二. 附录

    --

    -- 老师演示 JSON 的SQL

    --

    drop table if exists User ;
    
    CREATE TABLE User (
    uid BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
    name VARCHAR ( 32 ) NOT NULL ,
    email VARCHAR( 256 ) NOT NULL ,
    address VARCHAR ( 512 ) NOT NULL ,
    UNIQUE KEY (name),
    UNIQUE KEY (email)
    );
    
    INSERT INTO User VALUES ( NULL ,'David' ,'david@gmail' ,'Shanghai ...' );
    INSERT INTO User VALUES ( NULL ,'Amy' ,'amy@gmail' ,'Beijing ...' );
    INSERT INTO User VALUES ( NULL ,'Tom' ,'tom@gmail' ,'Guangzhou ...' );
    
    SELECT * FROM User;
    
    ALTER TABLE User ADD COLUMN address2 VARCHAR ( 512 ) NOT NULL ;
    ALTER TABLE User ADD COLUMN passport VARCHAR ( 64 ) NOT NULL ;
    
    DROP TABLE IF EXISTS UserJson;
    
    CREATE TABLE UserJson(
    uid BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
    data JSON
    );
    
    truncate table UserJson;
    
    insert into UserJson
    SELECT
    uid,JSON_OBJECT( 'name' ,name, 'email' ,email, 'address' ,address) AS data
    FROM
    User ;
    
    SELECT * FROM UserJson;
    
    SELECT uid,JSON_EXTRACT( data ,'$.address2' ) from UserJson;
    
    UPDATE UserJson
    set data = json_insert( data ,"$.address2" ,"HangZhou ..." )
    where uid = 1 ;
    
    SELECT JSON_EXTRACT( data ,'$.address[1]' ) from UserJson;
    
    select json_merge(JSON_EXTRACT( data ,'$.address' ) ,JSON_EXTRACT( data ,'$.address2' ))
    from UserJson;
    
    begin ;
    UPDATE UserJson
    set data = json_array_append( data ,"$.address" ,JSON_EXTRACT( data ,'$.address2' ))
    where JSON_EXTRACT( data ,'$.address2' ) IS NOT NULL AND uid > 0 ;
    select JSON_EXTRACT( data ,'$.address' ) from UserJson;
    UPDATE UserJson
    set data = JSON_REMOVE( data ,'$.address2' )
    where uid> 0 ;
    commit ;
    
  • 相关阅读:
    第九周学习进度总结
    SLR(1)语法分析(JAVA实现)
    算符优先分析
    第八周学习进度总结
    Android 自学之相对布局 RelativeLayout
    Android 自学之表格布局 TableLayout
    Android 自学之线性布局 LinearLayout
    Android 自学之帧布局 FrameLayout
    机器学习进度05(FaceBook案例)
    机器学习进度04(转换器、预估器、K-近邻算法、模式选择与调优)
  • 原文地址:https://www.cnblogs.com/securitybob/p/13672020.html
Copyright © 2011-2022 走看看