之前用create table select * from XXTable无疑是创建庞大表的最快方案之一,但是数据重复率是个问题,且数据难以操控。

于是我在之前批量插数据的基础上更新了一个类,让它具有了Resume的能力,这样可以利用碎片时间能插一点是一点。

以后此类还可能改进,先留一个版本在这里。

数据库连接参数类:

class DBParam {
public final static String Driver = "oracle.jdbc.driver.OracleDriver";
public final static String DbUrl = "jdbc:oracle:thin:@127.0.0.1:1521:orcl";
public final static String User = "ufo";
public final static String Pswd = "1234";
}

HugeTbBatchInserter类:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Random; class TypeField{
String type;
String field;
} // Insert huge records to a table
public class HugeTbBatchInserter {
private final int BatchSize=250;// Batch insert size,可以根据机器性能提高
private final int Total_Record_Count=100000000;// 最好是BatchSize的整倍数 // 如果是多个表,扩充数组即可
// PK:主键 CH:文字 DT:Datetime,RND:百以内随机数 还可以根据需要扩充代号,在getInsertSql函数中则根据代号来设置值
private final String[][] tableArray= {
{"score:"+Total_Record_Count,"PK:ID","RND:stuid","RND:courseid","RND:score"},
}; /**
* 批量插值
*/
public void batchInsert() {
Connection conn = null;
Statement stmt = null; try{
Class.forName(DBParam.Driver).newInstance();
conn = DriverManager.getConnection(DBParam.DbUrl, DBParam.User, DBParam.Pswd);
stmt = conn.createStatement();
System.out.println("Begin to access "+DBParam.DbUrl+" as "+DBParam.User+"..."); for(String[] innerArr:tableArray) {
String tableName=innerArr[0].split(":")[0];
System.out.println("Table:"+tableName); int existCount=fetchExistCount(tableName,stmt);
System.out.println("Exist record count:"+existCount); int maxId=fetchMaxId(tableName,stmt);
System.out.println("Max id:"+maxId); int count=Integer.parseInt(innerArr[0].split(":")[1])-existCount;
System.out.println("准备向表"+tableName+"插入"+count+"条记录."); // 是否需要插值前先清空,自行判断再放开
//truncateTable(tableName,stmt); // 真正插入数据
insertTestDataTo(tableName,maxId+1,count,innerArr,stmt);
}
} catch (Exception e) {
System.out.print(e.getMessage());
} finally {
try {
stmt.close();
conn.close();
} catch (SQLException e) {
System.out.print("Can't close stmt/conn because of " + e.getMessage());
}
}
} /**
* 以当前时间为基准减去数十秒
* @param n
* @return
*/
private static String getDatetimeBefore(int n) {
try {
Calendar now = Calendar.getInstance();
now.add(Calendar.SECOND,-n*10);//日期减去n*10秒 Date newDate=now.getTime(); SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String retval = sdf.format(newDate);
return retval;
}
catch(Exception ex) {
ex.printStackTrace();
return null;
}
} /**
* 清空一个表的数据,注意此功能有破坏性,不可恢复,注意备份好数据
* @param tableName
* @param conn
* @param stmt
* @throws SQLException
*/
private void truncateTable(String tableName,Statement stmt) throws SQLException{
String sql="truncate table "+tableName;
stmt.execute(sql);
System.out.println("truncated table:"+tableName);
} /**
* 得到表中已有的最大ID值
* @param tableName
* @param conn
* @param stmt
* @return
* @throws SQLException
*/
private int fetchMaxId(String tableName,Statement stmt) throws SQLException{
String sql="select max(id) as max from "+tableName+""; ResultSet rs = stmt.executeQuery(sql); while (rs.next()) {
int max = rs.getInt("max");
return max;
} return 0;
} /**
* 得到表中现存数量
* @param tableName
* @param conn
* @param stmt
* @return
* @throws SQLException
*/
private int fetchExistCount(String tableName,Statement stmt) throws SQLException{
String sql="select count(*) as cnt from "+tableName+""; ResultSet rs = stmt.executeQuery(sql); while (rs.next()) {
int cnt = rs.getInt("cnt");
return cnt;
} return 0;
} /**
* 向一个表插入数据
* @param tableName
* @param count
* @param innerArr
* @param conn
* @param stmt
* @throws SQLException
*/
private void insertTestDataTo(String tableName,int startId,int count,String[] innerArr,Statement stmt) throws SQLException{
// 得到字段名和字段类型
List<TypeField> typefields=new ArrayList<TypeField>();
for(int i=1;i<innerArr.length;i++) {
String temp=innerArr[i];
String[] arrTmp=temp.split(":"); TypeField tf=new TypeField();
tf.type=arrTmp[0];
tf.field=arrTmp[1];
typefields.add(tf);
} List<String> fields=new ArrayList<String>();
List<String> values=new ArrayList<String>();
int index=0;
for(TypeField tf:typefields) {
fields.add(tf.field);
values.add("''{"+index+"}''");
index++;
} index=0;
int times=count/BatchSize;
for(int i=0;i<times;i++) {
long startTime = System.currentTimeMillis();
StringBuilder sb=new StringBuilder();
sb.append("INSERT ALL "); for(int j=0;j<BatchSize;j++) {
index=i*BatchSize+j+startId;
sb.append(getInsertSql(tableName,typefields,index));
} sb.append(" select * from dual");
String sql = sb.toString();
stmt.executeUpdate(sql); long endTime = System.currentTimeMillis();
System.out.println("#"+i+"/"+times+" "+BatchSize+" records inserted to Table:'"+tableName+"',time elapsed:"+(endTime-startTime)+"ms.");
}
} /**
* 得到批量插入语句
* @param tableName
* @param typefields
* @param index
* @return
*/
private String getInsertSql(String tableName,List<TypeField> typefields,int index) {
String currTime=getDatetimeBefore(index); StringBuilder sb=new StringBuilder();
sb.append(" INTO "+tableName+"(");
List<String> fields=new ArrayList<String>();
for(TypeField tf:typefields) {
fields.add(tf.field);
}
sb.append(String.join(",",fields)); sb.append(") values(");
List<String> values=new ArrayList<String>();
for(TypeField tf:typefields) {
if(tf.type.equals("PK")) {
values.add("'"+String.valueOf(index)+"'");
}else if(tf.type.equals("CH")) {
values.add("'0'");
}else if(tf.type.equals("RND")) {
values.add("'"+getRND()+"'");
}else if(tf.type.equals("DT")) {
values.add("to_date('"+currTime+"','yyyy-MM-dd HH24:mi:ss')");
}
}
sb.append(String.join(",",values));
sb.append(")"); String insertSql=sb.toString();
return insertSql;
} private static String getRND() {
return getRandom(0,100);
} private static String getRandom(int min, int max){
Random random = new Random();
int s = random.nextInt(max) % (max - min + 1) + min;
return String.valueOf(s);
} /**
* 将秒转化为日时分秒
* @param secondCount
* @return
*/
private static String sec2DHMS(long secondCount) {
String retval = null; long days = secondCount / (60 * 60 * 24);
long hours = (secondCount % (60 * 60 * 24)) / (60 * 60);
long minutes = (secondCount % (60 * 60)) / 60;
long seconds = secondCount % 60; String strSeconds="";
if(seconds!=0) {
strSeconds=seconds + "s";
} if (days > 0) {
retval = days + "d" + hours + "h" + minutes + "m" + strSeconds;
} else if (hours > 0) {
retval = hours + "h" + minutes + "m" + strSeconds;
} else if (minutes > 0) {
retval = minutes + "m" + strSeconds;
} else {
retval = strSeconds;
} return retval;
} public static void main(String[] args) {
HugeTbBatchInserter mi=new HugeTbBatchInserter();
long startTime = System.currentTimeMillis();
mi.batchInsert();
long endTime = System.currentTimeMillis(); System.out.println("Time elapsed:" + sec2DHMS((endTime - startTime)/1000) );
}
}

