【Mariadb/Mysql】利用JSON函数巧妙实现行列置换提高系统效率

2023-05-16

在现实生活中,往往我们需要创建一些One to Many或Many to Many的关系数据表。例如:按照Configuration Solution的设计,产品与产品参数表的关系,往往一种产品对应多个参数。

PRODUCT
idnamedecription
1Product1Description of Product1
2Product2Description of Product2
3Product3Description of Product3

PARAMETER
idproduct_idparam_nameparam_value
11startDate2022-03-01
21endDate2023-03-31
32startDate2022-03-01
42endDate2023-03-31
53startDate2022-03-01
63endDate2023-03-31

如上PRODUCT 和 PARAMETER 表的数据显示,每个PRODUCT 都对应两个参数startDate和endDate。 根据这样的数据结构,一般情况下我们会把我们的实体Java Bean 设计成一下情况:每个ProductEntity下都对应一组 productParameterEntityList。

import lombok.Builder;
import lombok.Data;

import java.util.List;

@Data
@Builder
public class ProductEntity {
    private int id;
    private String name;
    private String description;
    private List<ProductParameterEntity> productParameterEntityList;
}

package test;

import lombok.Builder;
import lombok.Data;

@Builder
@Data
public class ProductParameterEntity {
    private int id;
    private int productId;
    private String paraName;
    private String paraValue;
}

根据以上的Enity设计,每次我们需要获取产品参数时,

  • 系统的前端:需要遍历检索productParameterEntityList,然后根据需要paramName来获取相依的paraValue.
  • 系统后端:系统需要根据产品返回的记录数,分别调用ProductParameter的查询来封装productParameterEntityList. 

很明显,这种Entity设计有一个弊端,就是在数据量比较少记录的情况下,系统一切正常。但是如果数据量比较大,比如几万,几十万,甚至几百万时,在没有使用任何额外缓存时,每次我们想要获取产品的资料,系统都需要花费大量的时间来获取产品参数,然后封装成所需要的列表productParameterEntityList。这样既增加了系统查询时间,也增加了系统后端与数据库的读取次数。

那有没有在不使用任何缓存时,解决这种问题的方法呢?

答案是有的。撇除使用外挂的缓存,本人至少可以提供两种解决方案。

  • 第一种:现有的数据库设计不变,改动后端系统Entity的机构来达到目的
  • 第二种:在这里,先卖个关子,详情下回分解。。。

先说第一种:解决思路就是希望能够通过一次数据查询,封装我们需要的信息,一次提出所有的产品信息。

一般情况下,我们的产品都会有相应固定的配置。这样的我们就能够把ProductEntity设计成相应固定的结构,然后封装所有的产品信息,就是把产品参数当作ProductEntity的属性。如下:

import lombok.Builder;
import lombok.Data;


@Data
@Builder
public class ProductEntity {
    private int id;
    private String name;
    private String description;
    private String startDate;
    private String endDate
}

更改过后的ProductEntity相对前端来说,

  • 有了固定的接口
  • 快速访问每个产品的属性,省了对产品参数的遍历

接下来我们需要对后端查询进行改进。

想要获取产品参数,我们少不了把PRODUCT和PARAMETER两个表链接起来。我们先来看看如果把他们连在一起会发生什么:

select * from PRODUCT pd, PARAMETER param where pd.id =param.product_id;

+----+----------+-------------------------+----+------------+---------------+-------------+
| id | name     | description             | id | product_id | param_name    | param_value |
+----+----------+-------------------------+----+------------+---------------+-------------+
|  1 | Product1 | Description of Product1 |  1 |          1 | startDate     | 2022-03-01  |
|  1 | Product1 | Description of Product1 |  2 |          1 | endDate       | 2023-03-31  |
|  2 | Product2 | Description of Product2 |  3 |          2 | startDate     | 2022-03-01  |
|  2 | Product2 | Description of Product2 |  4 |          2 | endDate       | 2023-03-31  |
|  3 | Product2 | Description of Product2 |  5 |          3 | startDate     | 2022-03-01  |
|  3 | Product2 | Description of Product2 |  6 |          3 | endDate       | 2023-03-31  |
|  1 | Product1 | Description of Product1 |  7 |          1 | Company       | DEMO        |
|  1 | Product1 | Description of Product1 |  8 |          1 | Business Unit | IT          |
+----+----------+-------------------------+----+------------+---------------+-------------+

