首页 > 数据库 >MySQL · 最佳实践 · 如何索引JSON字段

MySQL · 最佳实践 · 如何索引JSON字段

时间:2024-11-21 23:43:26浏览次数:3  
标签:字段 JSON games names MySQL NULL id

目录标题

概述

MySQL从5.7.8起开始支持JSON字段,这极大的丰富了MySQL的数据类型。也方便了广大开发人员。但MySQL并没有提供对JSON对象中的字段进行索引的功能,至少没有直接对其字段进行索引的方法。本文将介绍利用MySQL 5.7中的虚拟字段的功能来对JSON对象中的字段进行索引。

示例数据

我们将基于下面的JSON对象进行演示

{
    "id": 1,  
    "name": "Sally",  
    "games_played":{    
       "Battlefield": {
          "weapon": "sniper rifle",
          "rank": "Sergeant V",
          "level": 20
        },                                                                                                                          
       "Crazy Tennis": {
          "won": 4,
          "lost": 1
        },  
       "Puzzler": {
          "time": 7
        }
     }
 }

表的基本结构

CREATE TABLE `players` (  
    `id` INT UNSIGNED NOT NULL,
    `player_and_games` JSON NOT NULL,
    PRIMARY KEY (`id`)
);

如果只是基于上面的表的结构我们是无法对JSON字段中的Key进行索引的。接下来我们演示如何借助虚拟字段对其进行索引

增加虚拟字段

虚拟列语法如下

<type> [ GENERATED ALWAYS ] AS ( <expression> ) [ VIRTUAL|STORED ]
[ UNIQUE [KEY] ] [ [PRIMARY] KEY ] [ NOT NULL ] [ COMMENT <text> ]

在MySQL 5.7中,支持两种Generated Column,即Virtual Generated ColumnStored Generated Column,前者只将Generated Column保存在数据字典中(表的元数据),并不会将这一列数据持久化到磁盘上;后者会将Generated Column持久化到磁盘上,而不是每次读取的时候计算所得。很明显,后者存放了可以通过已有数据计算而得的数据,需要更多的磁盘空间,与Virtual Column相比并没有优势,因此,MySQL 5.7中,不指定Generated Column的类型,默认是Virtual Column。

如果需要Stored Generated Golumn的话,可能在Virtual Generated Column上建立索引更加合适,一般情况下,都使用Virtual Generated Column,这也是MySQL默认的方式

加完虚拟列的建表语句如下:

CREATE TABLE `players` (  
   `id` INT UNSIGNED NOT NULL,
   `player_and_games` JSON NOT NULL,
   `names_virtual` VARCHAR(20) GENERATED ALWAYS AS (`player_and_games` ->> '$.name') NOT NULL, 
   PRIMARY KEY (`id`)
);

Note: 利用操作符-»
来引用JSON字段中的KEY。在本例中字段names_virtual为虚拟字段,我把它定义成不可以为空。在实际的工作中,一定要集合具体的情况来定。因为JSON本身是一种弱结构的数据对象。也就是说的它的结构不是固定不变的。


我们插入数据

INSERT INTO `players` (`id`, `player_and_games`) VALUES (1, '{  
    "id": 1,  
    "name": "Sally",
    "games_played":{    
       "Battlefield": {
          "weapon": "sniper rifle",
          "rank": "Sergeant V",
          "level": 20
        },                                                                                                                          
       "Crazy Tennis": {
          "won": 4,
          "lost": 1
        },  
       "Puzzler": {
          "time": 7
        }
      }
   }'
);
...

查看表里的数据

SELECT * FROM `players`;

+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+
| id | player_and_games                                                                                                                                                                                           | names_virtual |
+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+
|  1 | {"id": 1, "name": "Sally", "games_played": {"Puzzler": {"time": 7}, "Battlefield": {"rank": "Sergeant V", "level": 20, "weapon": "sniper rifle"}, "Crazy Tennis": {"won": 4, "lost": 1}}}                  | Sally         |
|  2 | {"id": 2, "name": "Thom", "games_played": {"Puzzler": {"time": 25}, "Battlefield": {"rank": "Major General VIII", "level": 127, "weapon": "carbine"}, "Crazy Tennis": {"won": 10, "lost": 30}}}            | Thom          |
|  3 | {"id": 3, "name": "Ali", "games_played": {"Puzzler": {"time": 12}, "Battlefield": {"rank": "First Sergeant II", "level": 37, "weapon": "machine gun"}, "Crazy Tennis": {"won": 30, "lost": 21}}}           | Ali           |
|  4 | {"id": 4, "name": "Alfred", "games_played": {"Puzzler": {"time": 10}, "Battlefield": {"rank": "Chief Warrant Officer Five III", "level": 73, "weapon": "pistol"}, "Crazy Tennis": {"won": 47, "lost": 2}}} | Alfred        |
|  5 | {"id": 5, "name": "Phil", "games_played": {"Puzzler": {"time": 7}, "Battlefield": {"rank": "Lt. Colonel III", "level": 98, "weapon": "assault rifle"}, "Crazy Tennis": {"won": 130, "lost": 75}}}          | Phil          |
|  6 | {"id": 6, "name": "Henry", "games_played": {"Puzzler": {"time": 17}, "Battlefield": {"rank": "Captain II", "level": 87, "weapon": "assault rifle"}, "Crazy Tennis": {"won": 68, "lost": 149}}}             | Henry         |
+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------+

查看表Players的字段

SHOW COLUMNS FROM `players`;

