相关文章推荐
大力的键盘  ·  openOffice ...·  2 天前    · 
唠叨的硬盘  ·  Android解决java.lang.NoS ...·  1 年前    · 
刚毅的企鹅  ·  java - Spring Tool ...·  1 年前    · 
玩篮球的柠檬  ·  NumPy 安装| 菜鸟教程·  1 年前    · 

转载请注明来源-作者@loongshawn: http://blog.csdn.net/loongshawn/article/details/53457953 ,建议读者阅读原文,确保获得完整的信息

最近在应对一个数据查询导出模块,总体要求就是依据给定的SQL语句,输出其查询结果为csv或者xlsx文件。其中查询数据量可能会有大数据量,成百上千万都可能。

针对上述数据导出这个问题,提取出几个关键词:

  • 1、SQL是由作业人员临时写的。
  • 2、数据量大。
  • 3、输出csv或xlsx文件。

针对这几个关键词,咱分别扩展下其含义:

第一个关键词:SQL是临时写的,这就意味着咱只是去执行这条语句,并不能对语句进行分页设计啥的,如果要重新分析SQL可能比较困难。这个时候比较贴近的场景就是“数据库查询客户端”,客户端只管执行SQL,至于执行得快慢等取决于语句及数据库性能等。

第二个关键词:数据量大,即查询的返回结果可能比较多,你如何处理返回结果,是将其先存到List列表还是直接在结果集里面就给输出到文件。这就需要考虑内存、机器性能问题,不要一条语句执行了,直接导致你的java程序死掉了,比如JVM内存溢出,CPU使用率蹭蹭的涨到99%,导致整个程序无响应。

第三个关键词:输出csv或xlsx文件,比如csv是利用成熟的三方库还是自己写(毕竟就是逗号分隔的文本),不同人可能有不同看法,但是我主张大家用现成的三方依赖包,比如javacsv\opencsv都是比较成熟的工具包。

其中,有关csv读写在文章 《 利用JavaCSV API来读写csv文件》 中有详细介绍。而有关xlsx读写需要的jar包则在前面的文章 《 Java处理excel两种不同的方式》 有过介绍。

在实例“Java导出数据库查询结果”中,我选取的实现方法为直接在ResultSet结果集中将数据写入到文件,这么操作基于两点:

  • 1、做分页困难,没法降低查询数据量。
  • 2、大数据量内存稀缺,尽量减少重复数据存储。

3.1导出csv

使用的依赖库:

<!-- https://mvnrepository.com/artifact/net.sourceforge.javacsv/javacsv -->
<dependency>
    <groupId>net.sourceforge.javacsv</groupId>
    <artifactId>javacsv</artifactId>
    <version>2.1</version>
</dependency>

       截取部分代码片段