我们发现,每个product都返回多个记录,而已startDate, endDate的name, value是分开存取。这样的返回记录,并不能够直接封装成改进后的ProductEntity.

 有大神们可能会出主意,通过子查询,我们能够获得我们想要的, 例如:

select pd.*, 
(select param_value from PARAMETER where product_id = pd.id and param_name='startDate') as startDate, 
(select param_value from PARAMETER where product_id = pd.id and param_name='endDate') as endDate 
from PRODUCT pd

 

+----+----------+-------------------------+------------+------------+
| id | name     | description             | startDate  | endDate    |
+----+----------+-------------------------+------------+------------+
|  1 | Product1 | Description of Product1 | 2022-03-01 | 2023-03-31 |
|  2 | Product2 | Description of Product2 | 2022-03-01 | 2023-03-31 |
|  3 | Product2 | Description of Product2 | 2022-03-01 | 2023-03-31 |
+----+----------+-------------------------+------------+------------+

 以上子查询确实是一种解决办法,而且其返回结果可以映射成我们想要的ProductEntity。但是这种子查询方式,对数据库其实是额外的开销。视参数个数而定,如果参数几十个,我们估计要定义几十个子查询,如此一来,每次数据库获取param_value,数据库都需要额外遍历一下PARAMETER table. 对数据确确实实是不少的开销。

下面我给大家介绍另一种更快捷的方法。

首先我们把PRODUCT都Group起来,然后利用Mysql/Mariadb的group_concat函数,这样我们得到一下的数据:

select pd.*, group_concat(param.param_name, ':',param.param_value) as params 
from PRODUCT pd, PARAMETER param 
where param.product_id=pd.id group by pd.id, pd.name, pd.description;

+----+----------+-------------------------+-----------------------------------------+
| id | name     | description             | params                                  |
+----+----------+-------------------------+-----------------------------------------+
|  1 | Product1 | Description of Product1 | startDate:2022-03-01,endDate:2023-03-31 |
|  2 | Product2 | Description of Product2 | startDate:2022-03-01,endDate:2023-03-31 |
|  3 | Product2 | Description of Product2 | startDate:2022-03-01,endDate:2023-03-31 |
+----+----------+-------------------------+-----------------------------------------+

通过观察,我们发现group_concat帮我们把所有的parameter提取出来,而且存放在一个params的列下。这时候,如果我们对product1加入额外的parameter,我们也不需要更改我们的SQL;如下:

加入新的parameter后:

select * from PARAMETER;

+----+------------+---------------+-------------+
| id | product_id | param_name    | param_value |
+----+------------+---------------+-------------+
|  1 |          1 | startDate     | 2022-03-01  |
|  2 |          1 | endDate       | 2023-03-31  |
|  3 |          2 | startDate     | 2022-03-01  |
|  4 |          2 | endDate       | 2023-03-31  |
|  5 |          3 | startDate     | 2022-03-01  |
|  6 |          3 | endDate       | 2023-03-31  |
|  7 |          1 | Company       | DEMO        |
|  8 |          1 | Business Unit | IT          |
+----+------------+---------------+-------------+

 在我们的SQL基础上查询:我们看到Company和Business Unit已经自动显示出来。

select pd.*, group_concat(param.param_name, ':',param.param_value) as params 
from PRODUCT pd, PARAMETER param 
where param.product_id=pd.id group by pd.id, pd.name, pd.description;
+----+----------+-------------------------+-----------------------------------------------------------------------+
| id | name     | description             | params                                                                |
+----+----------+-------------------------+-----------------------------------------------------------------------+
|  1 | Product1 | Description of Product1 | startDate:2022-03-01,endDate:2023-03-31,Company:DEMO,Business Unit:IT |
|  2 | Product2 | Description of Product2 | startDate:2022-03-01,endDate:2023-03-31                               |
|  3 | Product2 | Description of Product2 | startDate:2022-03-01,endDate:2023-03-31                               |
+----+----------+-------------------------+-----------------------------------------------------------------------+

