一次sql优化

上周给以前做的模块新增了个报表统计的功能,感觉有点意思,写出来分享下,也方便以后回顾。

其实报表统计一般是要用到插件的,但是我这个是用html拼成的,虽然是low了点,但是要做好坑还是很多的。

本来想详细说一下业务和代码的,给sql都加上注释,但是发现业务这东西说清楚太麻烦了,所以就大概描述一下吧,最重要的还是最后面的一条sql。

用到的表为A01(人员基本信息表)、A11(人员培训信息表)、B00(机构信息表);A01为主表,查近五年的培训信息

首先做完第一个页面大概是长成这样子的

 

一次sql优化_第1张图片

 这种查询以前查过类似的,所以做的比较快

这是拼接html的后台代码

@Action
public String getBmPxxs() {
	HttpServletRequest request = ActionContext.getActionContext().getHttpServletRequest();
	Map map = new HashMap();
	String B00 = request.getParameter("B00");// 查询的部门id
	String year = request.getParameter("year");// 当前年
	map.put("B00", B00);
	map.put("year", year);
	List> bmpxxs_list = this.bdsoftMybatisUtils.selectList("jypx.cxtj.pxxxhz.bmpxxs", map);
	StringBuffer sbtest = new StringBuffer();
	// html表头
	sbtest.append("
"); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); // html数据填充 for (int i = 0; i < bmpxxs_list.size(); i++) { if (Integer.parseInt(bmpxxs_list.get(i).get("合计").toString()) < 300) { sbtest.append(""); } else { sbtest.append(""); } sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); } sbtest.append("
单位姓名培训学时
" + (Integer.parseInt(year) - 4) + "年" + (Integer.parseInt(year) - 3) + "年" + (Integer.parseInt(year) - 2) + "年" + (Integer.parseInt(year) - 1) + "年" + year + "年合计
" + bmpxxs_list.get(i).get("所在部门") + "" + bmpxxs_list.get(i).get("姓名") + "" + bmpxxs_list.get(i).get("J1") + "" + bmpxxs_list.get(i).get("J2") + "" + bmpxxs_list.get(i).get("J3") + "" + bmpxxs_list.get(i).get("J4") + "" + bmpxxs_list.get(i).get("J5") + "" + bmpxxs_list.get(i).get("合计") + "
"); return sbtest.toString(); }

这块主要就是把查出来的list动态拼接成table,下面是查询的sql语句

 这种统计类sql以前写的时候还是想了比较久的,第一次写的时候本来想找组长帮忙的,但是他发烧请假了,然后自己边想边写就写出来了,回头看其实挺简单的,主要就是一个行转列的操作,oracle中有个函数可以直接转,但是我没转成功,就直接用最原始的case when来写了,也比较容易理解吧。然后group by这个东西还是要慎用 (如果在返回集字段中,字段要么就要包含在Group By语句的后面,作为分组的依据;要么就要被包含在聚合函数中),然后就是个递归函数 START WITH...CONNECT BY PRIOR...来进行节点控制。

 下面是导出功能的实现

@Action
public void exportBMhtml() throws Exception {
	HttpServletRequest request = ActionContext.getActionContext().getHttpServletRequest();
	HttpServletResponse response = ActionContext.getActionContext().getHttpServletResponse();
	Map map = new HashMap();
	// 获取制定的excel
	String sourceSrc = System.getProperty("resourceFiles.location") + File.separator + "jypx" + File.separator;
	// 获取模板名称
	String templateName = request.getParameter("templateName");
	// 获取需要导出的文件名称
	String fileName = request.getParameter("fileName");
	// 获取文件类型
	String fileType = request.getParameter("fileType");
	// 获取模板路径
	String excelFilePath = sourceSrc + templateName;
	try {
		fileName = fileName + "." + fileType;
		InputStream in = new FileInputStream(new File(excelFilePath));
		Workbook wb = new Workbook(in);
		Worksheet worksheet = wb.getWorksheets().get(0);
		Cells cells = worksheet.getCells();
		String B00 = request.getParameter("B00");
		String year = request.getParameter("year");
		map.put("B00", B00);
		map.put("year", year);
		List> bmpxxs_datalist = new ArrayList>();
		bmpxxs_datalist = this.bdsoftMybatisUtils.selectList("jypx.cxtj.pxxxhz.bmpxxs", map);

		// excel表头
		cells.get(0, 0).putValue((Integer.parseInt(year) - 4) + "—" + year + "年参训学时汇总表");
		cells.merge(0, 0, 1, 8);// 合并行,列(起始行,起始列,合并行数,合并列数)
		// cells.setColumnWidth(0, 30); //设置宽度
		AddBorder(cells, 0, 0);
		AddBorder(cells, 1, 3);
		AddBorder(cells, 1, 4);
		AddBorder(cells, 1, 5);
		AddBorder(cells, 1, 6);
		AddBorder(cells, 1, 7);
		AddBorders(cells, 0, 0);

		cells.get(1, 0).putValue("单位");
		cells.merge(1, 0, 2, 1);
		cells.setColumnWidth(0, 30); // 设置行宽
		AddBorder(cells, 1, 0);
		AddBorder(cells, 2, 0);
		AddBorders(cells, 1, 0);

		cells.get(1, 1).putValue("姓名");
		cells.merge(1, 1, 2, 1);
		cells.setColumnWidth(1, 20); // 设置行宽
		AddBorder(cells, 1, 1);
		AddBorders(cells, 1, 1);

		cells.get(1, 2).putValue("培训学时");
		cells.merge(1, 2, 1, 6);
		AddBorder(cells, 1, 2);
		AddBorders(cells, 1, 2);

		cells.get(2, 2).putValue((Integer.parseInt(year) - 4) + "年");
		cells.merge(2, 2, 1, 1);
		cells.setColumnWidth(2, 15); // 设置行宽
		AddBorder(cells, 2, 2);
		AddBorders(cells, 2, 2);

		cells.get(2, 3).putValue((Integer.parseInt(year) - 3) + "年");
		cells.merge(2, 3, 1, 1);
		cells.setColumnWidth(3, 15); // 设置行宽
		AddBorder(cells, 2, 3);
		AddBorders(cells, 2, 3);

		cells.get(2, 4).putValue((Integer.parseInt(year) - 2) + "年");
		cells.merge(2, 4, 1, 1);
		cells.setColumnWidth(4, 15); // 设置行宽
		AddBorder(cells, 2, 4);
		AddBorders(cells, 2, 4);

		cells.get(2, 5).putValue((Integer.parseInt(year) - 1) + "年");
		cells.merge(2, 5, 1, 1);
		cells.setColumnWidth(5, 15); // 设置行宽
		AddBorder(cells, 2, 5);
		AddBorders(cells, 2, 5);

		cells.get(2, 6).putValue(year + "年");
		cells.merge(2, 6, 1, 1);
		cells.setColumnWidth(6, 15); // 设置行宽
		AddBorder(cells, 2, 6);
		AddBorders(cells, 2, 6);

		cells.get(2, 7).putValue("合计");
		cells.merge(2, 7, 1, 1);
		cells.setColumnWidth(7, 15); // 设置行宽
		AddBorder(cells, 2, 7);
		AddBorders(cells, 2, 7);

		// 存放表头
		List list_BT = new ArrayList<>();
		list_BT.add("所在部门");
		list_BT.add("姓名");
		list_BT.add("J1");
		list_BT.add("J2");
		list_BT.add("J3");
		list_BT.add("J4");
		list_BT.add("J5");
		list_BT.add("合计");
		// excel填充数据
		for (int i = 0; i < bmpxxs_datalist.size(); i++) {
			for (int j = 0; j < 8; j++) {
				cells.get(i + 3, j).putValue(bmpxxs_datalist.get(i).get(list_BT.get(j)));
				// 标记近5年培训总学时小于300的人员
				if (Integer.parseInt((bmpxxs_datalist.get(i).get("合计")).toString()) < 300) {
					Style style = cells.get(i + 3, j).getStyle();
					style.setPattern(BackgroundType.SOLID);
					style.setForegroundColor(Color.getLightGray());
					cells.get(i + 3, j).setStyle(style);
				}
			}
		}
		// 调整样式
		for (int i = 3; i < bmpxxs_datalist.size() + 3; i++) {
			for (int j = 0; j < 8; j++) {
				AddBorder(cells, i, j);
			}
		}
		response.addHeader("Content-Disposition",
				"attachment;filename=" + new String(java.net.URLEncoder.encode(fileName, "UTF-8")));
		response.addHeader("Content-Type", "application/octet-stream; charset=UTF-8");
		wb.save(response.getOutputStream(), SaveFormat.XLSX);
	} catch (Exception e) {
		throw e;
	}
}

// 添加边框
private void AddBorder(Cells cells, int row, int column) {
	Style style = cells.get(row, column).getStyle();
	style.setBorder(BorderType.TOP_BORDER, 1, Color.getBlack());
	style.setBorder(BorderType.LEFT_BORDER, 1, Color.getBlack());
	style.setBorder(BorderType.RIGHT_BORDER, 1, Color.getBlack());
	style.setBorder(BorderType.BOTTOM_BORDER, 1, Color.getBlack());
	style.setTextWrapped(true); // 单元格内容自动换行
	style.setHorizontalAlignment(TextAlignmentType.CENTER);// 文字居中
	style.setVerticalAlignment(TextAlignmentType.CENTER);
	style.setName("仿宋_GB2312");
	style.getFont().setSize(14);
	cells.get(row, column).setStyle(style);
}

上面这些都没啥好说的,就是一些和业务相关的逻辑,比较简单。

然后重点是第二个页面

页面是长这样的

一次sql优化_第2张图片

开始看到这个页面我是自闭的,有三个难点,第一是动态合并单元格,第二个是动态拼接合计行,第三就是要查出人员没有年份的培训记录,就是说16年即使这个人没有培训记录但是要查出来;

第一个难点和第二个难点我想的是把每个人都查一遍放到一个list里面再拼接,而不是一次查所有人,然后再在java里面做些逻辑处理就行了,然后第三点问了我旁边的sql大神,只要建张年份临时表,然后再cross join (笛卡尔积) A01表,再left jion A11表,最后再加些限制条件就OK了,反正他刚说的时候我也是很懵,我还以为他没听懂我要干嘛,没想到写出来的结果确实是我想要的,然后就开始动手写了,这种循环查每个人的培训信息的思路sql会很简单,但是java逻辑比较复杂。下面是拼成html的java代码

public String getRyPxxs2() {
	HttpServletRequest request = ActionContext.getActionContext().getHttpServletRequest();
	Map map = new HashMap();
	String B00 = request.getParameter("B00");// 查询的部门id
	String year = request.getParameter("year");// 当前年
	map.put("B00", B00);
	map.put("year", year);
	StringBuffer sbtest = new StringBuffer();
	// html表头
	sbtest.append("
"); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); // 存放人员A00 List> A00_list = this.bdsoftMybatisUtils.selectList("jypx.cxtj.pxxxhz.queryA00", map); Map map1 = new HashMap(); // html填充数据 for (int i = 0; i < A00_list.size(); i++) { map1.put("A00", A00_list.get(i).get("A00")); map1.put("year", year); // 存放人员培训信息 List> rypxxx_list = this.bdsoftMybatisUtils.selectList("jypx.cxtj.pxxxhz.queryrypxxx",map1); // 统计天数 int day = 0; for (int j = 0; j < rypxxx_list.size(); j++) { day += Integer.parseInt(rypxxx_list.get(j).get("天数").toString()); // 取到第一条数据时,合并姓名单元格 if (j == 0) { sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); } else { sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); } } // 个人培训信息遍历完后添加合计行 sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); // 清空list rypxxx_list.clear(); } sbtest.append("
姓名年份培训班开始日期结束日期天数学时
" + rypxxx_list.get(0).get("姓名") + "" + rypxxx_list.get(j).get("年份") + "" + (rypxxx_list.get(j).get("培训班") == null ? "-" : rypxxx_list.get(j).get("培训班")) + "" + (rypxxx_list.get(j).get("开始日期") == null ? "-" : rypxxx_list.get(j).get("开始日期")) + "" + (rypxxx_list.get(j).get("结束日期") == null ? "-" : rypxxx_list.get(j).get("结束日期")) + "" + rypxxx_list.get(j).get("天数") + "" + rypxxx_list.get(j).get("学时") + "
" + rypxxx_list.get(j).get("年份") + "" + (rypxxx_list.get(j).get("培训班") == null ? "-" : rypxxx_list.get(j).get("培训班")) + "" + (rypxxx_list.get(j).get("开始日期") == null ? "-" : rypxxx_list.get(j).get("开始日期")) + "" + (rypxxx_list.get(j).get("结束日期") == null ? "-" : rypxxx_list.get(j).get("结束日期")) + "" + rypxxx_list.get(j).get("天数") + "" + rypxxx_list.get(j).get("学时") + "
" + (Integer.parseInt(year) - 4) + "—" + year + "年合计" + "" + day + "" + (day * 8) + "
"); return sbtest.toString(); }

下面是导出功能的后台代码

@Action
public void exportRYhtml() throws Exception {
	HttpServletRequest request = ActionContext.getActionContext().getHttpServletRequest();
	HttpServletResponse response = ActionContext.getActionContext().getHttpServletResponse();
	Map map = new HashMap();
	// 获取制定的excel
	String sourceSrc = System.getProperty("resourceFiles.location") + File.separator + "jypx" + File.separator;
	// 获取模板名称
	String templateName = request.getParameter("templateName");
	// 获取需要导出的文件名称
	String fileName = request.getParameter("fileName");
	// 获取文件类型
	String fileType = request.getParameter("fileType");
	// 获取模板路径
	String excelFilePath = sourceSrc + templateName;
	try {
		fileName = fileName + "." + fileType;
		InputStream in = new FileInputStream(new File(excelFilePath));
		Workbook wb = new Workbook(in);
		Worksheet worksheet = wb.getWorksheets().get(0);
		Cells cells = worksheet.getCells();
		String B00 = request.getParameter("B00");
		String year = request.getParameter("year");
		map.put("B00", B00);
		map.put("year", year);
		List> A00_list = new ArrayList>();
		A00_list = this.bdsoftMybatisUtils.selectList("jypx.cxtj.pxxxhz.queryA00", map);
		Map map1 = new HashMap();
		// 存放表头
		List list_BT = new ArrayList<>();
		list_BT.add("姓名");
		list_BT.add("年份");
		list_BT.add("培训班");
		list_BT.add("开始日期");
		list_BT.add("结束日期");
		list_BT.add("天数");
		list_BT.add("学时");
		// excel表头
		cells.get(0, 0).putValue((Integer.parseInt(year) - 4) + "—" + year + "年人员参训情况汇总表");
		cells.merge(0, 0, 1, 7);// 合并行,列(起始行,起始列,合并行数,合并列数)
		cells.get(1, 0).putValue(list_BT.get(0));
		cells.setColumnWidth(0, 15); // 设置行宽
		cells.get(1, 1).putValue(list_BT.get(1));
		cells.setColumnWidth(1, 22); // 设置行宽
		cells.get(1, 2).putValue(list_BT.get(2));
		cells.setColumnWidth(2, 45); // 设置行宽
		cells.get(1, 3).putValue(list_BT.get(3));
		cells.setColumnWidth(3, 18); // 设置行宽
		cells.get(1, 4).putValue(list_BT.get(4));
		cells.setColumnWidth(4, 18); // 设置行宽
		cells.get(1, 5).putValue(list_BT.get(5));
		cells.setColumnWidth(5, 13); // 设置行宽
		cells.get(1, 6).putValue(list_BT.get(6));
		cells.setColumnWidth(6, 13); // 设置行宽
		// 填充数据起始行数
		int startCol = 2;
		// 填充数据
		for (int i = 0; i < A00_list.size(); i++) {
			map1.put("A00", A00_list.get(i).get("A00"));
			map1.put("year", year);
			// 存放人员培训信息
			List> rypxxx_list = this.bdsoftMybatisUtils.selectList("jypx.cxtj.pxxxhz.queryrypxxx", map1);
			// 统计天数
			int day = 0;
			for (int j = 0; j < rypxxx_list.size(); j++) {
				day += Integer.parseInt(rypxxx_list.get(j).get("天数").toString());
				for (int k = 0; k < 7; k++) {
					// 当循环到个人的第一条培训信息
					if (j == 0 && k == 0) {
						cells.get(startCol, k).putValue(rypxxx_list.get(j).get(list_BT.get(k)));
						// 合并单元格
						cells.merge(startCol, 0, rypxxx_list.size() + 1, 1);// 合并行,列(起始行,起始列,合并行数,合并列数)
					} else if (k != 0) {
						cells.get(startCol, k).putValue(rypxxx_list.get(j).get(list_BT.get(k)));
					}
				}
				startCol += 1;
			}
			cells.get(startCol, 1).putValue((Integer.parseInt(year) - 4) + "—" + year + "年合计");
			cells.get(startCol, 5).putValue(day);
			cells.get(startCol, 6).putValue(day * 8);
			startCol += 1;
		}
		// 调整样式
		for (int i = 0; i < startCol; i++) {
			for (int j = 0; j < 7; j++) {
				AddBorder(cells, i, j);
				AddBorders(cells, i, j);
			}
		}
		response.addHeader("Content-Disposition",
				"attachment;filename=" + new String(java.net.URLEncoder.encode(fileName, "UTF-8")));
		response.addHeader("Content-Type", "application/octet-stream; charset=UTF-8");
		wb.save(response.getOutputStream(), SaveFormat.XLSX);
	} catch (Exception e) {
		throw e;
	}
}

 这个是sql





写完运行测试,然后问题就来了,点开这个页面的时候发现啥都没有,看了下前端和后台都没报错,然后我就纳闷了,怎么没数据呢,好歹报个错啊,惆怅了十几秒后数据出来了,原来是因为加载太慢了,可以看到上面的代码查询的时候用了三层for循环,从数据库里查了几千次,运行完需要十几秒的时间。这样肯定是不行的,不然客户打开还以为死机了,所以我想加个类似这种提示框,但是加上去之后这个框怎么也去不掉(恶心的ext.js)。没办法了,只能硬着头皮去重构优化sql让它查快一点,想让它查快就不能查几千次,只能一次性把数据查出来,但是这样的话,之前提到的第一个难点和第二个难点就很难处理了。

一次sql优化_第3张图片

 虽然不好写,但是经过大神的指点后还是能看到希望的。查询结果为单个list的话,得知道什么时候开始合并单元格,以及要合并多少行的问题只能在sql里面解决,所以写起来就很复杂了,以下是优化后的java代码和sql

// 加载人员培训信息html
@Action
public String getRyPxxs() {
	HttpServletRequest request = ActionContext.getActionContext().getHttpServletRequest();
	Map map = new HashMap();
	String B00 = request.getParameter("B00");// 查询的部门id
	String year = request.getParameter("year");// 当前年
	int year_4 = Integer.parseInt(year) - 4;
	map.put("B00", B00);
	map.put("year", year);
	map.put("heji", year_4 + "年-" + year + "合计");
	StringBuffer sbtest = new StringBuffer();
	// html表头
	sbtest.append("
"); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); // 存放人员培训信息 List> pxxx_list = this.bdsoftMybatisUtils.selectList("jypx.cxtj.pxxxhz.queryRyInfoForPxxx", map); // html填充数据 for (int i = 0; i < pxxx_list.size(); i++) { String rn = "0"; if (pxxx_list.get(i).get("RN") != null) { rn = pxxx_list.get(i).get("RN").toString(); } // 跨行数 int rowspan = Integer.parseInt(pxxx_list.get(i).get("记录条数").toString()) + 1; // 取到第一条数据时,合并姓名单元格 if ("1".equals(rn)) { sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); } else { sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); sbtest.append(""); } } sbtest.append("
姓名年份培训班开始日期结束日期天数学时
" + pxxx_list.get(i).get("姓名") + "" + pxxx_list.get(i).get("年份") + "" + (pxxx_list.get(i).get("培训班") == null ? "-" : pxxx_list.get(i).get("培训班")) + "" + (pxxx_list.get(i).get("开始日期") == null ? "-" : pxxx_list.get(i).get("开始日期")) + "" + (pxxx_list.get(i).get("结束日期") == null ? "-" : pxxx_list.get(i).get("结束日期")) + "" + pxxx_list.get(i).get("天数") + "" + pxxx_list.get(i).get("学时") + "
" + pxxx_list.get(i).get("年份") + "" + (pxxx_list.get(i).get("培训班") == null ? "-" : pxxx_list.get(i).get("培训班")) + "" + (pxxx_list.get(i).get("开始日期") == null ? "-" : pxxx_list.get(i).get("开始日期")) + "" + (pxxx_list.get(i).get("结束日期") == null ? "-" : pxxx_list.get(i).get("结束日期")) + "" + pxxx_list.get(i).get("天数") + "" + pxxx_list.get(i).get("学时") + "
"); return sbtest.toString(); } // 培训信息导出 @Action public void exportRYhtml() throws Exception { HttpServletRequest request = ActionContext.getActionContext().getHttpServletRequest(); HttpServletResponse response = ActionContext.getActionContext().getHttpServletResponse(); Map map = new HashMap(); // 获取制定的excel String sourceSrc = System.getProperty("resourceFiles.location") + File.separator + "jypx" + File.separator; // 获取模板名称 String templateName = request.getParameter("templateName"); // 获取需要导出的文件名称 String fileName = request.getParameter("fileName"); // 获取文件类型 String fileType = request.getParameter("fileType"); // 获取模板路径 String excelFilePath = sourceSrc + templateName; try { fileName = fileName + "." + fileType; InputStream in = new FileInputStream(new File(excelFilePath)); Workbook wb = new Workbook(in); Worksheet worksheet = wb.getWorksheets().get(0); Cells cells = worksheet.getCells(); String B00 = request.getParameter("B00"); String year = request.getParameter("year"); int year_4 = Integer.parseInt(year) - 4; map.put("B00", B00); map.put("year", year); map.put("heji", year_4 + "年-" + year + "合计"); List> pxxx_list = this.bdsoftMybatisUtils.selectList("jypx.cxtj.pxxxhz.queryRyInfoForPxxx", map); // 存放表头 List list_BT = new ArrayList<>(); list_BT.add("姓名"); list_BT.add("年份"); list_BT.add("培训班"); list_BT.add("开始日期"); list_BT.add("结束日期"); list_BT.add("天数"); list_BT.add("学时"); // excel表头 cells.get(0, 0).putValue((Integer.parseInt(year) - 4) + "—" + year + "年人员参训情况汇总表"); cells.merge(0, 0, 1, 7);// 合并行,列(起始行,起始列,合并行数,合并列数) cells.get(1, 0).putValue(list_BT.get(0)); cells.setColumnWidth(0, 15); // 设置行宽 cells.get(1, 1).putValue(list_BT.get(1)); cells.setColumnWidth(1, 22); // 设置行宽 cells.get(1, 2).putValue(list_BT.get(2)); cells.setColumnWidth(2, 45); // 设置行宽 cells.get(1, 3).putValue(list_BT.get(3)); cells.setColumnWidth(3, 18); // 设置行宽 cells.get(1, 4).putValue(list_BT.get(4)); cells.setColumnWidth(4, 18); // 设置行宽 cells.get(1, 5).putValue(list_BT.get(5)); cells.setColumnWidth(5, 13); // 设置行宽 cells.get(1, 6).putValue(list_BT.get(6)); cells.setColumnWidth(6, 13); // 设置行宽 // 填充数据 for (int i = 0; i < pxxx_list.size(); i++) { String rn = "0"; if (pxxx_list.get(i).get("RN") != null) { rn = pxxx_list.get(i).get("RN").toString(); } // 合并行数 int rowspan = Integer.parseInt(pxxx_list.get(i).get("记录条数").toString()) + 1; for (int j = 0; j < 7; j++) { // 当循环到个人的第一条培训信息 if (j == 0 && "1".equals(rn)) { cells.get(i + 2, j).putValue(pxxx_list.get(i).get(list_BT.get(j)) == null ? "" : pxxx_list.get(i).get(list_BT.get(j)).toString()); // 合并单元格 cells.merge(i + 2, 0, rowspan, 1);// 合并行,列(起始行,起始列,合并行数,合并列数) } else if (j != 0) { cells.get(i + 2, j).putValue(pxxx_list.get(i).get(list_BT.get(j)) == null ? "" : pxxx_list.get(i).get(list_BT.get(j)).toString()); } } } // 调整样式 for (int i = 0; i < pxxx_list.size() + 2; i++) { for (int j = 0; j < 7; j++) { AddBorder(cells, i, j); AddBorders(cells, i, j); } } response.addHeader("Content-Disposition", "attachment;filename=" + new String(java.net.URLEncoder.encode(fileName, "UTF-8"))); response.addHeader("Content-Type", "application/octet-stream; charset=UTF-8"); wb.save(response.getOutputStream(), SaveFormat.XLSX); } catch (Exception e) { throw e; } }

写完这条sql有种身体被掏空的感觉,查询时间也缩短到了一秒以内,整体思路就是t1(培训信息字表)left join t2(个人记录条数表),连完之后再union all t3(统计行表)  ,这条sql能正常运行出来其中也是蛮多坑的,以前以为union的时候字段名相同就行了,现在才知道类型也要一样,所以字段基本都进行了to_char()处理,然后最精妙的还是

ROW_NUMBER () OVER (PARTITION BY ... ORDER BY ...) 函数的运用,这个函数确实帮了很大的忙。

总结

写完上面那条sql后我都在怀疑是不是我写出来的,因为第二天我就看不懂我写的是什么了,更不想细看或者去改动,可能是看到那么多select就感觉太复杂了,懒得看,其实静下心看还是能看出来每个select在干嘛,我也不想写这么多查询,只是每个查询都是必要的,可能还有精简优化的余地我没发现。想了下这几个月我进步最大的还是写sql吧,越写越感叹于sql的博大精深,其实我感觉写sql也可以用面向对象来理解,说简单点就是把每张表当个整体,想要什么就查什么,无非就是一些连接操作和一些函数的运用,一层不行就套两层,两层不行就再套一层,套完之后验证一下数据的正确性,看是不是自己想要的结果,最后再当成一张表进行操作,不用理里面的查询是怎么实现的,虽然到最后可能自己都看不懂在干什么,但是只要思路清晰,写起来就得心应手,水到渠成了。

 

 

你可能感兴趣的:(一次sql优化)