这个类运行起来是这样的:

Begin to access jdbc:oracle:thin:@127.0.0.1:1521:orcl as ufo...
Table:score
Exist record count:3351500
Max id:3351499
准备向表score插入96648500条记录.
#0/386594 250 records inserted to Table:'score',time elapsed:284ms.
#1/386594 250 records inserted to Table:'score',time elapsed:282ms.
#2/386594 250 records inserted to Table:'score',time elapsed:324ms.
#3/386594 250 records inserted to Table:'score',time elapsed:284ms.
#4/386594 250 records inserted to Table:'score',time elapsed:302ms.
#5/386594 250 records inserted to Table:'score',time elapsed:330ms.
#6/386594 250 records inserted to Table:'score',time elapsed:291ms.
#7/386594 250 records inserted to Table:'score',time elapsed:335ms.
#8/386594 250 records inserted to Table:'score',time elapsed:372ms.
#9/386594 250 records inserted to Table:'score',time elapsed:374ms.

下面这个类虽然更快些,但插入总量有限,需要改进,也留一个版本在这里吧:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.DecimalFormat; class DBParam {
public final static String Driver = "oracle.jdbc.driver.OracleDriver";
public final static String DbUrl = "jdbc:oracle:thin:@127.0.0.1:1521:orcl";
public final static String User = "ufo";
public final static String Pswd = "1234";
}
// Insert records to srcore table
public class ScoreInserter {
private final String Table="score";
private final int Total=1000000; public boolean fillTable() {
Connection conn = null;
Statement stmt = null; try{
Class.forName(DBParam.Driver).newInstance();
conn = DriverManager.getConnection(DBParam.DbUrl, DBParam.User, DBParam.Pswd);
conn.setAutoCommit(false);
stmt = conn.createStatement(); long startMs = System.currentTimeMillis();
clearTable(stmt,conn);
fillDataInTable(stmt,conn); long endMs = System.currentTimeMillis();
System.out.println("It takes "+ms2DHMS(startMs,endMs)+" to fill "+toEastNumFormat(Total)+" records to table:'"+Table+"'.");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
stmt.close();
conn.close();
} catch (SQLException e) {
System.out.print("Can't close stmt/conn because of " + e.getMessage());
}
} return false;
} private void clearTable(Statement stmt,Connection conn) throws SQLException {
stmt.executeUpdate("truncate table "+Table);
conn.commit();
System.out.println("Cleared table:'"+Table+"'.");
} private void fillDataInTable(Statement stmt,Connection conn) throws SQLException {
StringBuilder sb=new StringBuilder();
sb.append(" Insert into "+Table);
sb.append(" select dbms_random.value(0,200),dbms_random.value(1,10),dbms_random.value(0,101) from dual ");
sb.append(" connect by level<="+Total);
sb.append(" order by dbms_random.random"); String sql=sb.toString();
stmt.executeUpdate(sql);
conn.commit(); } // 将整数在万分位以逗号分隔表示
public static String toEastNumFormat(long number) {
DecimalFormat df = new DecimalFormat("#,####");
return df.format(number);
} // change seconds to DayHourMinuteSecond format
private static String ms2DHMS(long startMs, long endMs) {
String retval = null;
long secondCount = (endMs - startMs) / 1000;
String ms = (endMs - startMs) % 1000 + "ms"; long days = secondCount / (60 * 60 * 24);
long hours = (secondCount % (60 * 60 * 24)) / (60 * 60);
long minutes = (secondCount % (60 * 60)) / 60;
long seconds = secondCount % 60; if (days > 0) {
retval = days + "d" + hours + "h" + minutes + "m" + seconds + "s";
} else if (hours > 0) {
retval = hours + "h" + minutes + "m" + seconds + "s";
} else if (minutes > 0) {
retval = minutes + "m" + seconds + "s";
} else {
retval = seconds + "s";
} return retval + ms;
} public static void main(String[] args) {
ScoreInserter si=new ScoreInserter();
si.fillTable();
}
}

