mysql联表批处理操作

Wesley13
• 阅读 287

1 概述

mysql中的单表增删改查操作,可以说是基本中的基本.
实际工作中,常常会遇到一些基本用法难以处理的数据操作,譬如遇到主从表甚至多级关联表的情况(如一些历史问题数据的批量处理),考虑到效率问题,需要将请求次数控制在一个常量级别.
这种情况下,由于需要操作的(作为参数的)数据量较大,或者获取参数较为复杂,往往不太容易处理.
以下分三种情况结合常见问题分别说明下:

2 insert批处理操作

在提供了已知参数的情况下,insert的批处理操作一般有两种处理方式(这种情况适用于从前台传来的明细数据的插入操作).

1. java层处理,即jdbcTemplate的batchUpdate.

即提供一个sql模板和一组参数数组的集合,通过框架(如spring)中提供的jdbcTemplate的batchUpdate进行批处理操作.

//sql模板
String strSql="insert into t_am_assets_increase_detail  \n" +
                "(group_code,form_code,assets_code,remarks) \n" +
                "values \n" +
                "(?,?,?,?)";
//组成参数数组的集合
List<Object[]> lstParamArr=new ArrayList<Object[]>();
for (AmAssetsIncreaseDetail mAaid:lstAaid){
    Object[] arrParam={strGroupCode,strFormCode,mAaid.getAssets_code(),mAaid.getRemarks()};
    lstParamArr.add(arrParam);
}
//batchUpdate批处理操作
int[] arrResult=db.batchUpdate(strSql,lstParamArr);

需要说明:

  • 使用此种用法需要加上事务注解,一方面可以避免某一条数据有问题,整体得以回滚;另一方面,加上事务比不加事务的插入速度要快(数据量越大越明显,因为这减少了每条数据插入时打开关闭事务锁的开销).
  • 虽然spring也提供了另一种batchUpdate方法,即java方法的参数不是一个sql模板和一个参数数组的集合,而是sql语句数组,但强烈不建议使用该形式的batchUpdate,原因有二,一是使用该种形式的参数,几乎必然只能参数拼接sql,无法使用参数绑定,安全性上是一个问题;二是不使用sql模板,在插入效率上会很低(具体原因可以参考<高性能mysql>,此处不再细讲).
2.数据层处理.参考内容如下:
-- 以下是数据库层插入数据的一种常见形式
insert table (..) values (?,?),(?,?),..

本质上来说,这种处理相当于是把所有插入操作放在一条sql上,性能上较法一还要高.
如果说有什么不足,那就是当插入数量过多时(如超过1000条明细),sql语句会过长,特别是插入复杂表的情况.

3.特殊情况下的数据层处理:联表插入

此前讲到的两种方法,都适用于通用的批处理操作,即参数显式的情况.也说到,当插入数据数量过多时,则可能会产生速度较慢等不利影响.
如果参数均从外界直接传来,如从页面上或者导入功能传来,与原数据没有任何关联,则也只能这样处理.
但如果插入数据原本就在当前数据库的其他表格中,则可完全不必通过在后台先查询在插入的方式进行处理.

-- 譬如插入数据的源数据为t2,要插入到t1,则可使用如下模式执行:
insert into t1 (code,name,remarks,..)
(select code,name,remarks,.. from t2 where [condition]);

与正常插入sql语句有两个区别:

  • 没有value/values;
  • 也可提供插入自身表的数据,不过要先构建一个伪表(再套一层select * from(..));

2 update联表操作

与insert的第三种方式相似,在一些业务逻辑互相关联的表格间更新数据而非直接从外界(页面/导入)获取原始数据时,update同样可以使用联表操作使批处理更加容易和有效率.
该类操作特别适用于批量处理冗余类数据(如果有新人对冗余设计不太了解,可参考此前我的博客:mysql表结构设计优化建议).

  • 先举一个比较简单的例子:
    譬如使用t_user来记录职员信息,user_code,user_name分别表示该职员的工号,姓名;
    t_salary_standard表示职员的工资标准,两个核心字段user_code和user_salary表示职员工号和当前薪资标准;
    t_salary_change表示职员的薪资变更表.user_code和add_salary分别表示职员工号和增加薪资.此处我们先简单化处理,假设表单审批完成后即立刻变更职员的工资标准;
    则很容易看出来,t_salary_standard算是一种t_salary_change的一种冗余表,当后者变更(以审批为生效时刻)时,前者也要随之变更.
    由于薪资变更表对应的职员数量往往不止一个,所需增加的工资一般也不都相同,所以不使用联表操作的话,就只能先全部查询出来待修改的用户薪资变更数据,再在批次性增加:

    select user_code,add_salary from t_salary_chang where [condition]; -- 查询后再batchUpdate update t_salary_standard set user_salary=user_salary+? where user_code=?

此种情况,使用联表处理就容易很多:

update t_salary_standard tss,t_salary_change tsc
set tss.user_salary=tss.user_salary+tsc.add_salary
where tss.user_code=tsc.user_code
and [other conditions]

由于省略了许多中间步骤,操作速度必然是要快于前一种的方法的.且薪资变更表的数据量越大,这种效率上的提升越明显.

  • 以下是一个比较复杂的例子,逻辑稍微有些复杂,此处不再详细说明,读者可以根据sql大致推测出意义.

    //此处的逻辑是:先将当前资产的管理数据更新到领用明细的历史数据中,再将领用总表的管理数据更新到当前资产的管理数据中; update t_am_assets_use_detail taaud,t_am_assets taa,t_am_assets_use taau set taaud.history_organ_code=taa.using_organ_code,taaud.history_user_code=taa.custodian,taaud.history_store_code=taa.store_code,taaud.history_storage_places=taa.storage_places, taa.using_organ_code=taau.use_organ_code,taa.custodian=taau.use_user_code,taa.store_code=taau.store_code,taa.storage_places=taau.storage_places,taa.logic_state=2 where taaud.group_code=? and taa.group_code=? and taau.group_code=? and taaud.form_code=? and taau.form_code=? and taaud.assets_code=taa.assets_code and taa.logic_state=1;

3.delete联表操作

类似update的联表操作,delete也有相应的联表操作.
当然就本人的开发经验而言,delete联表操作几乎仅用于历史问题数据的处理,几乎不用于日常系统使用的sql中.

  • 此处同样先举一个简单的例子加以说明: t_customer表示客户信息,group_code,cust_code和cust_name表示企业编码,客户编码和客户名称;
    t_sale_form表示销售单据,group_code,form_code,cust_code表示表单编码和表单所属客户编码;
    譬如发现某个客户信息有问题,给t_customer表增加了is_error字段,有问题的客户信息is_error=1,要删除所有有问题客户的销售单据(一般这种删除都是考虑伪删除的,此处为了讲解方便,姑且采用真删除).
    如果不用批处理操作,就只能先查询出来所有客户信息,再根据客户信息删除主单据.

    select group_code,cust_code from t_customer where is_error=1; -- 此后再调用batchUpdate delete from t_sale_form where group_code=? and cust_code=?;

使用联表,就很容易处理了:

delete tsf
from t_sale_form tsf,t_customer tc
where tsf.group_code=tc.group_code
and tsf.cust_code=tc.cust_code
and tc.is_error=1
  • 以下是一个比较复杂的例子,同样不详细说明,仅作格式之参考.

    -- group_code是企业编码,voucher_code是凭证号. -- 此处的逻辑是,删除所有企业具有一定条件(体现在伪表中)的凭证明细 delete tvd from t_voucher_detail tvd,( select * from ( select tv2.voucher_code,tv.group_code from t_voucher tv inner join t_voucher_detail tvd on tvd.group_code=tv.group_code and tvd.voucher_code=tv.voucher_code and tvd.subject_one='5401'
    inner join t_voucher tv2 on tv2.group_code=tv.group_code and tv2.bill_code=tv.bill_code and tv2.is_del=0 inner join t_voucher_detail tvd2 on tvd2.group_code=tv2.group_code and tvd2.voucher_code=tv2.voucher_code and tvd2.subject_one='5401' where tv.cdate>='2018-06-20' and tv2.voucher_id>tv.voucher_id and left(tv.bill_code,4)='XSKD' and tv.is_del=0 )wt ) wtv where tvd.group_code=wtv.group_code and tvd.voucher_code=wtv.voucher_code

以下是将复杂sql格式简单化后的语句.

delete tvd
from t_voucher_detail tvd,(
select * from (...)wt
) wtv 
where tvd.group_code=wtv.group_code
and tvd.voucher_code=wtv.voucher_code

4.使用建议

  1. 以上用法形式,均不止一种,此处拿来的,仅是本人常用的形式,更多形式可以搜索出来;
  2. 联表操作,特别适用于两个及以上不全等字段的信息组操作(譬如update简单例子中user_code和add_salary不全相等,delete简单例子中的group_code和cust_code),因为一个字段的信息组操作可以通过子查询来实现;
  3. 虽说多数情况适用于冗余数据操作,但只要是各个表格之间业务有关联的,均可使用联表操作.
点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
Python3:sqlalchemy对mysql数据库操作,非sql语句
Python3:sqlalchemy对mysql数据库操作,非sql语句python3authorlizmdatetime2018020110:00:00coding:utf8'''
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
Oracle一张表中实现对一个字段不同值和总值的统计(多个count)
需求:统计WAIT\_ORDER表中的工单总数、未处理工单总数、已完成工单总数、未完成工单总数。表结构:为了举例子方便,WAIT\_ORDER表只有两个字段,分别是ID、STATUS,其中STATUS为工单的状态。1表示未处理,2表示已完成,3表示未完成总数。 SQL:  1.SELECT   2
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这