+------------------+------------------+------+-----+---------+-------------------+
| Field            | Type             | Null | Key | Default | Extra             |
+------------------+------------------+------+-----+---------+-------------------+
| id               | int(10) unsigned | NO   | PRI | NULL    |                   |
| player_and_games | json             | NO   |     | NULL    |                   |
| names_virtual    | varchar(20)      | NO   |     | NULL    | VIRTUAL GENERATED |
+------------------+------------------+------+-----+---------+-------------------+

我们看到虚拟字段names_virtual的类型是VIRTUAL GENERATED。MySQL只是在数据字典里保存该字段元数据,并没有真正的存储该字段的值。这样表的大小并没有增加。我们可以利用索引把这个字段上的值进行物理存储。

在虚拟字段上加索引

再添加索引之前,让我们先看下面查询的执行计划

EXPLAIN SELECT * FROM `players` WHERE `names_virtual` = "Sally"\G  
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: players
   partitions: NULL
         type: ALL
possible_keys: NULL  
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 6
     filtered: 16.67
        Extra: Using where

添加索引

CREATE INDEX `names_idx` ON `players`(`names_virtual`);  

再执行上面的查询语句,我们将得到不一样的执行计划

EXPLAIN SELECT * FROM `players` WHERE `names_virtual` = "Sally"\G  
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: players
   partitions: NULL
         type: ref
possible_keys: names_idx  
          key: names_idx
      key_len: 22
          ref: const
         rows: 1
     filtered: 100.00
        Extra: NULL

如我们所见,最新的执行计划走了新建的索引。

小结

本文介绍了如何在MySQL 5.7中保存JSON文档。为了高效的检索JSON中内容,我们可以利用5.7的虚拟字段来对JSON的不同的KEY来建索引。极大的提高检索的速度。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们处理,核实后本网站将在24小时内删除侵权内容。

在这里插入图片描述

标签:字段,JSON,games,names,MySQL,NULL,id
From: https://blog.csdn.net/Larry_794204525/article/details/143707068

相关文章

  • Mysql没有开启binlog日志还会执行二阶段提交吗?
    ......
  • 基于Java+Springboot+Jpa+Mysql实现的在线网盘文件分享系统功能设计与实现五
    一、前言介绍:免费学习:猿来入此1.1项目摘要在线网盘文件分享系统的课题背景主要源于现代社会对数字化信息存储和共享需求的日益增长。随着互联网的普及和技术的快速发展,人们越来越依赖电子设备来存储和传输各种类型的数据文件。然而,传统的本地存储方式存在诸多不便,如空间有限、......
  • 带你从入门到精通——MySQL(四. 函数基础)
     建议先阅读我之前的博客,掌握一定的MySQL前置知识后再阅读本文,链接如下:带你从入门到精通——MySQL(一.基础知识)-CSDN博客带你从入门到精通——MySQL(二.单表查询)-CSDN博客带你从入门到精通——MySQL(三.多表查询)-CSDN博客目录四. MySQL函数基础4.1数值函数4.1.1ROU......
  • kettle spoon连接mysql数据库报错 [mysql] : org.pentaho.di.core.exception.KettleDa
    先说结论。因为kettle/lib文件夹下没有对应版本的ar包,导致链接不上mysql。找到kettle下载文件,地址在 kettle/data-integration/lib 下载两个文件,一个5.1.41-bin文件,另一个对应自己mysql版本的jar包文件,下面举例我安装的8.0.24版本 将下载的两个jar包复制到 kettle/da......
  • 数据传输序列化 Json和protobuf
    1序列化序列化(Serialization)将对象的状态信息转换为可以存储或传输的形式的过程,与之相对应的过程称之为反序列化(Unserialization)。序列化和反序列化主要用于解决在跨平台和跨语言的情况下,模块之间的交互和调用,但其本质是为了解决数据传输问题。 1.1如何实现序列化......
  • 点餐管理系统设计与实现(Java+SpringBoot+MySql)
              目录第1章 绪论1.1选题动因1.2目的和意义1.3论文结构安排第2章 开发环境与技术2.1MySQL数据库2.2 Tomcat介绍2.3 vue技术2.4 SpringBoot框架第3章 系统分析3.1可行性分析3.1.1操作可行性分析3.1.2经济可行性分析......
  • MySQL 主从复制之多线程复制
    目录一、MySQL多线程复制的背景二、MySQL5.5主从复制1、原理2、部署主从复制2.1、主节点安装配置MySQL5.52.2、从节点安装配置MySQL5.53、检查主从库server_id和log_bin配置4、创建主从复制用户5、获取主库的二进制日志文件和位置6、配置从库连接主库参数并启动从库复制......
  • MySQL timestamp 默认值(explicit_defaults_for_timestamp)
    迁移数据库遇timestamp字段setnull。出现报错解决方案mysql>showvariableslike'explicit_defaults_for_timestamp';+---------------------------------+-------+|Variable_name|Value|+---------------------------------+-------+|explici......
  • SAP B1 重要单据字段介绍 - 常规设置(一)
    目录背景一、业务伙伴1.客户活动限制2.佣金设置依据3.审批流程4.默认值设定5.付款条件首选项6.其他设定7.一揽子协议背景在新建账套时往往需要对【常规设置】单据进行个性化定义,维护界面语言和颜色、新业务伙伴的缺省值、预算管理等常规设置。本系列文章将......
  • Navicat远程连接MySQL报错2003(10060)
    背景:学习黑马微服务时,使用Navicat远程连接容器中的MySQL一直报错2003(10060),原因是容器中的MySQL配置的root用户的访问连接权限,只能是本地连接。解决方式:1)查看docker容器是否启动:dockerps-a 未启动的话:(mysql是我的容器名字)dockerstartmysql2)进入mysql容器(注意空格,r......