// 判断文件是否存在,存在则删除,然后创建新表格
File tmp = new File(filePath);
if (tmp.exists()){
	if (tmp.delete()){
		logger.info(filePath + Constant.DUPLICATE_FILE_DELETE);
// 创建CSV写对象
CsvWriter csvWriter = new CsvWriter(filePath,Constant.SEPARATOR, Charset.forName("GBK"));
// 数据查询开始
preparedStatement = connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
// 获取结果集表头
ResultSetMetaData md = resultSet.getMetaData();
int columnCount = md.getColumnCount();
logger.debug("返回结果字段个数:" + columnCount);
JSONArray columnName = new JSONArray();
for (int i = 1; i <= columnCount; i++) {
JSONObject object = new JSONObject();
object.put(KEY.COLUNM_NAME,md.getColumnName(i));
columnName.add(object);
// 获取表头数组
int columnSize = columnName.size();
String[] columnNameList = ListUtil.getListFromJSONArray(columnName);
csvWriter.writeRecord(columnNameList);
// 数据记录数
int i = 0;
// 临时数据存储
StringBuffer stringBuffer = new StringBuffer();
while (resultSet.next()) {
	// 记录号
	i++;
	// 依据列名获取各列值
	for (int j = 1; j<=columnSize; j++){
		String value = resultSet.getString(j);
		//创建列
		stringBuffer.append(value);
		if (j != columnSize){
			stringBuffer.append(Constant.COMMA);
	String buffer_string = stringBuffer.toString();
	String[] content = buffer_string.split(Constant.COMMA);
	csvWriter.writeRecord(content);
	stringBuffer.setLength(0);
// 文件输出
csvWriter.flush();
csvWriter.close();

       针对相同文件导出csv结果如下:

3.2导出xlsx

       使用的依赖库:

<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>poi</artifactId>
	<version>3.15</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>poi-ooxml</artifactId>
	<version>3.15</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.xmlbeans/xmlbeans -->
<dependency>
	<groupId>org.apache.xmlbeans</groupId>
	<artifactId>xmlbeans</artifactId>
	<version>2.6.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml-schemas -->
<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>poi-ooxml-schemas</artifactId>
	<version>




    
3.14</version>
</dependency>

       截取部分代码片段:

// 数据查询开始
preparedStatement = connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
// 获取结果集表头
ResultSetMetaData md = resultSet.getMetaData();
int columnCount = md.getColumnCount();
logger.debug("返回结果字段个数:" + columnCount);
JSONArray columnName = new JSONArray();
for (int i = 1; i <= columnCount; i++) {
    JSONObject object = new JSONObject();
    object.put(KEY.COLUNM_NAME,md.getColumnName(i));
    columnName.add(object);
int columnNameSize = columnName.size();
//ExcelUtil.createSXSSFFile(columnName,filePath);
//inputFS = new FileInputStream(tmp);
// 读取工作薄
Workbook wb = new SXSSFWorkbook(500);
// 记录总数
int i = 0;
while (resultSet.next()) {
    // 记录号
    i++;
    // logger.info("i:"+i);
    int factor = CalculateUtil.getMultiplyingFactor(i);
    int index = i%Constant.XLSX_LENGTH;
    // logger.info("index:"+index);
    if ( index == 1){
        // 创建工作表
        sheet = wb.createSheet("sheet"+factor);
        // 写表头
        ExcelUtil.writeTitle(sheet,columnName);
    // 创建行
    Row row;
    if ( index != 0) {
        row = sheet.createRow(index);
    } else {
        row = sheet.createRow(Constant.XLSX_LENGTH);
    // 依据列名获取各列值
    for (int j = 0; j < columnNameSize; j++) {
        String value = resultSet.getString(j + 1);
        //创建列
        Cell cell = row.createCell((short) j);
        cell.setCellValue(value);
// 文件输出
FileOutputStream out = new FileOutputStream(filePath);
wb.write(out);
out.flush();
out.close();

       在导出xlsx里面需要补充说明一点的就是SXSSFWorkbook这个对象的使用,如下:

Workbook wb = new SXSSFWorkbook(500);

       因为通常咱使用习惯都是利用XSSFWorkbook来创建xlsx,两者明显的区别就是,SXSSFWorkbook可以设定内存数据写入硬盘的阈值,即每提交多少条数据就写入一次硬盘,有效的避免了大数据量存储时内存溢出的风险。比如代码中我指定的阈值为500条。

       有关SXSSFWorkbook对象的详细说明可以参考以下内容:

       从上图中可以看出,SXSSF是对XSSF的扩展,用来应对大容量电子表格的输出,自3.8-beta3版本的poi库就添加了。

       针对相同文件导出excel结果如下:

       补充说明:

excel存储容量,2010版后支持单张sheet表格最大行数1048576,sheet表最大数目没有统一说法,但有一点可以肯定,数据量大太加上自己电脑的性能的限制,excel整体性能会受影响,不是说无限制往里存。

csv与excel数据存储能力及效率对比:

属性csvexcel
存储容量没有限制,类似txt文本单张sheet表有限制,可以存大量sheet表
存储效率相对csv慢
占用空间相同内容,少量时占用空间少,大量时占用空间大相同内容,少量占用空间大,大量占用空间少

       其中相同文件名均代表同一SQL输出不同格式的文件,从下图中可以看出,相同的查询结果,如果内容较少(几十KB),存放在csv文件中占用空间较少。如果内容较大(几百KB),存放在excel文件中占用空间较少,这个应该是excel文件在大容量时做了性能优化。毕竟MicroSoft是靠系统和办公软件起家,其excel还是具备含金量的,不仅仅是好看,在高级层面还是做了不少工作的。

       输出csv及excel文件结果对比图:

       最近有同学说需要完整的代码,限于公司规定,完整代码不能提供。但是如果有同学在实际操作过程中碰到问题、不理解之处都可以通过留言指出来,看看可否协助你分析下。

6.工具类

样例代码中包含以下工具类,有不清楚的请留言。

CalculateUtil .java

import com.amap.axf.data.batch.constant.Constant;
 * Created by loongshawn on 2016/12/7.
 * NOTE 计算指定数字所属区间段
public class CalculateUtil {
    public static int getMultiplyingFactor(int num){
        int factor = 1;
        for (int i=1;i<= Constant.MAX_PAGE;i++){
            int tmp = num - (i*Constant.XLSX_LENGTH);
            if (tmp <= 0){
                factor = i;
                break;
        return factor;

ListUtil .java

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.amap.axf.data.batch.constant.Constant;
import com.amap.axf.data.batch.constant.KEY;
import java.util.List;
 * Created by loongshawn on 2016/11/29.
 * NOTE 排序
public class ListUtil {
    // 顺序排序
    public static List<Integer> getSortedList(List<Integer> list){
        int size = list.size();
        for (int i=0;i<size;i++){
            for (int j=i;j<size;j++){
                Integer item_i = list.get(i);
                Integer item_j = list.get(j);
                if (item_i.intValue() > item_j.intValue()){
                    list.set(i,item_j);
                    list.set(j,item_i);
        return list;
    public static String[] getListFromJSONArray(JSONArray jsonArray){
        // 获取表头数组
        int columnSize = jsonArray.size();
        StringBuffer tmp_column = new StringBuffer();
        for (int m=0;m<columnSize;m++){
            // 获取JSONObject
            JSONObject jsonCloumn = jsonArray.getJSONObject(m);
            String title_name = jsonCloumn.getString(KEY.COLUNM_NAME);
            tmp_column.append(title_name);
            if (m != columnSize-1){
                tmp_column.append(Constant.COMMA);
        String[] columnNameList = tmp_column.toString().split(Constant.COMMA);
        return columnNameList;
    public static String getStringFromJSONArray(JSONArray jsonArray){
        // 获取表头数组
        int columnSize = jsonArray.size();
        StringBuffer tmp_column = new StringBuffer();
        for (int m=0;m<columnSize;m++){
            // 获取JSONObject
            JSONObject jsonCloumn = jsonArray.getJSONObject(m);
            String title_name = jsonCloumn.getString(KEY.COLUNM_NAME);
            tmp_column.append(title_name);
            if (m != columnSize-1){
                tmp_column.append(Constant.TAB);
        return tmp_column.toString();
				
C# 开发的导出六个数据库查询结果为CSV文件到指定目录的小程序源代码。可同时导出多个库(mssql、oracle)多个查询数据,无需安装oracle客户端,。 本程序用于自动导出数据上报流感监测信息。需要的同学可下载学习。使用VS2017编译。 通过文件操作,加载SQL文件夹下特定语句和变量条件,查询数据库结果后保存为 文件名yyyymmdd.csv格式文件。 Console.WriteLine("等待导出下一个文件..."); string fileName3 = "D:\\流感上报\\hda_" + DateTime.Now.ToString("yyyyMMdd") + ".csv"; ImportToCSV(PETable3(), fileName3); Console.WriteLine(fileName3 + "出院小结导出成功"); Console.WriteLine("等待导出下一个文件..."); string fileName4 = "D:\\流感上报\\hdr_" + DateTime.Now.ToString("yyyyMMdd") + ".csv"; ImportToCSV(PETable4(), fileName4); Console.WriteLine(fileName4 + "院内死亡流感病例导出成功");
<groupId>org.apache.commons</groupId> <artifactId>commons-csv</artifactId> <version>1.8</version> </dependency> 二,代码实现 publicstaticvoidmain(Stri... @PostMapping("/news-csv") @ApiOperation(value = "查询数据导出CSV") public void newsDataExportCsv(HttpServletResponse response, @ApiParam(value = "导出对象实体", required = true) @RequestBody PageEntity pageEntity) throws Exception { logger.info(New
首先:什么是.csv文件? 所谓“CSV”,是CommaSeparatedValue(逗号分隔值)的英文缩写,通常都是纯文本文件。下面是一个实际CSV文件中的部分内容,让大家对他有一个感性的认识。我们选的是Sjojo_Rescan的CSV文件(Sjojo是ASW-亚洲扫图风的成员之一)。 sj_mino1001.jpg,715282,4FB55FE8, sj_mino1002.jpg,47...
以前导出数据的工具类总是各种各样的,而且数据量大的导出要么不行,要么就是比较慢,于是在网上找到一些关于导出数据到csv的代码,自己做了些整理和调整。其他地方只要调用改工具类对应方法即可。 package cn.yunda.guns.base.utils; import com.alibaba.druid.util.StringUtils; import lombok.extern.slf4j...
public class ExportCSV { public static void main(String[] args) { Connection connection = null; try { // 连接数据库 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase", "root", "password"); Statement statement = connection.createStatement(); // 查询数据 ResultSet resultSet = statement.executeQuery("SELECT * FROM mytable"); // 创建CSV文件并写入数据 FileWriter writer = new FileWriter("export.csv"); while (resultSet.next()) { String id = resultSet.getString("id"); String name = resultSet.getString("name"); String age = resultSet.getString("age"); writer.write(id + "," + name + "," + age + "\n"); writer.close(); // 关闭连接 resultSet.close(); statement.close(); connection.close(); } catch (SQLException | IOException e) { e.printStackTrace(); 以上代码将数据库表`mytable`中的`id`、`name`、`age`字段导出到`export.csv`文件中。你需要将其中的`mydatabase`、`root`、`password`、`mytable`替换为你自己的数据库信息。