我现在有一个任务就是把表里的数据导出来以文本文件写在本地,给下游系统。导出是程序每天自动导出一次保存在指定文件夹下以当天日期新建一个文件夹,放在里面,导出要求就是以定长格式导出,长度不够的在右侧补空格,具体的导出长度以数据库表的字段类型决定.
INTEGER                12 注:如不足12个字节右补空格
BYTEINT                12 注:如不足12个字节右补空格
SMALLINT                12 注:如不足12个字节右补空格
CHAR(n)                n 字符型的长度为定义的长度; 如char(10)或varchar(10)的字段,导出时对应长度为10;
VARCHAR(n)       n
DECIMAL(n,m)      (n +2) 注:如不足字节后右补空格  如decimal(15,3),导出时对应的长度为17;
FLOAT                30
DATE                10  注:如不足字节后右补空格 格式:YYYYMMDD  
TIMESTAMP                19  格式:YYYY-MM-DDBHH:MI:SS
这就是要导出的长度,现在导出数据有两个问题
1、导出太慢,具体导出一个30多万条数据的表要很长时间至少在20分钟以上,
2、数据太大,这个没办法,上面要这样要求只能这样做,30多万条数据,我导出来有800多M;中间的空格可想而知。
大家看看有什么好的办法可以在性能上优化一下。我的代码如下
private boolean ExportData(SQLExcuteInit sei,BufferedWriter bf){
SimpleLog.log("导出表数据",getJob().getJobId()+"");
boolean flag=true;
String sql = sei.getSQLJob(getJob().getParameter()).replace(";", "");//从数据库中取查询SQL
System.out.println("sql="+sql);
SimpleLog.log("sql:"+sql,getJob().getJobId()+"");
PreparedStatement pstmt=null;
Connection conn=null;
ResultSet rs=null;
ResultSetMetaData rsm=null;
ConnectionFactory cf = new ConnectionFactoryImpl();
conn = cf.getConnection("SMDB");
DBUtil dbutil=new DBUtil();
try {
pstmt = dbutil.createPreparedStatement(conn, sql);
rs = dbutil.getResultSet(pstmt);
// System.out.println("rs="+rs.getRow());
rsm = dbutil.getResultSetMetaData(rs);
int counter=0;
while (rs.next()) {
counter++;
StringBuffer sb = new StringBuffer();
// System.out.println("**************************");
for (int i = 1; i < rsm.getColumnCount() + 1; i++) {
// System.out.println("rsm.getColumnType(i)="+rsm.getColumnType(i)+"getPrecision="+rsm.getPrecision(i) );
switch (rsm.getColumnType(i)) {//下面是判断表的字段类型
case 2:
if (rsm.getPrecision(i) == 126
&& rsm.getScale(i) == -127) {// float类型,长度要求30
// System.out.println(rsm.getColumnName(i)+"="+rs.getFloat(i));
sb.append(formatFixLong(rs.getFloat(i) + "", 30));
break;
} else if (rsm.getPrecision(i) > 0
&& rsm.getScale(i) > 0) {// DECIMAL(n,m)类型,长度要求n+2
sb.append(formatFixLong(rs.getBigDecimal(i) + "",
rsm.getPrecision(i) + 2));
break;
} else {// 其他为number型,长度要求14
// System.out.println(rsm.getColumnName(i) + "="
// + rs.getInt(i));
sb.append(formatFixLong(rs.getInt(i) + "", 14));
break;
} case 12:
sb.append(formatFixLong(rs.getString(i)+"", rsm
.getPrecision(i)));
break;// varchar2(n)型,长度要求:n
case 91:
sb.append(formatFixLong(rs.getDate(i) + "", 10));
break;// date类型,长度要求10
case 93:
sb.append(formatFixLong(rs.getTimestamp(i) + "", 19));
break;// TIMESTAMP类型,长度要求19
 default ://未知数据类型,长度为数据库类型中默认最大长度
 sb.append(formatFixLong(rs.getString(i) + "", rsm.getPrecision(i)));
 SimpleLog.log("未知数据类型:"+rsm.getColumnName(i),getJob().getJobId()+"");
 System.out.println(rsm.getColumnName(i)+"="+rs.getString(i));//其他类型作字符串处理
   break;
 
} }
bf.write(sb.toString());
bf.newLine();
if(counter%1000==0)
bf.flush();
}
bf.flush();

} catch (SQLException e) {
flag=false;
SimpleLog.log(e,getJob().getJobId()+"");
e.printStackTrace();
}catch (IOException e) {
flag=false;
SimpleLog.log(e,getJob().getJobId()+"");
e.printStackTrace();
}finally{
try {
if(rs!=null)
rs.close();
if(pstmt!=null)
pstmt.close();
if(conn!=null)
conn.close();
} catch (SQLException e) {
SimpleLog.log(e,getJob().getJobId()+"");
e.printStackTrace();
}
}
System.out.println("收集表数据完成共有数据");
SimpleLog.log("导出表数据完成",getJob().getJobId()+"");
return flag;
}
private String formatFixLong(String str,int length){//把str格式化成length长度的字符串
// SimpleLog.log("格式化成定长文件:["+str+"]文件长度:"+length,getJob().getJobId()+"");
StringBuffer sb=new StringBuffer();
String temstr="                               " +
"                                       " +
"                              ";//长度100的空格
if(str==null)
str="";
// int strlen=str.length();
int strlen=str.getBytes().length;
if(strlen>=length){//如果字符串长度大于给定长度则截取本身
// byte[] =str.getBytes();
// String s=new String(by,0,length);
// return str.substring(0,length);
return new String(str.getBytes(),0,length);
}
sb.append(str);
while(strlen<length){//右侧加空格
int tlen=(length-strlen)>100?100:(length-strlen);//一次最大只能加100个空格
sb.append(temstr.substring(0,tlen));
// System.out.println(sb.toString());
strlen+=tlen;
}
return sb.toString();

}

解决方案 »

  1.   

    是的。一次把结果集全部弄出来不好,吃内存,数据更多比如1000w条,就可能出现OutOfMemory了。。
    分1k~1w条一个结果集写完一个再写另一个。可以开多线程来做,先写多个小文件,最后合并成一个文件。大文件的操作比较慢。
      

  2.   

    while、for、switch...这样不慢才怪。dao方面最好封装一下,用rm就先省了switch,然后一个for去做即可。或者看看sql里有没有直接可以格式化输出的函数
      

  3.   

    在sqlplus  
      直接将表存为文本文件  
      spool   c:\tbdata.txt  
      set   heading   off  
      set   feedback   off  
      select   *   from   tbname;  
      spool   off;
      

  4.   

    这个东西确实挺讨厌,while、for、switch...这样不慢才怪;就是因为要根据不同的数据类型来确定其输了长度,不这样做,我就不能知道他到底要多长,不过你说的看看sql里有没有直接格式化输出的函数,这里我倒是没有去查,可以去看看。
      

  5.   

    这个行不通,因为这个sql语句是事先不知道的,是他配置保存在数据库里的,我每次导数据都要去数据库里查出来让程序去导出来。而且要导出来的表可能会很多,而且是每天都执行的,所以人工导可能不怎么行得通。