将大量数据批量插入Oracle表的类,支持停止续传
之前用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表的类,支持停止续传的更多相关文章
- c#几种数据库的大数据批量插入(SqlServer、Oracle、SQLite和MySql)
这篇文章主要介绍了c#几种数据库的大数据批量插入(SqlServer.Oracle.SQLite和MySql),需要的朋友可以了解一下. 在之前只知道SqlServer支持数据批量插入,殊不知道Ora ...
- C#中几种数据库的大数据批量插入
C#语言中对SqlServer.Oracle.SQLite和MySql中的数据批量插入是支持的,不过Oracle需要使用Orace.DataAccess驱动. IProvider里有一个用于实现批量插 ...
- C#:几种数据库的大数据批量插入
在之前只知道SqlServer支持数据批量插入,殊不知道Oracle.SQLite和MySql也是支持的,不过Oracle需要使用Orace.DataAccess驱动,今天就贴出几种数据库的批量插入解 ...
- C#:几种数据库的大数据批量插入(转)
在之前只知道SqlServer支持数据批量插入,殊不知道Oracle.SQLite和MySql也是支持的,不过Oracle需要使用Orace.DataAccess驱动,今天就贴出几种数据库的批量插入解 ...
- C#:几种数据库的大数据批量插入 - faib
在之前只知道SqlServer支持数据批量插入,殊不知道Oracle.SQLite和MySql也是支持的,不过Oracle需要使用Orace.DataAccess驱动,今天就贴出几种数据库的批量插入解 ...
- SQL SERVER 使用BULK Insert将txt文件中的数据批量插入表中(1)
1/首先建立数据表 CREATE TABLE BasicMsg( RecvTime FLOAT NOT NULL , --接收时间,不存在时间相同的数据 AA INT NOT NULL, --24位地 ...
- c#数据批量插入
由于之前面试中经常被问到有关EF的数据批量插入问题,今天以Sqlserver数据库为例,对.net中处理数据批量处理的方案进行了测试对比. 1.四种测试方案 (1)普通的EF数据批量插入:即调用DbS ...
- PHP如何将多维数组中的数据批量插入数据库?
PHP将多维数组中的数据批量插入到数据库中,顾名思义,需要用循环来插入. 1.循环insert into 语句,逐渐查询 <?php /* www.qSyz.net */ @mysql_conn ...
- mybatis批量插入oracle时报错:unique constraint (table name) violated
mybatis批量插入oracle时报错:unique constraint (table name) violated,是因为插入的集合中有两条相同唯一约束的数据.
随机推荐
- 5.深入k8s:StatefulSet控制器
转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com 在上一篇中,讲解了容器持久化存储,从中我们知道什么是PV和PVC,这一篇我们讲通过Sta ...
- Xlua中LuaBehaviour的实现
简介 在基于lua进行热更新的项目中,我们通常会通过luaBehaviour来让lua文件模拟MonoBehaviour,可以让lua文件拥有一些MonoBehaviour的生命周期,如Enabl ...
- 全面介绍eBPF-概念
全面介绍eBPF-概念 前面介绍了BCC可观测性和BCC网络,但对底层使用的eBPF的介绍相对较少,且官方欠缺对网络方面的介绍.下面对eBPF进行全面介绍. 目录 全面介绍eBPF-概念 BPF概述 ...
- Java—API/Obiect类的equals toString方法/String类/StringBuffer类/正则表达式
API Java 的API(API: Application(应用) Programming(程序) Interface(接口)) 就是JDK中提供给我们使用的类,这些类将底层的代码实现封装了起来 ...
- java中三大集合框架
一.List集合 1.List实现的超级父类接口:Collection 存储一组不唯一(允许重复),有序的对象. 2.了解ArrayList类 A):定义的格式:ArrayList<具体类型&g ...
- Azure Application Gateway(二)对后端 VM 进行负载均衡
一,引言 上一节有讲到使用 Azure Application Gateway 为我们后端类型为 Web App 的 Demo 项目提供负载均衡,Azure Application Gateway 的 ...
- G4560 HD610安装黑苹果Hakintosh
这一篇主要是写给使用G4560笔记本折腾黑苹果的朋友. 我折腾了几天黑苹果,发现怎么也无法成功,最后我才知道HD610现在还不支持黑苹果. 由于大多数笔记本不支持屏蔽核显,这意味着要想使用黑苹果必须要 ...
- 学长小清新题表之UOJ 14.DZY Loves Graph
学长小清新题表之UOJ 14.DZY Loves Graph 题目描述 \(DZY\)开始有 \(n\) 个点,现在他对这 \(n\) 个点进行了 \(m\) 次操作,对于第 \(i\) 个操作(从 ...
- 《Java从入门到失业》第一章:计算机基础知识(二):计算机组成及基本原理
1.2计算机组成及基本原理 1.2.1硬件组成 这里说的计算机主要指微型计算机,俗称电脑.一般我们见到的有台式机.笔记本等,另外智能手机.平板也算.有了一台计算机,我们就能做很多事情了,比如我在写这篇 ...
- 你真的会做 2 Sum 吗?| 含双重好礼
小预告:文末有两份福利,记得看到最后哦- 2 Sum 这题是 Leetcode 的第一题,相信大部分小伙伴都听过的吧. 作为一道标着 Easy 难度的题,它真的这么简单吗? 我在之前的刷题视频里说过, ...