首页 > 代码库 > 大数据导入EXCEL

大数据导入EXCEL

    最近上头给我派了一个活,oracle数据导入excel,接任务的时候,我感觉比较轻松,心里想,这很简单,三下五除二,一个上午就可以搞定,因为之前实现过嘛!

    但是在加上“大数据”烙印之后,就不是那么简单的一回事了,实现过程中,出现最常见的两个问题:超出行数限制和内存溢出!

    18天的数据,总共是500w条,如何将500w条记录存入excel中,我当时想过两种实现方式:PLSQL DEVELOPER和Java poi!

    PLSQL DEVELOPER

    有两种实现方法:

    1、在新建一个SQL WINDOW,执行你要导出数据的查询语句,查询完之后,在结果显示的地方点击向下的箭头,让它全部显示,这可能需要一点点时间,显示结束后,右键点击显示结果的地方,选中 copy to excel(xls和xlsx,前者是03及以前版本,每个sheet只能显示65535条记录;后者是07及以后版本每个sheet可以显示1048576条记录)。

    2、在新建一个REPORT WINDOW,执行你要导出数据的查询语句,查询完之后,点击屏幕右边绿色圆饼状图标(export results),后面的操作就很简单了,不再废话。

    两种方式比较实现起来简单,易操作,但是有很严重的弊端:其一,PLSQL DEVELOPER一次导出excel数据有限,只有几十万条,超出范围,则内存溢出;其二,如果分页查询或者条件查询,则分批的数据又不能导入同一个excel中。挺痛苦的~

    在简单方法行不通的时候,只能走向更加复杂的程序之路...

    Java poi

    在使用Java poi之前,尝试过JXL,但是个人觉得Java poi更加顺手,这并不是说JXL不好用,JXL更多地面向的是底层,比较麻烦点,但更加灵活;而Java poi封装地更多,使用起来更加顺手。

    其实这些都不是重点!

    重点是在实现过程中如何处理上面两个最常见的问题:超出行数限制和内存溢出!

    内存溢出:

    一个经常处理大数据,公司硬件却跟不上的软肋,真心耗费时间!最常用的解决途径就是分批处理,结合Java 虚拟机观察一次处理中在不导致内存溢出的前提下,最大能处理的数据量,以达到虚拟机的充分利用。

    在oracle查询数据这一段,写个分页查询,分页查询完后,都放入到一个集合中,具体实现过程,暂且不表!

    超出行数限制:

    如果将oracle中查询出500w数据一股脑儿导入excel,又会遇到另一个棘手问题:超出行数限制。

    如果到的是xls格式,我就让程序循环跑起来,循环一次,导入65535条;xlsx格式的,就让它循环一次,导入1048576条,如此循环下去,直到程序跑完!

    请看本人代码示例:  

public class XlsDto2Excel {  
    @Autowired
    private ToDBDao toDBDao;
    /**
     *
     * @param xls
     * XlsDto实体类的一个对象
     * @throws Exception
     * 在导入Excel的过程中抛出异常
     */
    public void toExcel(String date, int count) {

        int PAGESIZE = 65535;
        // declare a new workbook 声明一个工作簿
        HSSFWorkbook wb = new HSSFWorkbook();

        // declare a row object reference 声明一个新行
        HSSFRow r = null;

        // declare a cell object reference 声明一个单元格
        HSSFCell c0, c1, c2, c3, c4, c5 = null;

        HSSFCell[] firstcell = new HSSFCell[6];

        // create 2 cell styles 创建2个单元格样式
        HSSFCellStyle cs = wb.createCellStyle();
        HSSFCellStyle cs2 = wb.createCellStyle();

        // create 2 fonts objects 创建2个单元格字体
        HSSFFont f = wb.createFont();
        HSSFFont f2 = wb.createFont();

        // Set font 1 to 12 point type, blue and bold 设置字体类型1到12号,蓝色和粗体
        f.setFontHeightInPoints((short) 12);
        f.setColor(HSSFColor.RED.index);
        f.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);