但是,仔细观察以上结果,我们发现params列其实就是一个string,它还不能直接Map到我们想要的Java Fields。进一步优化:首先我们需要吧params string转换成JSON

select pd.*, concat('{', group_concat( JSON_QUOTE(param.param_name), 
':',JSON_QUOTE(param.param_value)),'}') as params_json
from PRODUCT pd, PARAMETER param where param.product_id=pd.id group by pd.id, pd.name, pd.description;

注意:如果我们的参数param_value只是basic type,比如text, 我们可以使用JSON_OBJECTAGG函数一次过生成我们的param_json 

select pd.*, JSON_OBJECTAGG(param.param_name,param.param_value) as params_json
from PRODUCT pd, PARAMETER param where param.product_id=pd.id group by pd.id, pd.name, pd.description;

两种查询都能够获得一下结果: 



+----+----------+-------------------------+-----------------------------------------------------------------------------------------+
| id | name     | description             | params_json                                                                             |
+----+----------+-------------------------+-----------------------------------------------------------------------------------------+
|  1 | Product1 | Description of Product1 | {"startDate":"2022-03-01","endDate":"2023-03-31","Company":"DEMO","Business Unit":"IT"} |
|  2 | Product2 | Description of Product2 | {"startDate":"2022-03-01","endDate":"2023-03-31"}                                       |
|  3 | Product2 | Description of Product2 | {"startDate":"2022-03-01","endDate":"2023-03-31"}                                       |
+----+----------+-------------------------+-----------------------------------------------------------------------------------------+

通过函数“concat('{', group_concat( JSON_QUOTE(param.param_name), ':',JSON_QUOTE(param.param_value)),'}')” 我们成功变成一个JSON stirng

接下来我们需要拆分我们的JSON,使其properties变成我们想要的列:

我们可以使用JSON_TABLE 函数或JSON_VALUE来获得我们想要的结果:

JSON_TABLE 的方法:

select result.id, result.name, result.description, jtable.* from (
select pd.*, concat('{', group_concat( JSON_QUOTE(param.param_name), ':',JSON_QUOTE(param.param_value)),'}') as params_json
from PRODUCT pd, PARAMETER param where param.product_id=pd.id group by pd.id, pd.name, pd.description
) as result, json_table(result.params_json, '$' columns(
startDate varchar(255) path '$.startDate',
  endDate varchar(255) path '$.endDate',
  Company varchar(255) path '$.Company',
  BusinessUnit varchar(255) path '$.Business Unit'

)) as jtable

JSON_VALUE 的方法:

JSON_VALUE,JSON_QUERY,JSON_EXTRACT 各自的区别请访问各个数据库的文档。

select result.id, result.name, result.description, 
json_value(result.params_json, '$.startDate') as startDate,
json_value(result.params_json, '$.endDate') as endDate,
json_value(result.params_json, '$.Company') as Company,
json_value(result.params_json, '$."Business Unit"') as BusinessUnit
from (
select pd.*, concat('{', group_concat( JSON_QUOTE(param.param_name), ':',JSON_QUOTE(param.param_value)),'}') as params_json
from PRODUCT pd, PARAMETER param where param.product_id=pd.id group by pd.id, pd.name, pd.description
) as result

以上JSON_TABLE,JSON_VALUE SQL我们都可以获得一下的结果:

+----+----------+-------------------------+------------+------------+---------+--------------+
| id | name     | description             | startDate  | endDate    | Company | BusinessUnit |
+----+----------+-------------------------+------------+------------+---------+--------------+
|  1 | Product1 | Description of Product1 | 2022-03-01 | 2023-03-31 | DEMO    | IT           |
|  2 | Product2 | Description of Product2 | 2022-03-01 | 2023-03-31 | NULL    | NULL         |
|  3 | Product2 | Description of Product2 | 2022-03-01 | 2023-03-31 | NULL    | NULL         |
+----+----------+-------------------------+------------+------------+---------+--------------+