--END-- 2020年1月4日16点57分

将大量数据批量插入Oracle表的类,支持停止续传的更多相关文章

  1. c#几种数据库的大数据批量插入(SqlServer、Oracle、SQLite和MySql)

    这篇文章主要介绍了c#几种数据库的大数据批量插入(SqlServer.Oracle.SQLite和MySql),需要的朋友可以了解一下. 在之前只知道SqlServer支持数据批量插入,殊不知道Ora ...

  2. C#中几种数据库的大数据批量插入

    C#语言中对SqlServer.Oracle.SQLite和MySql中的数据批量插入是支持的,不过Oracle需要使用Orace.DataAccess驱动. IProvider里有一个用于实现批量插 ...

  3. C#:几种数据库的大数据批量插入

    在之前只知道SqlServer支持数据批量插入,殊不知道Oracle.SQLite和MySql也是支持的,不过Oracle需要使用Orace.DataAccess驱动,今天就贴出几种数据库的批量插入解 ...

  4. C#:几种数据库的大数据批量插入(转)

    在之前只知道SqlServer支持数据批量插入,殊不知道Oracle.SQLite和MySql也是支持的,不过Oracle需要使用Orace.DataAccess驱动,今天就贴出几种数据库的批量插入解 ...

  5. C#:几种数据库的大数据批量插入 - faib

    在之前只知道SqlServer支持数据批量插入,殊不知道Oracle.SQLite和MySql也是支持的,不过Oracle需要使用Orace.DataAccess驱动,今天就贴出几种数据库的批量插入解 ...

  6. SQL SERVER 使用BULK Insert将txt文件中的数据批量插入表中(1)

    1/首先建立数据表 CREATE TABLE BasicMsg( RecvTime FLOAT NOT NULL , --接收时间,不存在时间相同的数据 AA INT NOT NULL, --24位地 ...

  7. c#数据批量插入

    由于之前面试中经常被问到有关EF的数据批量插入问题,今天以Sqlserver数据库为例,对.net中处理数据批量处理的方案进行了测试对比. 1.四种测试方案 (1)普通的EF数据批量插入:即调用DbS ...

  8. PHP如何将多维数组中的数据批量插入数据库?

    PHP将多维数组中的数据批量插入到数据库中,顾名思义,需要用循环来插入. 1.循环insert into 语句,逐渐查询 <?php /* www.qSyz.net */ @mysql_conn ...

  9. mybatis批量插入oracle时报错:unique constraint (table name) violated

    mybatis批量插入oracle时报错:unique constraint (table name) violated,是因为插入的集合中有两条相同唯一约束的数据.

随机推荐

  1. 【JavaScript】windows.open用法详解

    windows.open("URL","窗口名称","窗口外观设定");的用法详解 function onNewWindows(redire ...

  2. Webpack 定义process.env的时机

    定义 process.env的时机 如果已经提取了公共配置文件 webpack.common.js 分别定义了开发配置webpack.dev.js和生产配置webpack.prod.js 在webpa ...

  3. 从udaf谈flink的state

    1.前言 本文主要基于实践过程中遇到的一系列问题,来详细说明Flink的状态后端是什么样的执行机制,以理解自定义函数应该怎么写比较合理,避免踩坑. 内容是基于Flink SQL的使用,主要说明自定义聚 ...

  4. Web前端性能优化,应该怎么做?

    摘要:本文将分享一些前端性能优化的常用手段,包括减少请求次数.减小资源大小.各种缓存.预处理和长连接机制,以及代码方面的性能优化等方面. base64:尤其是在移动端,小图标可以base64(webp ...

  5. python关于函数调用作为参数的说明&&装饰器

    python关于函数调用作为参数的说明&&装饰器 简单的: 先看代码: def out(): print('out') def inner(): return 'inner' retu ...

  6. 题解 洛谷P3469

    题目每个割点去掉后会导致多少对点不能连通 考虑跑Tarjan的时候记录每个儿子的size,那么去掉这个割点后其他的点都不能和这个儿子连通 注意每个点去掉后它本身就不能与其他所有点连通 还有就是题目里求 ...

  7. add application window with unknown token XXX Unable to add window;is your activity is running?

    报错: Attempted to add application window with unknown token XXX Unable to add window——token android.o ...

  8. Typescript node starter 2.Router Middleware

    Router 路由器对象是中间件和路由的一个独立实例.可以将它视为一个“迷你应用程序”,仅能够执行中间件和路由功能.每个Express应用程序都有一个内置的应用程序路由器. 路由器的行为类似于中间件本 ...

  9. 团队作业1——团队展示&选题 (追忆少年)

    目录 一,团队展示 1.1队名 1.2队员学号 1.3项目描述 1.4队员风采 1.5团队分工 1.6团队合照 1.7团队特色 (一)目标导向 (二)协作基础 (三)共同的规范和方法 (四)技术或技能 ...

  10. 第六篇 Scrum冲刺博客

    一.会议图片 二.项目进展 成员 已完成情况 今日任务 冯荣新 购物车列表,购物车工具栏 博客撰写 陈泽佳 静态结构 自定义图片组件,提交功能 徐伟浩 协助前端获取数据 协助前端获取数据 谢佳余 未完 ...