        // Set font 2 to 10 point type, red and bold 设置字体类型2到10号,黑色和粗体
        f2.setFontHeightInPoints((short) 10);
        f2.setColor(HSSFColor.BLACK.index);
        f2.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);

        // Set cell style and formatting 设置单元格样式和格式
        cs.setFont(f);
        // 水平布局:居中
        cs.setAlignment(HSSFCellStyle.ALIGN_CENTER);
        // cs.setDataFormat(df.getFormat("#,##0.0"));

        // Set the other cell style and formatting 设置其他单元格样式和格式
        cs2.setBorderBottom(cs2.BORDER_THIN);
        cs2.setDataFormat(HSSFDataFormat.getBuiltinFormat("text"));
        cs2.setFont(f2);
        // 水平布局:居中
        cs2.setAlignment(HSSFCellStyle.ALIGN_CENTER);

        // 从数据库,获取总的集合大小
        List list = this.toDBDao.selectFatherData(date);

        // 获取循环次数(在此结果上+1),一次循环下,子循环PAGESIZE次,最后一次循环,子循环mod次
        int circleCount = list.size() / PAGESIZE;

        int mod = list.size() % PAGESIZE;

        String firstOrderId = "";

        String orderTime = "";

        for (int i = 0; i < circleCount + 1; i++) {
          
            // create a new sheet 创建一个新工作表,但一个sheet加载满65535条记录后,自动生成一个新的sheet,以保证不会超出行数限制
            HSSFSheet sheet = wb.createSheet("第" + i+ "页");
            /*
             * 设置表头
             */
            HSSFRow firstrow = sheet.createRow(0); // 下标为0的行开始
            
            String[] names = new String[6];
            names[0] = "访问编号";
            names[1] = "浏览数";
            names[2] = "平均访问时长";
            names[3] = "订单编号";
            names[4] = "下单时间";
            names[5] = "初始时间";

            for (int j = 0; j < 6; j++) {

                firstcell[j] = firstrow.createCell(j);
                firstcell[j].setCellValue(new HSSFRichTextString(names[j]));
                firstcell[j].setCellStyle(cs2);
            }
            //最后一次循环
            if (i == circleCount) {
                
                for (int rownum = 1; rownum < mod; rownum++) {

                    // 获取行对象
                    r = sheet.createRow(rownum);

                    HashMap father = (HashMap) list.get(rownum + PAGESIZE * i);

                    for (int cellnum = 0; cellnum < 6; cellnum++) {

                        /*
                         * 获取列对象
                         */
                        c0 = r.createCell(0);
                        c1 = r.createCell(1);
                        c2 = r.createCell(2);
                        c3 = r.createCell(3);
                        c4 = r.createCell(4);
                        c5 = r.createCell(5);
                        /*
                         * 给列对象赋值
                         */
                        c0.setCellValue(father.get("sessionId").toString());
                        c0.setCellStyle(cs2);

                        c1.setCellValue(father.get("visitPages").toString());
                        c1.setCellStyle(cs2);

                        c2.setCellValue(father.get("perVisitsTime").toString());
                        c2.setCellStyle(cs2);

                        if (null != father.get("firstOrderId")) {

                            firstOrderId = father.get("firstOrderId")
                                    .toString();

                        }
                        c3.setCellValue(firstOrderId);
                        c3.setCellStyle(cs2);

                        if (null != father.get("orderTime")) {

                            orderTime = father.get("orderTime").toString();

                        }
                        c4.setCellValue(orderTime);
                        c4.setCellStyle(cs2);

                        c5.setCellValue(father.get("initTime").toString());
                        c5.setCellStyle(cs2);

                    }

                }

            } else {

                for (int rownum = 1; rownum <= PAGESIZE; rownum++) {

                    // 获取行对象
                    r = sheet.createRow(rownum);

                    HashMap father = (HashMap) list.get(rownum + PAGESIZE * i);

                    for (int cellnum = 0; cellnum < 6; cellnum++) {

                        /*
                         * 获取列对象
                         */
                        c0 = r.createCell(0);
                        c1 = r.createCell(1);
                        c2 = r.createCell(2);
                        c3 = r.createCell(3);
                        c4 = r.createCell(4);
                        c5 = r.createCell(5);
                        /*
                         * 给列对象赋值
                         */
                        c0.setCellValue(father.get("sessionId").toString());
                        c0.setCellStyle(cs2);

                        c1.setCellValue(father.get("visitPages").toString());
                        c1.setCellStyle(cs2);

                        c2.setCellValue(father.get("perVisitsTime").toString());
                        c2.setCellStyle(cs2);

                        if (null != father.get("firstOrderId")) {

                            firstOrderId = father.get("firstOrderId")
                                    .toString();

                        }
                        c3.setCellValue(firstOrderId);
                        c3.setCellStyle(cs2);

                        if (null != father.get("orderTime")) {

                            orderTime = father.get("orderTime").toString();

                        }
                        c4.setCellValue(orderTime);
                        c4.setCellStyle(cs2);

                        c5.setCellValue(father.get("initTime").toString());
                        c5.setCellStyle(cs2);

                    }
                }

            }

            sheet.autoSizeColumn((short) 0); // 根据内容调整第一列宽度,不过不设置,默认情况下,按照表头自动调整宽度
            sheet.autoSizeColumn((short) 4); // 根据内容调整第五列宽度,不过不设置,默认情况下,按照表头自动调整宽度
            sheet.autoSizeColumn((short) 5); // 根据内容调整第六列宽度,不过不设置,默认情况下,按照表头自动调整宽度
        }
        // Save保存
        FileOutputStream out;
        try {
            out = new FileOutputStream("d://workbook.xls");
            wb.write(out);
            out.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("--执行完毕--");

    }
}
    以上代码导入的是xls格式。

    说明:虽然,用xlsx格式方式可以一次导入100w条记录,但是代码执行要慢上许多,我也没搞清楚具体原因。不过,大家可以在导入同样多数据情况下,用xls和xlsx两种方式对比一下。

    其实除了以上两种方式之外,经本人查找资料,还找到了另一个更加方便的方式,那就是搭建数据源ODBC,连接excel,数据传输。

    如何搭建oracle ODBC,网上资料一查一大把,这里我就不再赘述!

    搭建完之后,我们就来操作excel,新建一个excel,07版的更好,选中数据。

   

    选择来自数据连接向导—>其他/高级—>选择带有oracle的选项,接着输入用户名、密码和连接地址,连接成功后,会显示oracle中的所有表名,注意这些表明排列是没有顺序的,但是我们可以快速索引到我们想要导出数据的表名,比如一个表名叫做father_user_behavior,我们可以这样定位:先按f,再按a,再按t,这样基本上可以直接定位到你想要的表名,接下来的操作就很简单了,不多说!

    需要注意的是,当数据超出1048576条,同样会出现问题(它不会自动生成第二个sheet):

   

   所以这种方式,也有其弊端。