至此,我们可以采用java的Mybatis,Hibernate Native SQL获得相应的JAVA Object, 而且我们只需要查询一遍就能获得我们想要的结果。对于几十万,甚至几百万的数据,我们可以轻松应付。

下回我给大家介绍另一种更快速解决以上问题的方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Mariadb/Mysql】利用JSON函数巧妙实现行列置换提高系统效率 的相关文章

  • 一步一步定制自己的google map(各个省市的经纬度查询)

    安徽省 合肥 北纬31 52 东经117 17 安徽省 安庆 北纬30 31 东经117 02 安徽省 蚌埠 北纬32 56 东经117 21 安徽省 亳州 北纬33 52 东经115 47 安徽省 巢湖 北纬31 36 东经117 52
  • 网页刷新或者重新加载后滚动条的位置不变

    在开发的过程中我们经常需要重新加载或者刷新某个画面 xff0c 已确保数据显示是最新的 但是如果一丁点改变就刷新画面的话 xff0c 会导致用户体验很差 xff0c 想想看你好不容易把网页拖到最后 xff0c 结果点击某个按钮的时候 xff
  • 番茄工作法(番茄钟时间管理)

    番茄工作法是我一次偶然的时间在网上看到的 xff0c 因为自己性格大大咧咧 丢三落四 xff0c 所以经常容易在时间问题上犯迷糊 很多人都有时间拖沓症 xff0c 就是一件事不到最后阶段不去解决它 比如你有一个星期的时间写的毕业论文 xff
  • jquery中美元符号($)命名冲突

    在Jquery中 xff0c 是JQuery的别名 xff0c 所有使用 的地方也都可以使用JQuery来替换 xff0c 如 39 msg 39 等同于JQuery 39 msg 39 的写法 然而 xff0c 当我们引入多个js库后 x
  • js实现省市联动

    效果图如下 xff1a 思路很简单 xff0c 就是先加载省信息 xff0c 然后当省改变的时候加载市县信息 烦的是数据的录入 xff0c 代码如下 lt DOCTYPE html PUBLIC 34 W3C DTD XHTML 1 0 T
  • 正则在小偷程序中的应用(续)

    获取资源信息 content 61 file get contents 34 http list sososteel com qg list html pg 61 1 amp h 61 34 time 对抓取的信息进行处理 取class为l
  • mysql密码过期问题解决方案

    mysql密码过期问题解决方案 问题再现 xff1a 密码过期 旧电脑许久没有用 xff0c 今天打开发现数据库连接不上了 提示密码过期 xff0c 请修改密码 ERROR 1862 HY000 Your password has expi
  • 安装MITIE的条件

    1 版本 在安装rasa xff08 1 1 8 xff0c 其它版本应该也行 xff09 时候必须安装mitie xff0c 下面是我安装mitie时的环境 xff1a 1 VS2017 装没装忘记了 xff0c 但是电脑中有Micros
  • Linux重启命令 / 查看、重启、禁用网卡命令 / 修改IP / 操作防火墙命令

    重启命令 1 reboot 2 shutdown r now 立刻重启 root用户使用 3 shutdown r 10 过10分钟自动重启 root用户使用 4 shutdown r 20 35 在时间为20 35时候重启 root用户使
  • Centos下rpm离线下载与安装命令

    xff08 1 xff09 安装yum即相关插件 sudo apt get install yum sudo apt get install yum utils sudo yum install yum plugin downloadonl
  • Windows下搭建Vxworks开发环境,VMware虚拟机实现

    这是本人自己整理的笔记 xff0c 参考了两篇文章 xff0c 大同小异 参考的第一篇 xff1a http hi baidu com hezhe1008 blog item 26e5cd53364eb314377abe01 html 参考
  • 使用opencv下的sift进行特征匹配(python)

    1 安装opencv 首先确保安装了opencv包和vs2015 xff0c 命令为 xff1a pip install opencv python 也可以直接下载安装文件 xff0c 测试代码 import cv2 import nump
  • CentOS7.0上用命令安装Chrome浏览器。

    1 配置yum下载源 xff1a 在目录 etc yum repos d 下新建文件 google chrome repo 并且在该文件中添加如下内容 xff1a 1 google chrome 2 name 61 google chrom
  • centos7安装anaconda

    步骤一 xff1a 删除centos7中自带有python2 7 xff08 可选 xff09 xff08 1 xff09 强制删除已安装python及其关联 rpm qa grep python xargs rpm ev allmatch
  • new释放内存理解c++

    释放内存后 xff0c 本身抄指针的内容是不会改变的即指针指向没有变 xff0c 但是它里面保存的地址所对应的内存在系统里标记为未使用的即指向的内存区域已经被系统收回了 xff0c 这块内存随时可能会被分配给其他进程或进程里面的变量使用 x
  • archlinux BIOS+BMR 安装记录

    声明 xff01 xff01 本安装指南采用的是传统的BIOS 43 BMR分区方案 43 GRUB引导 适用于早期的电脑 现在大多电脑基本都是UEFI 43 GPT xff0c 这种方案 安装前准备 制作启动盘 1 xff0c 下载arc
  • MAC Please reinstall Android Studio form screatch

    遇到问题的流程 xff0c 是误操作关闭了Kotlin插件 xff0c 重启的时候提示这个信息 解决办法 xff1a 资源库 Preferences AndroidStudio4 0 下面找到disabled plugins txt文件 删
  • 关于zookeeper启动一闪而过的情况

    问题描述 笔者在虚拟机启动zk cmd无论怎么启动都是闪退的情况 解决方案 在cmd文件加个pause查看问题 如下图所示 64 echo off REM Licensed to the Apache Software Foundation
  • jupyter notebook error: FileNotFoundError: [WinError 2]

    文章目录 问题现象解决方法 问题现象 Traceback most recent call last File 34 D Anaconda3 envs robot env lib site packages tornado web py 3
  • Hp 4200添加为网络打印机的步骤

    物理连接 1 只要连接电源 网线即可 设置打印机IP 1 在打印机控制面板上按下 键 xff0c 进入菜单 xff1b 2 按下 或 找到 CONFIGURE DEVICE xff08 配置设备 xff09 选项 xff0c 按下 键 xf

