# Sql语句过长报错查询慢优化方案探索
Posted 爱码代码的喵
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了# Sql语句过长报错查询慢优化方案探索相关的知识,希望对你有一定的参考价值。
Sql 过长查询报错、in过多、查询慢优化方案探索
目录
文章目录
背景描述
-
当不能直接进行
sql
联表查询的时候,一些中间数据需要写到表中进行查询从而解决一些无法解决的问题 -
Sql
中通过sql in or in
的方式拼接sql
会导致sql
长度过长,Sql
执行报错。 -
使用
in
查询效率较慢
select * from test where id in('1001','1002') or id in('1001')
in
的内容过长后,sql
超过数据库对sql
长度的设定后会报错
解决方案
方案1(内存中过滤)
in
中的内容超过一定的阈值后,超过阈值数据的字段在内存中进行过滤,先查出其它条件符合的数据(sql
不做分页查询 ),加载到内存中,最后进行内存分页
/**
* 内存分页
*
* @param list 数据
* @param pageNum 当前页
* @param pageSize 分页条数
* @return List
*/
public <T> List<T> pageOperateInMemory(List<T> list, Integer pageNum, Integer pageSize)
if (CollectionUtils.isEmpty(list))
return Collections.emptyList();
// 根据当前页和分页条数 从总记录数中取数据
return list.stream().skip((long) pageSize * (pageNum - 1)).limit(pageSize)
.collect(Collectors.toList());
存在问题
- 主表数据过大的时候,查询全部数据过慢
方案2 In 查询的内容写入固化表
- 创建一张固化表保存
In
里面的数据,每次查询的时候先写表,再用这张表做关联查询
# 方式 1 子查询
select * from master_table x where col_one in(select code from table_temp y where x.col_one=y.code)
select * from test x where exists (select id from test y where y.id=x.id )
# 方式 2 inner join
select x.* from master_table x inner join table_temp y on x.col_one = y.code
- 一次查询完成后用
id
删除数据
固化表格式如下
table_temp
标识 | 名称 | 含义 |
---|---|---|
id | id | 一次查询生成的 uuid |
code | 值 | in 中的值 |
type | 类型 | 类型 |
request_id | 查询id | 查询id |
写多张固化表思路
- 每次查询生成一个查询
id
- 创建多张固化表,每次查询随机
hash
到随机表进行写表 - 使用多线程写表(存在问题见存在的问题描述)
存在问题
- 本地测试
15
万数据写入空表5
秒左右,当表中数据越来越多的时候,写入速度变慢 - 使用多项成写表的时候,数据量比较大的时候,写表速度不是特别理想,在并发情况下数据库处理大量
sql
不是很理想、并且在写完表后还有后续查询逻辑,sql
查询不是特别理想 - 此种方法不推荐
方案2 改进使用 left join
- 写固化表在使用
join
方式比in
子查询的方式快 - 使用
in
子查询在遇到分页的问题的时候会比较慢,推荐优先使用join
方式
方案3(精确查询条件)
sql
拼接更加精确的条件,减少in
的查询范围
方案4 使用临时表
- 经过实践发现使用临时表效果较好
- 使用临时表详情:https://blog.csdn.net/qq_37248504/article/details/127349776
方案5 固化写表使用事务特性
- 手动控制事务,写表之后事务不提交,等所有查询语句执行完成之后,回滚事务
- 这种方案比较好,整体速度快
查询执行过程性能分析
table_temp
共有15
万
写表性能分析
写入固化表
单条插入
mybatis insert
方式
private void insertOneByOne(List<TempDO> tempDOList)
StopWatch stopWatch = new StopWatch("task1");
stopWatch.start();
int i = 0;
for (TempDO tempDO : tempDOList)
testMapper.insertSelective(tempDO);
i++;
if (i == 10000)
stopWatch.stop();
logger.info(String.valueOf(stopWatch.getTotalTimeMillis()));
i = 0;
// 10000 条 需要 4分29秒
jdbcTemplate
方式
private void insertOneByOneJdbcExcute(List<TempDO> tempDOList)
String sql = "INSERT INTO table_temp_one (ID,VER,CODE,ORGCODE,NAME,SHORTNAME,VALIDTIME,INVALIDTIME,PARENTCODE,ORDINAL,CREATEUSER,CREATETIME,PARENTS) VALUES\\n" +
"\\t (?,?,?,?,?,?,?,?,?,?,?,?,?);\\n";
List<Object[]> objectList = new ArrayList<>();
for (TempDO tempDO : tempDOList)
Object[] object =
tempDO.getId(), tempDO.getVer(), tempDO.getCode(), tempDO.getOrgcode(), tempDO.getName(), tempDO.getShortname(), tempDO.getValidtime(), tempDO.getInvalidtime(),
tempDO.getParentcode(), tempDO.getOrdinal(), tempDO.getCreateuser(), tempDO.getCreatetime(), tempDO.getParents()
;
objectList.add(object);
StopWatch stopWatch = new StopWatch("task1");
stopWatch.start();
int i = 0;
for (Object[] objects : objectList)
jdbcTemplate.update(sql, objects);
i++;
if (i == 10000)
stopWatch.stop();
logger.info(String.valueOf(stopWatch.getTotalTimeMillis()));
i = 0;
// 10000 条 需要 4分 36秒
批处理
sql 多values
@Insert("<script>INSERT INTO table_temp\\n" +
" (id,request_id,code,type)\\n" +
" values\\n" +
" <foreach collection=\\"list\\" item=\\"item\\" separator=\\",\\">\\n" +
" (#item.id, #item.requestId, #item.code, #item.type)\\n" +
" </foreach></script>")
int insertBatch(TempDO tempDO);
写入 10000 条执行耗时:937
写入 10000 条执行耗时:734
写入 10000 条执行耗时:925
写入 10000 条执行耗时:1241
写入 10000 条执行耗时:959
写入 10000 条执行耗时:999
写入 10000 条执行耗时:701
写入 10000 条执行耗时:352
写入 10000 条执行耗时:582
写入 10000 条执行耗时:390
写入 10000 条执行耗时:527
写入 10000 条执行耗时:388
写入 10000 条执行耗时:357
写入 10000 条执行耗时:491
写入 10000 条执行耗时:300
写入 10000 条执行耗时:23
写入 150000 条执行耗时:9915
------------------------------------
写入 150000 条执行耗时:6206
------------------------------------
写入 150000 条执行耗时:5658
写入 150000 条执行耗时:6674
写入 150000 条执行耗时:7744
手动提交事务
SqlSessionFactory sqlSessionFactory = ApplicationContextRegister.getBean(SqlSessionFactory.class);
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
InsertTestMapper mapper = sqlSession.getMapper(InsertTestMapper.class);
List<List<TempDO>> lists = splitList(list, 10000);
StopWatch stopWatch = new StopWatch("totalTask");
stopWatch.start();
for (List<TempDO> tempDOList : lists)
StopWatch stopWatch1 = new StopWatch("task1");
stopWatch1.start();
mapper.insertBatch(tempDOList);
stopWatch1.stop();
System.out.println("写入 10000 条执行耗时:" + stopWatch1.getTotalTimeMillis());
sqlSession.commit();
stopWatch.stop();
System.out.println("写入 150000 条执行耗时:" + stopWatch.getTotalTimeMillis());
---------------------------------------------------------------
写入 10000 条执行耗时:159
写入 10000 条执行耗时:106
写入 10000 条执行耗时:106
写入 10000 条执行耗时:106
写入 10000 条执行耗时:106
写入 10000 条执行耗时:139
写入 10000 条执行耗时:109
写入 10000 条执行耗时:106
写入 10000 条执行耗时:133
写入 10000 条执行耗时:107
写入 10000 条执行耗时:107
写入 10000 条执行耗时:107
写入 10000 条执行耗时:106
写入 10000 条执行耗时:107
写入 10000 条执行耗时:121
写入 10000 条执行耗时:0
写入 150000 条执行耗时:4734
---------------------------------------------------------------------
写入 10000 条执行耗时:130
写入 10000 条执行耗时:105
写入 10000 条执行耗时:105
写入 10000 条执行耗时:127
写入 10000 条执行耗时:135
写入 10000 条执行耗时:106
写入 10000 条执行耗时:105
写入 10000 条执行耗时:133
写入 10000 条执行耗时:106
写入 10000 条执行耗时:107
写入 10000 条执行耗时:111
写入 10000 条执行耗时:106
写入 10000 条执行耗时:159
写入 10000 条执行耗时:142
写入 10000 条执行耗时:138
写入 10000 条执行耗时:0
写入 150000 条执行耗时:6495
PreparedStatement executeBatch
方式
Connection connection = SpringContextUtils.getBean(JdbcTemplate.class).getDataSource().getConnection();
String sql = "insert into table_temp(id, request_id,code,type) VALUES (?,?,?,?)";
PreparedStatement ps = null;
try
ps = connection.prepareStatement(sql);
// 取消自动提交
connection.setAutoCommit(false);
StopWatch stopWatch = new StopWatch("totalTask");
stopWatch.start();
for (int i = 1; i <= list.size(); i++)
TempDO tempDO = list.get(i - 1);
ps.setObject(1, tempDO.getId());
ps.setObject(2, tempDO.getRequestId());
ps.setObject(3, tempDO.getCode());
ps.setObject(4, tempDO.getType());
ps.addBatch();
if (i % 10000 == 0)
StopWatch stopWatch1 = new StopWatch("task1");
stopWatch1.start();
ps.executeBatch();
ps.clearBatch();
stopWatch1.stop();
System.out.println("写入 10000 条执行耗时:" + stopWatch1.getTotalTimeMillis());
ps.executeBatch();
ps.clearBatch();
connection.commit();//所有语句都执行完毕后才手动提交sql语句
stopWatch.stop();
System.out.println("写入 150000 条执行耗时:" + stopWatch.getTotalTimeMillis());
catch (SQLException e)
e.printStackTrace();
finally
connection.close();
写入 10000 条执行耗时:1696
写入 10000 条执行耗时:1423
写入 10000 条执行耗时:1192
写入 10000 条执行耗时:1273
写入 10000 条执行耗时:1203
写入 10000 条执行耗时:1445
写入 10000 条执行耗时:1226
写入 10000 条执行耗时:1298
写入 10000 条执行耗时:1666
写入 10000 条执行耗时:1251
写入 10000 条执行耗时:1190
写入 10000 条执行耗时:1364
写入 10000 条执行耗时:1263
写入 10000 条执行耗时:1191
写入 10000 条执行耗时:1682
写入 150000 条执行耗时:20753
--------------------------------------------------------------------
写入 10000 条执行耗时:1579
写入 10000 条执行耗时:1859
写入 10000 条执行耗时:1483
写入 10000 条执行耗时:1470
写入 10000 条执行耗时:1488
写入 10000 条执行耗时:1059
写入 10000 条执行耗时:1112
写入 10000 条执行耗时:1164
写入 10000 条执行耗时:1426
写入 10000 条执行耗时:1202
写入 10000 条执行耗时:1315
写入 10000 条执行耗时:1654
写入 10000 条执行耗时:1301
写入 10000 条执行耗时:1134
写入 10000 条执行耗时:1529
写入 150000 条执行耗时:21168
写临时表插入
- 先创建临时表,在执行插入,使用完之后删除
多values方式
写入 10000 条执行耗时:1267
写入 10000 条执行耗时:440
写入 10000 条执行耗时:352
写入 10000 条执行耗时:524
写入 10000 条执行耗时:380
写入 10000 条执行耗时:306
写入 10000 条执行耗时:457
写入 10000 条执行耗时:303
写入 10000 条执行耗时:301
写入 10000 条执行耗时:804
写入 10000 条执行耗时:333
写入 10000 条执行耗时:297
写入 10000 条执行耗时:323
写入 10000 条执行耗时:325
写入 10000 条执行耗时:324
写入 150000 条执行耗时:6746
---------------------------------------------------------------
写入 10000 条执行耗以上是关于# Sql语句过长报错查询慢优化方案探索的主要内容,如果未能解决你的问题,请参考以下文章