随机推荐

  • Qt内置浏览器引擎WebEngine调试和分析方法

    问题背景 H5和JS代码写好了 xff0c 在浏览器里调试也一切OK xff0c 然后嵌入到Qt中 xff0c 发现各种问题 xff0c 这时候 xff0c 如何在Qt框架下调试Web的代码呢 xff1f 调试方法 在Qt帮助文档中 xff
  • IOS开发之——网络-视频播放-JSON解析(11)

    一 概述 解析JSON结果到OC类通过视频播放地址构成视频播放器并播放显示网络请求 xff0c 返回视频播放列表 xff0c 点击列表中的一项进行播放 二 Main storyboard 三 解析JSON结果到OC类 3 1 JSON数据
  • 华为交换机 ssh和Telnet远程登录配置命令

    华为交换机 ssh和Telnet远程登录配置命令 联系方式 QQ481715271 简单拓扑 LSW4 vlan 20配置地址的原因是SSH Telnet 都可以登录 LSW3 配置 vlan batch 10 20 interface V
  • CentOS7配置yum本地源时报错Repository ‘centos7-media‘: Error parsing config

    错误如下 xff1a 已加载插件 xff1a fastestmirror Repository 39 centos7 media 39 Error parsing config Error parsing 34 gpgkey 61 39 f
  • 深入理解Tomcat虚拟目录

    我们知道 xff0c Web网站中的内容 xff08 包括网页 xff0c 图片 xff0c 音频文件等 xff09 一般都存放在App的目录下 但随着网站内容的不断丰富 xff0c 用户需要把不同层次的内容组织成网站的子目录 我们通常的做
  • Spring Security 实现身份认证

    Spring Security可以运行在不同的身份认证环境中 xff0c 当我们推荐用户使用Spring Security进行身份认证但并不推荐集成到容器管理的身份认证中时 xff0c 但当你集成到自己的身份认证系统时 xff0c 它依然是
  • Spring Security身份认证之UserDetailsService

    zhiqian我们采用了配置文件的方式从数据库中读取用户进行登录 虽然该方式的灵活性相较于静态账号密码的方式灵活了许多 xff0c 但是将数据库的结构暴露在明显的位置上 xff0c 绝对不是一个明智的做法 本文通过Java代码实现UserD
  • 基于Apache OLTU的OAuth2.0授权解决方案

    Apache OLTU实现了OAuth 2 0的规范 xff0c 是一种可靠的Java授权解决方案 但是 xff0c 官方文档实在是太惨不忍睹了 本文参考了开涛的 OAuth 2 0集成Shiro文章 模拟了OAuth2 0的认证流程 技术
  • Couch的MapReduce查询

    1 MapReduce介绍 传统的关系型数据库中 xff0c 只要你的数据是结构化的 xff0c 你可以进行任何类型的查询 Apache Couch与此相反 xff0c 它使用MapReduce xff08 预定义的map和的reduce方
  • Java遍历读取文件目录结构

    Java读取计算机目录 xff0c 并打印 public class ReadDirectory 文件所在的层数 private int fileLevel 生成输出格式 64 param name 输出的文件名或目录名 64 param
  • Java实现数字水印

    数字水印有可见不可见之分 xff0c 可见的比如课件上印有学校校徽 xff0c 微博发图片会水印上上传者的信息及微博logo等 用java实现可见的数字水印 xff0c 草人主要是用到了java awt包中的AlphaComposite类
  • 程序员应该如何去设计需求

    刚出道的程序员 xff0c 在做需求分析的时候 xff0c 总是经常挨批 xff0c 客户说他们不能按照客户的要求去设计原型 xff0c 领导说他们不用心去与客户沟通交流 程序员总是感到自己很冤枉 xff0c 明明客户没有给出一点建设性建议
  • Android硬件开发之——使用Android Beam传输文本

    前言 本文主要讲述使用Android Beam传输文本 xff0c 内容包含 xff1a Android Beam的基本理念Android Beam API实例 Android Beam Android Beam的基本理念 Android
  • 小小程序员的一周日报

    工作依旧在有条不紊的进行着 xff0c 一周的时间很快就会过去 xff0c 正如今天李哥所说的 xff0c 这一周还没有感觉怎么过呢 xff0c 就结束了 是啊 xff0c 这就是我们的工作 xff0c 程序员的工作 xff0c 软件设计师
  • 项目空间都有啥

    项目空间是什么 xff0c Workplace 答案是 xff1a No 项目空间是由项目负责人提出的实施某项目方案的一种流程 项目空间是XX海油ERP管理系统下的一个业务 xff0c 项目负责人通过创建项目名称 项目负责人 使用资源 所属
  • 你不要瞧不起Ctrl+C

    曾经 xff0c 在我未参加工作之前 xff0c 我认为靠 Ctrl 43 C 来完成工作的人 xff0c 肯定是懒惰的程序员 xff0c 但是现在我发现我错了 xff0c 而且是彻底的错了 能够通过 Ctrl 43 C 来完成工作的人 x
  • 文档交接说明书(模板)

    因为同事的离职 xff0c 我的入职 xff0c 要从同事手中交接过来一些项目 公司里只有一些开发文档相关的模板 xff0c 并没有文档交接相关的模板 xff0c 所以交接文档的模板也就由我们自己来定 我结合自己在工作中的经验 xff0c
  • Java如何打印输出九九乘法表

    Java如何打印输出九九乘法表 打印乘法表的方法 1 使用双重for循环打印九九乘法表 2 使用do while 实现打印九九乘法表 双重for循环的使用 打印结果如图示 xff1a Java程序源代码如下 xff1a span class
  • 服务器出现大量 TIME_WAIT,如何解决

    经常在服务器发现一些连接出现 TIME WAIT 状态 xff0c 那么为什么会有 TIME WAIT状态 xff0c 它是如何产生的 xff1f 大量的 TIME WAIT 有什么危害 xff1f 如何排查 xff1f 如何优化 xff1
  • 【Mariadb/Mysql】利用JSON函数巧妙实现行列置换提高系统效率

    在现实生活中 xff0c 往往我们需要创建一些One to Many或Many to Many的关系数据表 例如 xff1a 按照Configuration Solution的设计 xff0c 产品与产品参数表的关系 xff0c 往往一种产